I'm trying to use WillPopScope inside Stateless widget its not working.
Easy to look up in Flutter source
https://github.com/flutter/flutter/blob/d927c9331005f81157fa39dff7b5dab415ad330b/packages/flutter/lib/src/widgets/will_pop_scope.dart#L16-L43
class WillPopScope extends StatefulWidget {
Clearly a stateful Widget.
Related
I have a screen made using Stateless Widget.
I want to run some code when the screen is loaded or when the Stateless widget is created.
In android we could do this in onCreate() method.
i.e. is there some equivalent of onCreate() in flutter.
The terminology for this is "mount". So you want to run the code when the widget is mounted. In Flutter, all widgets have a mounted property and it turns true when the buildContext is assigned to a widget.
But I don't think you can do something to workaround with that property.
bool get mounted => _element != null;
relevant line
I think what you can do is turn your widget into Stateful widget and use initState()
I'm trying to see if I can add some generic navigation behavior passed around my app, and I see that InheritedWidget is a great candidate to avoid passing a specific callback around the widget tree, however I'm noticing more and more that I can only have InheritedWidget of specific class type in order to make the InheritedWidget pattern work, and I was wondering if there's a way to use InheritedWidget as a mixin or if there's a better alternative.
My app looks like this, it passes a callback down the tree
I have 3 navigators right now that I need to present the same method but to act on it themselves right now I need to create 3 InheritedWidget navigators, but the problem is that any widget under the tree would either have to do
NavigatorA.of(context).pushWidget()
But I'd much rather prefer if it was a generic GenericNavigator.of(context).pushWidget() so that I the leaf widgets don't even need to know the right value of the navigator object, I'm afraid to achieve this I'd need to be able to use InheritedWidget as a mixin
This is the desired flow, no callbacks passed
Is this the correct strategy or is there a better way?
How can I use InheritedWidget as a mixin?
No, you cannot make InheritedWidget as a mixin.
However, you can make a generic InheritedWidget:
class Provider<T> extends InheritedWidget {
Provider({Key key, this.value, Widget child}) : super(key: key, child: child);
static T of<T>(BuildContext context) {
Provider<T> provider = context.dependOnInheritedWidgetOfExactType<T>();
return provider?.value;
}
final T value;
#override
bool updateShouldNotify(Provider<T> oldWidget) {
return value != oldWidget.value;
}
}
Adding to #RĂ©miRousselet's answer
Working with flutter 1.19+, where some inheritedWidget APIs are deprecated, the of should look like this now:
static T of<T>(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<Provider<T>>().value;
}
Since the api is based on generics, you no longer need the _typeof tricks.
Consider the following code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
),
body: new Center(
child: new Text('Hello World'),
),
),
);
}
}
My understanding of Buildcontext is that it is the "context" of the widget (please correct me if I'm wrong or elaborate further). What I don't understand is how is the argument "context" passed into the method. Does the runApp function supply the "context"?
TLDR: This is controlled by the framework.
For those who like reading:
Starting from the very beginning. The runApp method takes your app widget and inserts it into the tree, as per comment on the method (binding.dart):
Inflate the given widget and attach it to the screen.
When that's being done, the app StatelessWidget (which obviously is a Widget) is being inflated into an Element (Widget class comments in framework.dart file)
Each time a widget is placed in the tree, it is inflated into an
[Element], which means a widget that is incorporated into the tree
multiple times will be inflated multiple times.
If you then have a look at the abstract class Element, in the same GitHub repo file (framework.dart), you will see the comments above it, saying:
Elements have the following lifecycle:
The framework creates an element by calling [Widget.createElement]
on the widget that will be used as the element's initial
configuration.
The framework calls [mount] to add the newly created
element to the tree at a given slot in a given parent. The [mount]
method is responsible for inflating any child widgets and calling
[attachRenderObject] as necessary to attach any associated render
objects to the render tree.
These two methods createElement and mount are the ones that are responsible for calling the build method.
If you have a look at the the StatelessWidget class, you will see that it has an override for createElement method (framework.dart). Which creates StatelessElement object and passes itself (this) as a constructor parameter. Notice, how the StatelessElement class overrides the build method and calls widget.buildmethod (in this case, the widget is your app widget - i.e. MyApp). This still doesn't tell us how the build method is called though. If you drill down a bit deeper, into the ComponentElement (class that StatelessElement derives from - framework.dart), you can see the mount method being overridden. In this method (which is invoked by the framework), there's a call to _firstBuild method, which then calls rebuild, this method then calls performRebuild method, which finally ends up invoking the build method.
Easy, right?
DISCLAIMER: This is just me connecting the dots. I'm not an expert on Flutter - I literally started using Flutter a week ago. It would be good if some more experienced devs could confirm or not, if my understanding of the mechanisms behind widgets is correct.
EDIT: Answering comment questions
Inflating (in my mind) is creating the object in the memory (so that framework has a reference of it) and rendering it on the screen.
Yes, the BuildContextis going to be the StatelessElement, which contains a reference to the app itself
I'm writing a Flutter app, and I'd like to know what is the best way to make my code more readable.
Right now, I know this two methods:
Create a final variable and store the customized widget on it.
Create a new widget as a template.
So any recommended methods?
As for:
create a final variable and store the customized widget on it
Using a class variable to hold your widget such as
final Widget foo = new Foo();
may be unnecessary or may point to some other issues. I think you are better off creating a function that returns your widget such as
Widget _buildFoo() {
return new Foo();
}
And then you can use this in, for example, your build() method such as (I'm adding MaterialApp Scaffold to provide more context):
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("My App"),
),
body: _buildFoo(),
);
}
Take a look at other example apps on the web such as the Flutter Examples for ideas on structuring your widgets.
As for:
create a new widget as a template
I am going to interpret this as creating another file to hold your widget class e.g. foo.dart. This may not be what you are asking, but creating new dart files for each widget can become a good idea as your app grows. For simple apps, especially most example apps you see, leaving the classes in the same dart file (e.g. main.dart) works just fine. Again, I may not be addressing your question.
Getting back to:
best way to make my code more readable
So, in summary, using private functions that return different sections of your widget tree (i.e functions that just return a widget and any sub widgets that it may include) is generally better than storing widgets as class instance variables. You may want to use a class variable if you need to later reference or interact with the widget; however, this is probably a bad practice and may point to a better way to handle the changing of states and the structure of your widget tree (for example, Flutter's Stateful and Inherited widgets are great for handling state change with needing to make an imperative call on an object reference).
I develop my own custom widget which is used in some other view. In this custom widget, I have one class property which stores information, let's say that this is a list which gains new items inside the widget. Now I want to get items from this list from the level of my main widget.
How to do that?
I don't want to create a variable like this: var customWidget = MyCustomWidget() and then, get the inside variable like customWidget.createState().myList - I think this is a terrible solution (and I'm not sure if it will work). Also passing a list to the constructor of my custom widget looks very ugly.
Is there any other way to get other widget's state?
First, bear in mind that in Flutter data can only be passed downward. It is by design not possible to access data of children, only parents (although there are some hacks around it).
From this point, there are 2 main solutions to pass data:
Add the wanted data to your widgets constructors.
I don't think there is much to say here. Easy to use. But boring when you want to pass one field to all your widget tree. Use this method only when the scope of a value is limited. If it's something like configurations or user details, go for the second solution.
class Bar extends StatelessWidget {
final String data;
Bar({this.data});
#override
Widget build(BuildContext context) {
return Text(data);
}
}
Using Flutter's BuildContext
Each widget has access to a BuildContext. This class allows one widget to fetch information from any of their ancestors using one of the following methods:
inheritFromWidgetOfExactType(Type)
ancestorStateOfType(TypeMatcher)
ancestorWidgetOfExactType(Type)
...
As a matter of facts, if there's a data that needs to be accessed many times, prefer inheritFromWidgetOfExactType.
This uses InheritedWidget; which are specifics kind of widgets that are extremely fast to access to.
See Flutter: How to correctly use an Inherited Widget? for more details on their usage
As a side note, there a third solution. Which is GlobalKey
I won't go into too many details, as this is more a hack than a proper solution. (see Builder versus GlobalKey)
But basically, it allows getting the state/context of any widgets outside of the build call. Be it parents or children.
on my side I implemented onChanged callback.
For example In my Widget :
class ObjectiveCardWidget extends StatefulWidget {
final Objective? objective;
final ValueChanged<Objective?>? onChanged;
ObjectiveCardWidget({this.objective, this.onChanged});
#override
State<ObjectiveCardWidget> createState() => _ObjectiveCardWidgetState();
}
when my data is updated un my custom widget I just called :
widget.onChanged!(newValue); // newvalue has been set previously
In my parentWidget I used onChanged as usual:
ObjectiveCardWidget(
objective: myObjective,
onChanged: (value) {
setState(() {
myObjective = value;
});
})
Hope it will help.