Flutter, how to use exist Bloc for other screens? - flutter

Assume that class A is arbitrarily rooted, and its children are classes B, C, and D.
(Not all classes are in the same hierarchy)
When I use BlocConsumer or BlocBuilder in class D, I get an error.
I just want to reuse the Bloc state used in class A and class B.
is there any solutions? thanks
--UPDATE
I am talking about this https://github.com/felangel/bloc/issues/74
Is there any way other than passing it as navigator argument to class one by one?

Note: I assume you are using the flutter_bloc library, but the concept is the same even though you are not.
The BLoC you want to access must be provided ABOVE all the screens/pages you want to access this BLoC in. The simplest solution to your problem would be wrapping your root Widget (probably MaterialApp) with BlocProvider:
runApp(
BlocProvider<YourBloc>(
create: (_) => YourBloc(), // create your BLoC here
child: MaterialApp(...),
),
);
For more info, I would recommend watching this video: https://www.youtube.com/watch?v=laqnY0NjU3M

introduce blocprovider on main myapp page and use it on other pages

Related

Different instances of the same cubit (local scope)in the same tree

Is it bad practice to create multiple instances of the same bloc/cubits in the tree? I'm creating an instance of the cubit and using it in a few locations in the tree-like so.
BlocBuilder<BlocA, BlocAState>(
cubit: blocA, // provide the local cubit instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
... but the blocs are scoped to a single widget with this method. I'm using this technique 3 times in the widget tree (splitting up my widgets into different classes). My console is spitting out several instances of the bloc in question from the BlocObserver. The blocs "work" but I'm suspicious I'm going about things the incorrect way...
Seems like you misunderstood the core concept of providing a bloc instance to your widget tree. You need BlocBuilder to set up the BuildContext for your widgets, that depend on bloc, and to minimize the scope of widgets, that will be marked for rebuild on bloc changes.
Yes this is very bad practice to have multiple bloc instances as it will almost nullify its usefulness to your app. To provide a single instance to your widgets use BlocProvider or BlocProvider.value, more about it you can check here or here

Meaning of the term .of() in flutter

So I was wondering about the Command I use when I close an AlertDialog:
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(context).pop();
},
),
What does .of() exactly do? I could not find anything in the flutter dev documentation (Probably because I was missing the correct search term)
Can anyone explain what happens there?
In the Flutter SDK the .of methods are a kind of service locator function that take the framework BuildContext as an argument and return an internal API related to the named class but created by widgets higher up the widget tree. These APIs can then be used by child widgets to access state set on a parent widget and in some cases (such as Navigator) to manipulate them. The pattern encourages componentization and decouples the production of information related to the build tree with its consumption.
In addition to Navigator.of (returns a NavigatorState) there are:
Theme.of (returns a ThemeData containing the ambient theme settings)
MediaQuery.of (returns a MediaQueryData containing information computed about the device screen size)
Directionality.of (returns a TextDirection containing information about text display)
Of course Flutter has non-specific methods for looking up parent widgets from the build context:
context.findAncestorWidgetOfExactType<T extends Widget>()
context.findAncestorStateOfType<T extends State>()
context.findRootAncestorStateOfType<T extends State>()
so Theme.of(context) is really just a static shorthand for context.findAncestorWidgetOfExactType<Theme>().data and Navigator.of(context).pop() is really just a shorthand for context.findAncestorStateOfType<NavigatorState>().pop()
From the documentation of the Navigator class,
Although you can create a navigator directly, it's most common to use the navigator created by the Router which itself is created and configured by a WidgetsApp or a MaterialApp widget. You can refer to that navigator with Navigator.of.
As a general rule, any time you see something along the lines of Classname.of(someObject) in an OO language, .of is a builder that returns an instance of Classname from someObject.

Context is lost after Navigator.push

Provider.of<IsSpecialist>(context).value
is accessible above this Navigator.push:
Navigator.push(context, MaterialPageRoute(builder: (c) => ChatScreen()));
But in after the Navigator.push it's unavailable in ChatScreen:
Provider.of<IsSpecialist>(context).value
returns
Could not find the correct Provider above this Widget
Why is context broken? How to maintain the original context?
My current temporary solution is to get isSpecialist from Provider before the Navigator and send it as a parameter to the ChatScreen, but I need to find a better solution.
Context is not getting lost, you are just using the wrong context.
suppose A -> B -> C is the widget hierarchy.
I think you are pushing your IsSpecialist at B and accessing it in C but you are using context of A.
It would be more clear if you can post some more code that can give idea about your widget hierarchy.
Provider should be placed on the top of the widget tree, above MaterialApp

How to access to provider field from class that do not have context?

