Analysis of Flutter Scoped_Model

In the front-end development, we often hear terms such as state management redux.

But for people like me to move out of the end came, it is not very familiar with these terms.

Flutter draws as many languages ​​React thought, naturally there will be a corresponding state management.

What is the state management? Why state management?

What is the state management?

Personally I think the state management solution is the communication between the components and the status of centralized management and distribution issues

for example:

For example, I use multiple pages at the same time a User object, when I was one of the places changed after the other places you want to have change, and that this time will need to state management to centrally manage data.

Why state management?

Having said that, another point:

We have already used StatefulWidget, also know that it maintains a State, which is the current state of the Widget.

When we need to change the state of the Widget, you need setState (), which would go again re-build method to redraw.

When simple page when better said, if the page is complex, and every time we click on, or must slide the entire page to build it?

Obviously, this is not consistent with common sense.

I believe many people have heard provide redux... and so on state management of the program,

So what Scoped_Model that?

Scoped_Model

Look at the contents of the Scoped_Model GitHub documentation:

A set of utilities that allow you to easily pass a data Model from a parent Widget down to it's descendants. In addition, it also rebuilds all of the children that use the model when the model is updated. This library was originally extracted from the Fuchsia codebase.

A set of utilities that allow you to easily transfer data model from the parent widget to its offspring. It also rebuilt all the children using the model of the model is updated. This library was originally extracted from group Fuchsia code.

Like other state management, InheritedWidget it is also used by InheritedWidget to manage this data using the Widget.

This will update the Widget when the data changes.

Simple to use Scoped_Model

Look at the official gives Demo:

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';

void main() {
  runApp(MyApp(
    model: CounterModel(),
  ));
}

class MyApp extends StatelessWidget {
  final CounterModel model;

  const MyApp({Key key, @required this.model}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // At the top level of our app, we'll, create a ScopedModel Widget. This
    // will provide the CounterModel to all children in the app that request it
    // using a ScopedModelDescendant.
    return ScopedModel<CounterModel>(
      model: model,
      child: MaterialApp(
        title: 'Scoped Model Demo',
        home: CounterHome('Scoped Model Demo'),
      ),
    );
  }
}

// Start by creating a class that has a counter and a method to increment it.
//
// Note: It must extend from Model.
class CounterModel extends Model {
  int _counter = 0;

  int get counter => _counter;

  void increment() {
    // First, increment the counter
    _counter++;

    // Then notify all the listeners.
    notifyListeners();
  }
}

class CounterHome extends StatelessWidget {
  final String title;

  CounterHome(this.title);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            // Create a ScopedModelDescendant. This widget will get the
            // CounterModel from the nearest parent ScopedModel<CounterModel>.
            // It will hand that CounterModel to our builder method, and
            // rebuild any time the CounterModel changes (i.e. after we
            // `notifyListeners` in the Model).
            ScopedModelDescendant<CounterModel>(
              builder: (context, child, model) {
                return Text(
                  model.counter.toString(),
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      // Use the ScopedModelDescendant again in order to use the increment
      // method from the CounterModel
      floatingActionButton: ScopedModelDescendant<CounterModel>(
        builder: (context, child, model) {
          return FloatingActionButton(
            onPressed: model.increment,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          );
        },
      ),
    );
  }
}
复制代码

Code is a little long, but it does not matter, mostly comments.

Directly copy the code to the project, run look at the results:

The effect is very simple, and we have just started to learn Flutter as examples.

Here's the code to explain,

We can see, first of all is to ScopedModel APP on the very top to initialize:

class MyApp extends StatelessWidget {
  final CounterModel model;

  const MyApp({Key key, @required this.model}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // At the top level of our app, we'll, create a ScopedModel Widget. This
    // will provide the CounterModel to all children in the app that request it
    // using a ScopedModelDescendant.
    return ScopedModel<CounterModel>(
      model: model,
      child: MaterialApp(
        title: 'Scoped Model Demo',
        home: CounterHome('Scoped Model Demo'),
      ),
    );
  }
}
复制代码

Then define a CounterModel:

// Start by creating a class that has a counter and a method to increment it.
//
// Note: It must extend from Model.
class CounterModel extends Model {
  int _counter = 0;

