I have a widget that contains a "save" button. After pressing that button several other widgets (not only ancestor ones) must save its state to the file. I don't know how to inform them. What is the best way to achieve that in flutter? I was thinking about using Notification in the "save" widget, closest shared ancestor would contain a NotificationListener that triggers an event to which every widget will subscribe. For me, it doesn't look like a solution.
Provider is the recommended way to do State Management for apps of all sizes. -–Chris Sells – Product Manager, Flutter. June 19, 2019
It's pretty complicated at first, best to check out Simple app state management
The ChangeNotifier uses the notifyListeners function whenever there is any change in the class:
class ChangeNotifierModel extends ChangeNotifier {
String stringThatChanges = 'startValue';
void changeTheString(String value) {
stringThatChanges = value;
notifyListeners();
}
}
The only code that is specific to ChangeNotifier is the call to notifyListeners(). Call this method any time the model changes in a way that might change your app’s UI.
I'm pretty new to Flutter myself, but the way I understand it so, it kinda acts like a wrapper, e.g. wrapping the child of main.dart
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => SingleNotifier())
],
child: MyApp(),
));
don't forget the dependencies
dependencies:
provider: ^4.3.2+2
Related
In short, I have three levels of ChangeNotifiers, the top level, intermediary level and bottom level. Naturally, bottom level is dependent on intermediary level and the latter dependent on the top level.
My questions are:
Is it possible to achieve this using the Flutter Provider plugin?
If Yes, when using the Provider plugin, is it possible to achieve this using only ChangeNotifierProvider only?
More detailed scenario
In my Flutter application, the state is logically structured in several levels. At the top, I have the Configuration state that stores all the configurations of the app. Then, I have a DirectionState and a LocationState, both of which are dependent on the Configuration state and should be notified of changes.
Then, I have my views (UIs), that is basically structured into a Stateful widget class for encoding the logic of the UI only, and a UI model class for implementing the business logic of the UI, somewhat the state of one UI only. The latter class implements the ChangeNotifier so that the UI updates everytime its model changes. That part is pretty simple.
It becomes complex when each model's UI is dependent both on the DirectionState and a LocationState that is then dependent on the Configuration state. All UI's model should be able to change the Configuration state that should then have all effect on the entire tree. Similarly, a change in both the Direction state and Location state should affect all UI's model and ultimately the UI themselves.
With the Provider package a Provider widget will provide to all of it's children an object (we can call it a state).
With context.read<MyType>() you can retrieve the value of one of your providers.
Also calling context.watch<MyType>() inside a build method will rebuild your widget when the provider changes.
In your case we can image this structure:
Provider(
create: (_) => Configuration(),
child: Provider(
create: (_) => DirectionState(),
child: Provider(
create: (_) => LocationState(),
child: ...
)
)
)
Also you should check the Bloc library witch make the state management easier than the Provider library. (Bloc is based on the Provider package)
The official Mobx documentation for Flutter says that in order to transfer data correctly, you must use a Provider and refer to the context to retrieve the data.
But why can't I just call the Mobx class at the root of the application and access the global variable to get the data?
CbtStore cbt = CbtStore();
void main() async {
runApp(const MyApp());
}
Why should I be doing this?
void main() async {
runApp(MultiProvider(
providers: [
Provider<CbtStore>(create: (_) => CbtStore()),
],
child: MyApp()));
}
And how do I refer to Mobx inside the widget methods in that case, for example, if I want to call the action in the Mobx class in initState method? Now I do it in the following way. But when using Provider in initState there is no context.
#override
void initState() {
cbt.init();
super.initState();
}
Provider is used only for dependency injection with mobx. It is not used for state changes.
Now when you are using mobx you don't need a stateful widget in most cases because you are handling your state changes inside your mobx store and if there is any changes in the state we use Observer to change ui.
if want something to initialise with the screen than prefer using constructor of mobx class rather then initState.
for example,
class MyStore = _MyStore with _$MyStore;
abstract class _MyStore with Store {
_MyStore(){
getData();
}
}
Now don't use global providers for your all of store. Only Initialise a provider whenever you need it. So when you push a route wrap it with a provider so that Provider.of(context); can find it. Only use global store if it required globally.
You mentioned creating an instance of store to use it. When you initialise a store in stateless widget it, the data will get destroyed when you close the screen and when you reopen it everything will start all over again. It is useful when you don't need to maintain state after screen pops. It will based on your use case.
You should do what works best for your use case.
The reason why providers are useful is that they can be provided where needed. This could be in the application root, but also somewhere deeper in the widget tree.
Another advantage of the providers is that you can have a provider that notifies listeners. Widgets will rebuild automatically in this case, which can be useful if you have stored and need data to update everywhere in the application.
The initState does indeed not allow the use of providers directly. There are 3 solutions for this:
Don't have the provider listing (Provider.of(context, listen: false); This allows you to use the methods, but not listen to changes.
Use the provider in the build method, using the consumer.
I am by no means an expert on flutter, but this is just what I have experienced so far.
I am developing a mobile application using Flutter. I am new to Flutter. I am using BLoC for state management. I know that it is specially designed for management async execution. But also the purpose of BLoC is for state management. But I am a little bit confused and not quite sure how to handle the primary data types in BLoC.
Let's imaging the I have a button and an image. The functionality would be that when the button is clicked, the visibility of the image will be toggled. Literally, we just need to use a boolean variable to manage the state of the image. This is how I would implement it.
I have a bloc class called HomeBloc with the following implementation.
class HomeBloc {
bool _isImageDisplayed = true;
bool get isImageDisplayed => _isImageDisplayed;
void set isImageDisplayed(bool displayed) {
this._isImageDisplayed = displayed;
}
//the rest of the code for other functionalities goes here
}
final homeBloc = HomeBloc();
Then in the HomePage widget, I update the state of the image like this inside the setState method when the button is clicked.
this.setState(() {
homeBloc.isImageDisplayed = false;
});
My question is that "is it the standard way to manage primary data type in the BLoC in Flutter"? Is this the best practice? Do we need to use StreamBuilder? Do we even need to manage it inside the BLoC?
It's not the best practice I guess, as using setState becomes really hard on big applications and re-rendering widgets that don't change for no reason. Imagine making an e-commerce app and you just go to the product page, you add the product you like into the cart, but you have designed in your home page a cart icon with a red dot with a number inside it to specify how much products you got in your cart, so you handle the state of that icon in the main.dart file by passing a function that setState the home page route or maybe the whole application, it's hard, isn't it?.
Thankfully, BLoC and Provider patterns are basically using setState but in a better way so you don't have to re-render the whole page just for a small change in a text or something else, but you just re-render a specific widget in your widget tree.
I also recommend using BLoC Provider which is built on Provider and RxDart (Streams) as it makes great isolation between UI code and business code.
Check Provider and BLoC Provider.
class App extends StatefulWidget {
....
return HomeProvider(
homeBloc: HomeBloc(),
child: MaterialApp(
home: HomeScreen(),
),
);
class HomeScreen extends StatefulWidget {
HomeBloc homeBloc = HomeBloc();
}
From the above two scenarios, Most of the tutorials I read, is using the first option. Is the second method completely wrong? or does it have any negative effects in-app?
I can see one difference.
I can access the homeBloc by HomeProvider.of context in the first method. For the second method, I have to pass homeBloc in all the widgets.
Sure, you can use bloc without a provider. But if you share 2 screens with the same bloc, the stream value inside the bloc will be different, because you don't use the InheritedWidget (usually in a provider). The function of a provider is to provide your bloc with InhteritedWidget, so that multiple screens could access to the same stream.
It is definitely possible. I using a single bloc for my entire application right now (as I have come from react-native redux, apollo background, single source of truth makes more sense for me). An example is like below. You can declare your single instance of bloc and import it wherever you use it so you refer to same instance.
class Bloc {
/// Your Bloc Stuff
}
final bloc = Bloc();
I use the inherited widget at the top of my app to update my application whenever a new location is setted. This results in a refresh of my location coordinates at every possible place in my app.
However I also have a textfield. When i tap on the textfield some seconds later
the keyboard will be hidden by the update of the inherited widget.
Is there a way to prevent flutter to hide the keyboard or to reinitialize the state so that the update of the inherited widgets works together with my search field?
It should be also possible that when i enter some text that this trigger the inherited widget for a new update but the search bar and keyboard should stay open.
new TextField(
controller: _controller,
autocorrect: false,
autofocus: true
...
)
Without seeing all of your code, it's a little bit difficult to know what's going on.
My best guess though is that you're using a context somewhere above the TextField to register for updates from the inherited widget.
What I'd advise you to do though is go up the widget tree from your TextField and make sure that you're not using the inherited widget in a context anywhere above it. It might be worth putting some debug statements at the beginning of your build methods to determine exactly where the build is being triggered from (the inherited widget shouldn't trigger a rebuild immediately below it if you're using it right, but stranger things have happened).
This includes if you have something like this:
MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
Location location = MyInheritedWidget.of(context).location;
return new Row(
children: [
new LocationDisplayer(location),
new TextField( ..... ),
],
);
}
}
The reason I say this is that from my understanding of how inherited widgets work; when it changes, whichever widgets whose contexts were used to get the inherited widget get rebuilt.
That means that even though your textfield technically isn't changing any properties, it might actually be a new textfield depending on how the build went (there's a bit of flutter magic I don't fully understand around how they decide when to make new vs re-build things) and therefore it hasn't requested focus.
Rather than having to write a bunch of new widgets to enclose whatever is using MyInheritedWidget, I believe you could use something like this:
MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
return new Row(
children: [
new Builder(
builder: (context) {
Location location = MyInheritedWidget.of(context).location;
return new LocationDisplayer(location);
},
),
new TextField( ..... ),
],
);
}
}
If you're 100% sure you're not doing this anywhere, then you could go for the non-flutter way of doing things... your location class could expose a subscribe and unsubscribe method. All of the widgets where you're actually using the location call subscribe (which should also be stateful widgets) with a callback in their initState() and unsubscribe in their dispose(). The location class would simply call each of the callbacks instead of changing state as you're doing now, and within each of the callbacks you should call setState for the widget that actually shows the location.