I am using Provider. I have got two classes: class TenderApiData {} it's stand alone class (not widget). How I can write accesstoken to AppState?
class AppState extends ChangeNotifier // putted to ChangeNotifierProvider
{
String _accesstoken; // need to fill not from widget but from stand alone class
String _customer; // Fill from widget
List<String> _regions; // Fill from widget
List<String> _industry; // Fill from widget
...
}
I need way to read\write accesstoken from stand alone classes.
Or I have issue with architecture of my app?
Here is full source code.
You cannot and should not access providers outside of the widget tree.
Even if you could theoretically use globals/singletons or an alternative like get_it, don't do that.
You will instead want to use a widget to do the bridge between your provider, and your model.
This is usually achieved through the didChangeDependencies life-cycle, like so:
class MyState extends State<T> {
MyModel model = MyModel();
#override
void didChangeDependencies() {
super.didChangeDependencies();
model.valueThatComesFromAProvider = Provider.of<MyDependency>(context);
}
}
provider comes with a widget built-in widgets that help with common scenarios, that are:
ProxyProvider
ChangeNotifierProxyProvider
A typical example would be:
ChangeNotifierProxyProvider<TenderApiData, AppState>(
initialBuilder: () => AppState(),
builder: (_, tender, model) => model
..accessToken = tender.accessToken,
child: ...,
);
TL;DR
Swap provider for get_it. The later does DI globally without scoping it to a BuildContext. (It actually has its own optional scoping mechanism using string namedInstance's.)
The rest...
I ran into a similar problem and I believe it comes down to the fact that Provider enforces a certain type of (meta?) architecture, namely one where Widgets are at the top of what you might call the "agency pyramid".
In other words, in this style, widgets are knowledgable about Business Logic (hence the name BLoC architecture), they run the show, not unlike the ViewController paradigm popularised by iOS and also maybe MVVM setups.
In this architectural style, when a widget creates a child widget, it also creates the model for the widget. Here context could be important, for example, if you had multiple instances of the same child widget being displayed simultaneously, each would need its own instance of the underlying model. Within the widget or its descendents, your DI system would need the Context to select the proper one. See BuildContext::findAncestorWidgetOfExactType to get an idea why/how.
This architectural style is the one seemingly encouraged by plain vanilla Flutter, with its paradigms of app-as-a-widget ("turtles all the way down"), non-visual widgets, layout-as-widgets and InheritedWidget for DI (which provider uses I believe)
BUT
Modern app frameworks libs (e.g. redux, mobx) encourage the opposite kind of meta-architecture: widgets at the bottom of the pyramid.
Here widgets are "dumb", just UI signal generators and receivers. The business logic is encapsulated in a "Store" or via "Actions" which interact with a store. The widgets just react to the relevant fields on the store being updated and send Action signals when the user interacts with them.
Which should you use?
In my experience, at least on mobile where the screen realestate is less, scoping a model to a branch in the render tree is seldom required. If it suddenly becomes important then there are plenty of other ways to handle it (indexed array, id lookup map, namedInstances in get_it) than to require linking it to the semantics of UI rendering.
Currently, having spent too much time in iOS ViewControllers, I'm a fan of new systems which enforce better SoC. And personally find Flutter's everything-is-a-widget pardigm to appear a bit messy at times if left untended. But ultimately it's a personal preference.
you can use navigator key
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
and put this key in MaterialApp and wrap it with your provider (TenderApiData)
ChangeNotifierProvider<TenderApiData>(
create: (_) => TenderApiData(),
child: Consumer<TenderApiData>(builder: (context, tenderApiData , child) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'title',
home: SplashScreen());
}),
);
and listen to this provider from anywhere with this navigator key
navigatorKey.currentContext?.read<TenderApiData>();

BLoC: how to pass it?

I would like to know the best way to pass the bloc. I read about the bloc providers, but what`s the difference between using them and just passing the bloc in the constructor like:
ExampleView X = ExampleView(bloc,...)
Actually I find this way easier to test and also a better way to keep the code cleaner. For example, if I have more blocs, something like this can happen:
XBlocProvider(
bloc: XBloc,
child: YBlocProvider(
bloc: Y,
child: ZBlocProvider...
)
or maybe it's just my lack of knowledge.
So, which ones are the benefits?
The problem with this:
ExampleView X = ExampleView(bloc,...)
It only make sense if you use your bloc as normal class/provider with some Stream\ValueNotifier. Otherwise it has some issues.
If it's global bloc, it's more exhausting way to pass it. You should use XBlocProvider on top of MaterialApp.
By the way, if it's global/top level bloc, you can do this:
XBlocProvider(
bloc: xBloc, // Singleton
child XPage,
...
This way, you can access this bloc from anywhere of the application and also you can listen it.
If it's local bloc, because the way we listen Bloc or ChangeNotifierProvider via InheritedWidget's updateShouldNotify method, it doesn't make sense to pass as constructor because you can't use it directly as you intended. You need to put that instance inside BlocProvider and consume it again, so it's extra work.
https://api.flutter.dev/flutter/widgets/InheritedWidget/updateShouldNotify.html
To overcome multi nested BlocProviders you can use MultiProvider or MultiBlocProvider.
Example:
MultiBlocProvider(
providers: [
XProvider(...),
YProvider(...),
ZProvider(...),
],
child: someWidget,
)
There are multi ways to pass depends on your need but don't worry about InheritedWidget because it's pretty fast and convenient way to obtain your XBlocProvider.
At the end, try to comprehend every approach, I especifically suggest you to grasp this article:
https://www.didierboelens.com/2018/12/reactive-programming---streams---bloc---practical-use-cases/
You'll get the idea when to use bloc with provider or as a singleton or instantiate like your example etc.
The first difference is how you access your bloc from the widget.
when you pass it through the constructor you have direct access to it
when you use BlocProvider then, in most cases depending on your
bloc implementation, you have obtain it via Provider which extends InheritedWidget
using context for example:
final xBloc = Provider.of<XBloc>(context);
What's more when you wrap your bloc with BlocProvider then your bloc's scope is limited to this widget's subtree so it can be only accessed by the BlocProviders descendants.
Not having to pass the bloc in the constructor is the benefit of using the provider. It reduces the complexity of your application by enabling you to propagate information down the widget tree with the help of InheritedWidget. All you just need is to access the bloc from the child widget with BlocProvider.of(context).