  int get counter => _counter;

  void increment() {
    // First, increment the counter
    _counter++;

    // Then notify all the listeners.
    notifyListeners();
  }
}
复制代码

Notes written above it is clear that must be inherited from Model.

why? We see Model Source:

abstract class Model extends Listenable {
  final Set<VoidCallback> _listeners = Set<VoidCallback>();
  int _version = 0;
  int _microtaskVersion = 0;

  /// [listener] 将在Model更改时调用。
  @override
  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }

  /// [listener] 移除时调用。
  @override
  void removeListener(VoidCallback listener) {
    _listeners.remove(listener);
  }

  /// Returns the number of listeners listening to this model.
  int get listenerCount => _listeners.length;

  /// 仅当Model已更改时由[model]调用。
  @protected
  void notifyListeners() {
    // 我们安排一个微任务来消除可能同时发生的多个更改。
    if (_microtaskVersion == _version) {
      _microtaskVersion++;
      scheduleMicrotask(() {
        _version++;
        _microtaskVersion = _version;

        // Convert the Set to a List before executing each listener. This
        // prevents errors that can arise if a listener removes itself during
        // invocation!
        _listeners.toList().forEach((VoidCallback listener) => listener());
      });
    }
  }
}
复制代码

Can be seen, Model inherited Listenable, so we can call in when their own definition Model notifyListeners()method.

Finally, where it is needed to use the Model ScopedModelDescendantto get.

ScopedModelDescendant<CounterModel>(
  builder: (context, child, model) {
    return Text(
      model.counter.toString(),
      style: Theme.of(context).textTheme.display1,
    );
  },
),
复制代码

Some may feel this way is not very elegant, too much code.

The official also provides another method: ScopedModel.of<CounterModel>(context).

Centralized management status and update Widget

Official example only provides a simple example, does not show its power,

So write ourselves an example.

This example uses the same data in a plurality of pages, and a page in which the update data.

This achieves centralized management of our so-called state.

Results are as follows:

The main code is as follows:

// 点击事件
@override
Widget build(BuildContext context) {
  return FloatingActionButton(
    onPressed: ScopedModel.of<ScopedCounter>(context).increment,
    tooltip: 'Increment',
    child: const Icon(Icons.add),
  );
}


// 接收事件
class CounterLabel extends StatelessWidget {
  const CounterLabel({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print("third counter label build");
    final counter = ScopedModel.of<ScopedCounter>(context, rebuildOnChange: true);
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const Text(
          'You have pushed the button this many times:',
        ),
        Text(
          '${counter.count}',
          style: Theme.of(context).textTheme.display1,
        ),
      ],
    );
  }
}
复制代码

We can see the Widget are stateless, which means we really reached a data update request to update the UI.

Then we can look at the print log, updated only if the use of the Model of Widget.

Or the entire Page are build up.

We print in Page's build process:

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    print("home page build");
  }
}

........ // 第二第三页同理
print("second home page build");
print("third counter label build");
复制代码

Then in print in CounterLabel

class CounterLabel extends StatelessWidget {
  const CounterLabel({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print("home counter label build");
  }
}

........ // 第二第三页同理
print("second counter label build");
print("third counter label build");
复制代码

Operating results are as follows:

We can see, really only updated Widget use of the Model.

to sum up

Flutter in the state administration has a lot, redux, fish_redux so much more.

And Scoped_Model I used the easiest, most comfortable one.

Because I was engaged in mobile development, so I would choose Scoped_Model.

Next simply talk about the principle of Scoped_Model.

The complete code has been transmitted to GitHub: github.com/wanglu1209/...

Reproduced in: https: //juejin.im/post/5cfe421d518825668a37429e

Guess you like

Origin blog.csdn.net/weixin_34200628/article/details/91430638