How to allow Flutter "nested" state management? - flutter

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)

Related

stream builder vs Bloc with event

the question is why we use Bloc with streams and bloc builder not streams and stream builder only ?
and i want to know can i use stream builder with stateless widget ? and stream builder` rebuild all build method or the widget that wrap it only?
and can i use bloc builder in stateless widget when i use controller ? if yes when should i close the controller ?
BlocBuilder<BlocA, BlocAState>(
builder: (context, state) {
// return widget here based on BlocA's state
}
)```
First, I want you to read this from the bloc documentation.
BlocBuilder is very similar to StreamBuilder but has a more simple API to reduce the amount of boilerplate code needed.
The StreamBuilder is based on Stream, and the BlocBuilder` is based on cubits and blocs which are similar to screens.
But the main goal of using the Bloc library in the first place is to achieve the BloC (Business Logic Component ) pattern which separates your project into layers, the presentation, business logic, and data layers.
This bloc library makes your project follows the BlocC pattern and no layer should know about the other, this will make your project more clean and maintainable to work with, edit, upgrade, and analyze...
Using StreamBuilder, all it can do is listen to a Stream, and build a widget-sub tree based on it as an example, your project logic will be inside your view, and in another place, there is the data layer, which leads to an absolute mess, now just imagine if you have an intermediate or big flutter project, and you wanted to change just the name of a method in some file, I'm pretty sure you will get lost before you will find it because your project does not follow any architecture.
using the BlocBuilder will give you same rebuilding functionalities as StreamBuilder, but your project will be well architectures and everything is organized, the presentation/view layer will now nothing about your logic and so on for the data layer.

What's a state in flutter?

I just started learning Flutter and as a beginner I'm facing some issues about understanding a state. I watched some videos on YouTube, read articles online. They explain the reason why one should use a Stateful Widget instead of Stateless. But I can't really understand what part of an app needs to be a Stateful Widget and what the use of the setState method is. Also what's the difference between the state of a widget and state of the app?
Flutter is declarative. This means that Flutter builds its user interface to reflect the current state of your app:
State can be described as "whatever data you need in order to rebuild your UI at any moment in time".
When the state of your app changes (for example, the user flips a switch in the settings screen), you change the state, and that triggers a redraw of the user interface.
There is no imperative changing of the UI itself (like widget.setText)—you change the state, and the UI rebuilds from scratch.
The state that you do manage yourself can be separated into two conceptual types: ephemeral(widget state on your question) state and app state.
Ephemeral state
Ephemeral state (sometimes called UI state or local state) is the state you can neatly contain in a single widget.
There is no need to use state management techniques (ScopedModel, Redux, Provider, bloc, etc.) on this kind of state. All you need is a StatefulWidget.
You only need to use setState to alter your current state.
For example, below, you see how the currently selected item in a bottom navigation bar is held in the _index field of the _MyHomepageState class.
In this example, _index is ephemeral state.
class MyHomepage extends StatefulWidget {
const MyHomepage({Key? key}) : super(key: key);
#override
_MyHomepageState createState() => _MyHomepageState();
}
class _MyHomepageState extends State<MyHomepage> {
int _index = 0;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: _index,
onTap: (newIndex) {
setState(() {
_index = newIndex;
});
},
// ... items ...
);
}
}
Here, using setState() and a field inside the StatefulWidget’s State class is completely natural. No other part of your app needs to access _index.
The variable only changes inside the MyHomepage widget. And, if the user closes and restarts the app, you don’t mind that _index resets to zero.
App state
State that is not ephemeral, that you want to share across many parts of your app, and that you want to keep between user sessions, is what we call application state (sometimes also called shared state).
Examples of application state:
User preferences
Login info
Notifications in a social networking app
The shopping cart in an e-commerce app
Read/unread state of articles in a news app
For managing app state, you’ll want to research your options. Your choice depends on the complexity and nature of your app, your team’s previous experience, and many other aspects.
Here's a link to an example on app wide state management using provider(one of many state management libraries), Example.
Here's a list of libraries that are used for app wide state management, Options.
In summary, there are two conceptual types of state in any Flutter app. Ephemeral state can be implemented using State and setState(), and is often local to a single widget. The rest is your app state.
Both types have their place in any Flutter app, and the split between the two depends on your own preference and the complexity of the app.
In the Stateless widget, the build function is called only once which makes the UI of the screen. It means through that widget you cannot change the UI again. It will render(build) the UI at once. So for those widgets, you need to send the information before building the widget.
Stateful Widgets: The widgets whose state can be altered once they are built are called stateful Widgets. This simply means the state of an app can change multiple times with different sets of variables, inputs, data. Below is the basic structure of a stateful widget. That means you can change the UI of this widget without building it again with help of the setState method.
For more information refer - https://medium.com/flutter-community/flutter-stateful-vs-stateless-db325309deae
if we want to make changes in our UI so that we use statefulWidget
otherwise we use statelessWidget.
in most case we will be using statefulWidget.
that's it.

How to scope providers on several widgets without putting the provider on a root widget in flutter

I'm working on a simple demo where the user logs in, sees a list of items, clicks on an item, and then sees its details. These are 3 screens but let's say two flows where one deals with login and the other with items.
I'm managing my state using provider package. I placed a LoginRepository provider on my root widget because that state is needed everywhere. This works great.
Now I need an ItemRepository which should be available on the ItemListScreen and the ItemDetailScreen. To implement that scoping, I decided to create a common ancestor widget called ItemScreens. This is where I placed my provider and my nested Navigator.
I navigate my screens using Navigator.of(context) to make sure the closes ancestor navigator is used. I fetch my provider using Provider.of<ItemRepository>(context) to make sure the closes ancestor state is used.
This apparently does not work because I get a Could not find the correct provider. I understand that the provider from the sibling route cannot be accessed by another sibling but here I'm nesting navigators I would expect the ItemRepository to be placed at ItemScreens which then handles subrouting so that these two routes can fetch providers from the parent route?
Sidenote:
Flutter documentation clearly states that providers should be lifted to achieve proper scoping. But I've never seen an example where the provider was lifted to a non-root widget. If all we can do is place providers into the root widget, that should be clearly stated.
EDIT: Re-creating providers on several widgets is kind of an alternative but that's not really scoping it's just pseudo passing by value.
EDIT: Putting all providers on the root widget with MultiProvider isn't a good idea because it creates a global state anti-pattern. Some states should also simply not be created unless needed i.e. the user is visiting that flow/screen.
You can wrap your MaterialApp Widget in main.dart with Multiprovider and give the list of providers you are using, That way App knows which class to treat as providers.
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => FirstProvider(),
),
ChangeNotifierProvider(
create: (_) => SecondProvider(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Auro',
home: _decideMainPage(context),
// routes: routes,
theme: AllCoustomTheme.getThemeData(),
),
)

Notification to adjacent widget

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

Flutter BLoC: managing the state of primary data types

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.