Separate widgets in other files flutter - flutter

I want to make my code neater but I have a problem when I separate widgets that I use often in 1 file
here is it my main widget
import 'package:a_tiket/Helpers/widget_helper.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool _isLoading = false;
var _message = '';
var _hasError = false;
#override
Widget build(BuildContext context) {
return
_isLoading ?
_loadingWidget(context)
:
Scaffold(
body: SingleChildScrollView(
child: Container(
),
],
),
)
)
)
;
}
}
this is my widget_helper.dart
Widget _loadingWidget (BuildContext context){
return Scaffold(
body: Center(
child: CircularProgressIndicator(
backgroundColor: ACCENT_COLOR,
valueColor: new AlwaysStoppedAnimation<Color>(PRIMARY_COLOR),
),
),
);
}
the problem is i got some error. i have add import for widget_helper but still get error
lib/Pages/loginPage.dart:193:7: Error: The method '_loadingWidget' isn't defined for the class '_LoginPageState'.
what should i do? i just want to make the code neater

please remove underline
change from
_loadingWidget(context)
to
loadingWidget(context)

There are a few issues with your code:
For such a small piece of code like showing a
CircularProgressIndicator you should not be putting a method in a separate
file. Instead of making your code "neater", you are making it harder
to read. If you really want to have it in a separate file, create a Stateless widget that shows the code you want. But then again you are just using a CircularProgressIndicator. You aren't saving any code, just creating more unnecessary code.
You already have a Scaffold where your are going to show the CircularProgressIndicator. You don't need to have another one. It's not doing anything.
While Dart uses camelCase for variable naming, file names use snake_case. Try to use it when naming files.

Related

Global key is not restoring state sometimes of child widget

Basically, in this example, I am using global key to restore value of by child,
This work,
But sometimes, it appears that the global key does not restore the value of the child items.
To make it clear, I have added a checkbox widget and a slider widget to the first and last pages of this code (green and red).
When I change the slider value on the first page and try to see it on the third page, it works fine.
Same thing if I change checkbox value (like true) on first page and then click on third page it shows mostly true, but sometimes does not.
I am unable to understand the reason behind this issue. Please refer to the following gif to better understand my question.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
final _key = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Global Key Example")),
body: PageView.builder(
itemCount: 3,
itemBuilder: (context, index) {
switch (index) {
case 0:
return Container(
color: Colors.green,
child: ActionPage(_key),
);
break;
case 1:
return Container(
color: Colors.blue,
child: Text("Blank Page"),
);
break;
case 2:
return Container(
color: Colors.red,
child: ActionPage(_key),
);
break;
default:
throw "Not found";
}
},
),
);
}
}
class ActionPage extends StatefulWidget {
#override
_ActionPageState createState() => _ActionPageState();
ActionPage(key) : super(key: key);
}
class _ActionPageState extends State<ActionPage> {
bool _switchValue = false;
double _sliderValue = 0.5;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
value: _switchValue,
onChanged: (v) {
setState(() => _switchValue = v);
},
),
Slider(
value: _sliderValue,
onChanged: (v) {
setState(() => _sliderValue = v);
},
)
],
);
}
}
I'd recommened you to not use a GlobalKey here. Make sure to pass the needed data not the widget sate. If you take a look at the documentation the following pitfalls are listed:
GlobalKeys should not be re-created on every build. They should
usually be long-lived objects owned by a State object, for example.
Creating a new GlobalKey on every build will throw away the state of
the subtree associated with the old key and create a new fresh subtree
for the new key. Besides harming performance, this can also cause
unexpected behavior in widgets in the subtree. For example, a
GestureDetector in the subtree will be unable to track ongoing
gestures since it will be recreated on each build.
Instead, a good practice is to let a State object own the GlobalKey,
and instantiate it outside the build method, such as in
State.initState.
So you have to make sure that two widgets with the same key are never rendered on the screen at the same time. With a PageView this obviously can happen since you can display (eventhough there might be a transition state) two of your widgets with the same key at the same time. This can mess things up. Also they changed quite a bit in the past implementation wise if I recall correctly.
You should either pass the value directly to your widget or use something like an InheritedWidget to be safe.
Generally speaking I'd always try to avoid using GlobalKeys because they are quite complex and can have side effects which might not be always fully comprehensible. Also there are good alternatives.

