In my last article, Provider GET, the mainstream state management framework for Flutter, I analyzed the reasons for the emergence of state management frameworks for Flutter and their design ideas. On the whole, state management can be divided into two categories. One is the frame that relies on the “Flutter tree mechanism”, such as Provider and Bloc. Another is a “dependency injection” framework like GET. Today I would like to share with you some of the problems I have encountered in daily development and some of the designs that I think they don’t make sense. Hope to help you reduce the possibility of mistakes in daily development.


A, ProviderNotFoundException!!!!!!

The problem with such state frames that rely on tree mechanisms is a thousand words to make an exception

ProviderNotFoundException!!!!!!!!!!

Yes, most of the problems we’ve encountered so far have come from this exception. Because the tree mechanism depends on the context we are using, we can get this exception if the context is not correct, in the following cases:

Context has a higher level than Provider

This problem is the same as the example mentioned in the further understanding of BuildContext article on Flutter.

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: FlatButton(
              onPressed: () {
                Navigator.of(context).push(
                    MaterialPageRoute(builder: (context) => SecondPage()));
              },
              child: Text('jump')),),),); }}Copy the code

When we click the jump button, we get an exception,

flutter: Navigator operation requested with a context that does not include a Navigator.
flutter: The context used to push or pop routes from the Navigator must be that of a widget that is a 
flutter: descendant of a Navigator widget.
Copy the code

This error is easy to understand. The mainstream state management framework of Flutter provider GET analysis and Thinking mentioned that the context corresponds to the Widget’s node in the Element tree by looking up the Presenter in the parent node. If the context is too high, you can’t get it.

The same goes for using a provider. Once you know how it works, it’s easy to solve it by using the context under the provider.

2. The context has been removed from the tree

I have encountered this in practice. When a page is opened, loading is displayed to make a request, and then content or abnormal pages are displayed according to the network status. If the display is abnormal we will display it as a “reload” page, click on the network request.

The problem in that case was that the network state was successful when the request was made again, and the page was displayed as normal. However, the business logic still uses “reload” the context of the widget to get the Provider. Because the reload widget has been removed from the tree, an error is reported when using the results.

This appears to be a simple problem, but the first time I encountered it, I didn’t think in this direction at all. I discovered the problem by debugging the widget corresponding to the current context step by step and checking its parent node.

To summarize, for ProviderNotFoundException such exceptions, is often caused by error context object. Don’t panic when you encounter them. Grasp the heart of the Flutter tree mechanism. Debug the current context object to see if its widget, parent, and other properties are as expected.


Second, some practical problems in GET

As we mentioned last time, the core design of GET is to store Presenter in a singleton Map that can be accessed anywhere at any time.

RuntimeType + tag A tag is a String identifier and a value is a corresponding Presenter instance.

This global singleton store actually has a painful problem of repeating types:

1. Type repetition

For example, the jump between taobao product details page.

Because pages are the same class, just different instances. Since Presenter is already stored on the first page, use it on the second pageGet.find<A>You will find that the Presenter is still retrieved from the previous page.

To solve this problem, we must give each Presenter a unique tag, such as an item ID, when getting. Put.

If so, the tag must be known when retrieving a Presenter, and sharing a tag when accessing a Presenter across pages becomes a new problem.

You might be wondering if providers don’t have this problem. Because when we use providers, we usually follow the page. In other words, each page has a Map for storage. Different pages have different maps, so there is no need to consider the problem of type duplication.

But cross-page access to presenters is still a problem.

2, Getbuilder < T >

This last point is also a real problem encountered by a group of Fluttercandidate, and it is also where I personally think the framework is unreasonable. Getbuilder

returns it to use in builder by passing the Presenter generic.

GetBuilder<Presenter>(
   /// The corresponding instance of Controller is returned directly from builder
   builder: (presenter) => 
   Text('clicks: ${presenter.count}'))Copy the code

At that time, brother Dei was also in the same situation where the product details page repeatedly jumped. In order to solve the problem of type repetition, he used time stamp as tag.

final Presenter presenter = Get.put(Presenter(),tag: DateTime.now().toString());
Copy the code

And then I was dumbfounded using GetBuilder:

GetBuilder<Presenter>(
   /// Tag is not the same as the tag above, so we cannot find the instance
   tag: DateTime.now().toString(),
   builder: (presenter) => 
   Text('clicks: ${presenter.count}'))Copy the code

Once you understand the injection of Get, the principle behind GetBuilder is pretty simple. GetBuilder is a global singleton Map store, so all you need is a key (runtimeType+tag) to retrieve the Presenter. In this example, the tag of put and GetBuidler is different (the current timestamp), so getting GetBuilder is not available. Simply save the tag and use it in the GetBuilder.

ValueListenableBuilder allows the user to manually pass in an instance of Presenter as follows.

GetBuilder<Presenter>(
   /// Manually passing in a presenter means that the presenter has established a listening relationship with the GetBuilder.
   presenter: presenter,
   builder: (presenter) => 
   Text('clicks: ${presenter.count}'))Copy the code

This may seem like an extra parameter for presenter, but it must be one that you can get in advance. Manually specifying the “listener” and “listener” bindings I think will give developers a clearer idea of who the presenter that GetBuilder is currently listening to is. This eliminates the timestamp tag problem in the first place.


Third, summary

The above is some of the problems I encountered in practice, in fact, the essence of their different design ideas. For example, a provider that uses a tree mechanism must have the possibility of a context exception. The global singleton approach to GET does not avoid type duplication, or recycling, for that matter. Therefore, for the framework, it is more important to understand his design ideas, rather than mindlessly follow the trend. The mainstream state management framework for Flutter, provider Get, analyzes and thinks about Flutter, hoping to help you avoid some pitfalls.

This is the end of status management. In the next part, I will focus on Dart VIRTUAL machine and ISOLATE. Welcome to pay attention.

If you have any questions, please contact me through the official account. If the article inspires you, I hope to get your thumbs up, attention and collection, which is the biggest motivation for me to continue writing. Thanks~

The most detailed guide to the advancement and optimization of Flutter has been collected in Advance of Flutter or RunFlutter.

Past highlights:

Advance optimization of Flutter

The Flutter core rendering mechanism

Flutter routing design and source code analysis