Read nested widget/class properties value in flutter

I'm building a simple app with lots of nested widgets/classes from different specialised files
list of files:
main.dart -> the menu file used to start the activity
"Activity()"
group_widgets.dart -> the file that contains the custom widget
"CustomWidget()"
file_a.dart -> the file that uses the custom widgets
inside the "Activity()"
other.dart -> other files that needs to manage data changed in CustomWidget()
inside main.dart:
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Activity(),
));
},
inside group_widgets.dart:
class CustomWidget extends StatefulWidget {
const CustomWidget({Key? key}) : super(key: key);
#override
State<CustomWidget> createState() => _CustomWidgetState();
}
class _CustomWidgetState extends State<CustomWidget> {
var _boolean = false;
bool switchBoolean(bool state) => !state;
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => {
setState(() {
_boolean = switchBoolean(_boolean);
})
},
child: Container(
color: _boolean == true ? Colors.green : Colors.red,
),
);
}
}
inside file_a.dart
class Activity extends StatefulWidget {
const Activity({Key? key}) : super(key: key);
#override
State<Activity> createState() => _ActivityState();
}
class _ActivityState extends State<Activity> {
#override
Widget build(BuildContext context) {
bool boolean = true;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CustomWidget(),
Text('Here where to show the variable from CustomWidget'
'and prove I can retrieve it')
],
),
),
);
}
}
inside other.dart
if ( booleanFromCustomWidget == true) {
Something ...
}
What is the best practice to achieve it?
I've read a lot here but nothing seems to well fit my needing.
Just comment if my request is not as clear as it seems to me))
Please correct me if I am wrong, but if you want to access data from parent widgets from inside their descendants (children or even nested children) you can either pass them down via parameter arguments:
Child(int age, String name);
And then accept it in the new file, where the Child widget lives, via its constructor:
class Child {
String name;
int age;
// Constructor
Child(String passedName, int passedAge) {
this.name = passedName;
this.age = passedAge;
}
}
Inside the parent.dart you then have to import the children.dart to use it.
Or use a popular package like the provider package: https://pub.dev/packages/provider
This allows you to store data containers, which you can access basically anywhere in your code. Feel free to google it & watch some tutorials to get started, as it is the preferred approach to avoid passing data to widget which really do not care about the passed parameters.
Note: You can transfer the idea to output the String data like in your example code above.
you can use a state manager like provider, or bloc
At the top level, you set up the data services

Best way to pass widgets to child widget in flutter

I'm new to flutter but I have a widget that wraps a custom painter. I am trying to get it to work so I can supply a Widget to this child widget's constructor and then use that widget as the child of the custom painter.
For example:
class MyPainterWrapper extends StatefulWidget {
Widget _childWidget;
SceneRender([this._childWidget]);
#override
State<StatefulWidget> createState() {
return new MyPainterWrapperState(_childWidget);
}
}
class MyPainterWrapperState extends State<SceneRender> {
Widget _childWidget;
MyPainterWrapperState(this._childWidget);
#override
Widget build(BuildContext context) {
return Column(
children: [
CustomPaint(painter: MyPainter(), child: _childWidget)
],
);
}
}
And in another widget (called testWidget):
bool _answerCorrect = false;
bool _answerInputted = false;
var _msgController = TextEditingController();
FocusNode _answerFieldFocus = new FocusNode();
DictionaryEntry _currentEntry;
void _checkIfCorrect(String answerGiven) {
setState(() {
_answerCorrect = false;
if (_currentEntry.Full_Word == answerGiven)
_answerCorrect = true;
else if (_currentEntry.dictReadings.isNotEmpty) {
for (AlternateDictionaryEntryReading entryReading in _currentEntry
.dictReadings) {
if (entryReading.Alternate_Reading == answerGiven) {
_answerCorrect = true;
break;
}
}
}
_answerInputted = true;
_msgController.clear();
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: MyPainterWrapper(Center(Container(Column(children: <Widget>[
if (_answerCorrect && _answerInputted) Text('CORRECT!'),
if (!_answerCorrect && _answerInputted) Text('WRONG:'),
if (_answerInputted)
Text(_currentEntry.Full_Word),
if (_answerInputted)
for(AlternateDictionaryEntryReading reading in _currentEntry.dictReadings)
Text(reading.Alternate_Reading),
Container(
constraints: BoxConstraints.expand(
height: 100,
width: 1000
),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (DictionaryTranslation translation in _currentEntry.dictTranslations)
Text(translation.Translation),
],
)
),
),
Text('Enter Answer:',),
TextField(
controller: _msgController,
focusNode: _answerFieldFocus,
onSubmitted: (String value) {
_checkIfCorrect(value);
_answerFieldFocus.requestFocus();
},
)
This works to render the first time correctly, but any setState calls from checkIfCorrect from testWidget do not force the child widget to rebuild. I've tried testing it this way and it works, so that leads me to believe that I'm passing the widget incorrectly to have it redrawn via setState
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: CustomPaint(painter: TestPainter(), child: Center(
child: Container(...))
Your MyPainterWrapperState class reads like you are creating a new _childWidget property inside your state (which has a default value of null). You are then using it to initialize a new instance of MyPainterWrapperState, then throwing away the instance of MyPainterWrapper() that you just created.
You're not actually using the stateful part of your stateful widget at all; You're just calling a method that returns something once.
That said, your approach is basically right, but the implementation is off a little.
My advice:
First, you can use named properties to supply constructor arguments to your class. I've made that change in the code snippet shown below.
The State class of a stateful widget supplies a widget property that should be used to reference the properties of the widget that created it. The State widget should also have a solitary initializer that accepts no arguments.
Also good to know is that the State class provides an initState() method that you can override to initialize any class local variables that you declare. This should be used to give a value to your _childWidget property.
Finally, anything you expect to be rebuilt should be inside the MyPainterWrapperState() class. Your SceneRender() method doesn't appear in your code, but you might want to move it into MyPainterWrapperState() if you expect the scene to change based on the value of the child.
I suggest these changes.
Pass arguments to MyPainterWrapper via named arguments.
Remove the argument to MyPainterWrapperState() and reference the child through the widget property supplied to the state.
Initialize _childWidget by overriding initState()
If SceneRender() does anything that depends on the value of _childWidget, move it to the build() method of MyPainterWrapperState().
The Flutter docs are great, and the Flutter team has created a ton of helpful YouTube videos that explain in a couple of minutes examples of how to use dozens of them. For a better understanding of StatefulWidget, you can read about it here.
If you make these changes, your solution would look something like the code snippet below.
Presuming you make those changes, you would alter your call to MyPainterWrapper() to use named properties.
Change this line
body: MyPainterWrapper(Center(Container(Column(children: <Widget>[
To this
body: MyPainterWrapper(child: Center(Container(Column(children: <Widget>[
This won't get you to done, but it will get you closer. I also haven't run this through a compiler, so there are probably errors in the snippet, but this should serve to illustrate the approach.
class MyPainterWrapper extends StatefulWidget {
MyPainterWrapper(
{
#required child: this.childWidget,
}
);
final Widget childWidget;
// Not sure what this does, but I'm pretty sure that it doesn't
// provide anything into the widget tree.
// If it mutates its arguments, then you might still need it.
// SceneRender([this._childWidget]);
#override
State<StatefulWidget> createState() {
// Note no arguments.
return new MyPainterWrapperState();
}
}
class MyPainterWrapperState extends State<MyPainterWrapper> {
// This is an uninitialized variable inside this class.
Widget _childWidget;
// MyPainterWrapperState(this._childWidget);
// Initialize state variables here.
#override initState() {
// Assigns the widget class initializer to your state variable.
_childWidget = widget.childWidget;
}
#override
Widget build(BuildContext context) {
return Column(
children: [
CustomPaint(painter: MyPainter(), child: _childWidget)
],
);
}
}```

Dynamically create instance from type name

There's the question – could I create class instance if I have string variable which contains its name?
For example, I have
var className = 'DocumentsList';
could I do something like this
var docListWidget = createInstance(className[, params]);
Flutter (and Dart) do not have the facilities to do that. On purpose. They implement tree shaking, a mechanism to remove unused code to make your app smaller and faster. However, to do that, the compiler has to know what code is used and what is not. And it cannot possibly know what code gets used if you can do stuff like you describe.
So no, this is not possible. Not with that degree of freedom. You can have a big switch statement to create your classes based on strings, if you know in advance which string it will be. That is static, the compiler can work with that.
What you want is called "reflection" and you can add some capabilities using packges like reflectable or mirror but they cannot change the compilation process, they too work through the fact that you specify beforehand which classes need reflection. A totally dynamic usage is just not possible (on purpose).
Since you mentioned the routing table: You cannot create a class from a string, you can however create the string form the class:
import 'package:flutter/material.dart';
typedef Builder<T> = T Function(BuildContext context);
String routeName<T extends Widget>() {
return T.toString().toLowerCase();
}
MapEntry<String, Builder<Widget>> createRouteWithName<T extends Widget>(Builder<T> builder) {
return new MapEntry(routeName<T>(), (context) => builder(context));
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: routeName<ScreenPicker>(),
routes: Map.fromEntries([
createRouteWithName((context) => ScreenPicker()),
createRouteWithName((context) => ScreenOne()),
createRouteWithName((context) => ScreenTwo()),
]),
);
}
}
class ScreenPicker extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Navigate a route")),
body: Column(children: [
RaisedButton(
child: Text('One'),
onPressed: () => Navigator.pushNamed(context, routeName<ScreenOne>())),
RaisedButton(
child: Text('Two'),
onPressed: () => Navigator.pushNamed(context, routeName<ScreenTwo>())),
]));
}
}
class ScreenTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Second Screen")), body: Center(child: Text("Two")));
}
}
class ScreenOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("First Screen")), body: Center(child: Text("One")));
}
}
This way you have no strings with route names in your project that can be changed or mistyped or forgotten when renaming something.
The best way to do that is using a named constructor like:
class MyClass {
const MyClass.create();
}
So the way you do this is passing the Type calling the method within. Remember you can pass the function without calling it, like:
Get.lazyPut(MyClass.create);
If you really need to use String for identifying the class type I suggest you create some mapping for it.
There's some functions from rootBundle that can be useful in this situation, like rootBundle.loadString() that can be done to load code from files.
Hope this was helpful.

Flutter: localization not working due to context being null. how to correctly pass it through stateless to stateful?

I have this stateless widget called myPage.dart. which contains a Stack of Texts and Stateful List View Builder.
here is the code (I commented out the 2nd group of Text and Stateful List View Builder for now:
Widget content(BuildContext context) =>
Container(
child: Stack(
children: <Widget>[
sameDayText(context),
SameDayWorkManagement(context),
// nextDayText(),
// nextDay.NextDayWorkManagement(),
],
),
);
The sameDayText is no problem. probably because the class for that is inside the myPage.dart but I can't seem to pass the context to sameDayWorkManagement.dart which is a stateful widget that contains a listview builder. keep in mind that everything worked in the past. its just that when I tried to add localization now, It seems that the context is null for some reason in the sameDayWorkManagement. Localization requires context. and I keep getting error on snippet of codes in the sameDayWorkManagement that localizes text. and again because of the context being null:
here is the sample code of the context being null in the sameDayWorkManagement.dart
Localization.of(widget.buildContext).getTranslatedValue('wakeup')
and here is the script for the sameDayWorkManagement.dart
class SameDayWorkManagement extends StatefulWidget {
BuildContext buildContext;
SameDayWorkManagement(buildContext);
#override
_SameDayWorkManagementState createState() => _SameDayWorkManagementState();
}
class _SameDayWorkManagementState extends State<SameDayWorkManagement>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Container(
// backgroundColor: Color(app_background_color_blue),
child: LayoutBuilder(
builder: (context, constraints) => SafeArea(
child: Container(
child: new StoreConnector<AppState, MainPageViewModel>(
converter: (store) => MainPageViewModel.fromStore(store),
builder: ( _, viewModel) => content(viewModel, constraints),
),
),
),
),
);
}
#override
void initState () {
super.initState();
if(widget.buildContext != null) {
print("is true");
} else {
print("is not true");
}
}
In initState the result is is not true
to be more precise. here is the image of myPage that does not have Localization and instead uses static Japanese Text
The first dot and Japanese Text with a telephone icon in the right is the sameDayText widget. the card below it is the sameDayWorkManagement its a list view and its scrollable.
and then the rest bellow are those that I commented out ( for now) called next day
I created a really ugly work around, so I'm still hoping this would be fixed. my work around is I created a map of all the necessary translated text in myPage using the localization which again is working in there. and pass that map to the sameDayWorkManagement as a parameter. and use that map to populate my needed text. yes it is very ugly. but for now it is working.