Flutter状态管理provider的使用和封装

Flutter提供了InheritedWidget类,帮助我们处理父子组件之间的状态管理。provider是InheritedWidget的封装,让开发者易于使用和服用。但是初看provider的文档,有点让人头大:

namedescription
ProviderThe most basic form of provider. It takes a value and exposes it, whatever the value is.
ListenableProviderA specific provider for Listenable object. ListenableProvider will listen to the object and ask widgets which depend on it to rebuild whenever the listener is called.
ChangeNotifierProviderA specification of ListenableProvider for ChangeNotifier. It will automatically call ChangeNotifier.dispose when needed.
ValueListenableProviderListen to a ValueListenable and only expose ValueListenable.value.
StreamProviderListen to a Stream and expose the latest value emitted.
FutureProviderTakes a Future and updates dependents when the future completes.

不是说provider是易于使用吗?我只想以一种的简单的方式管理状态,却给我这么多选择,到底我该选择哪个呢?选择困难症急的想薅头发。

使用

新建Futter项目,更改默认的计数器布局,效果如下:

 

 

 

点击FlatButton,更改应用程序的计数器状态,使计数器加1,前两行的text显示计数器状态最新值,FlatButton和两个text是不同部分的widget。

  1. 在的pubspec.yaml文件中依赖provider:
dependencies:
  flutter:
    sdk: flutter
  provider: ^4.1.2
复制代码
  1. 导入: import 'package:provider/provider.dart';

Provider

Provider是provider包中最基本的提供者widget类型。它可以给包括住的所有widget提供值,但是当该值改变时,并不会更新widget。

新增MyModel类,作为要让Provider提供出去的值,把计数器的数值counter声明到这里,并且更改计数值的方法也放在这里,点击按钮的时候,调用MyModel对象的incrementCounter(),延时2秒并更改counter:

class MyModel {
  
  MyModel({this.counter=0});

  int counter = 0;

  Future<void> incrementCounter() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    print(counter);
  }
}
复制代码

在widget树的顶部包裹Provider小部件,将MyModel对象通过Provider提供给widget树。然后使用了两种获取Provider提供值的方式,在Column里:

  1. 先使用Provider.of(context)获取到MyModel对象的引用;
  2. 然后使用Consumer小部件获得对MyModel对象的引用;
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider(
      create: (_) => MyModel(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                // 获取到provider提供出来的值
                MyModel _model = Provider.of<MyModel>(context);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('当前是:${_model.counter}'));
              },
            ),
            Consumer<MyModel>(
                // 获取到provider提供出来的值
              builder: (context, model, child) {
                return Container(
                  margin: const EdgeInsets.only(top: 20),
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(20),
                  alignment: Alignment.center,
                  color: Colors.lightGreen,
                  child: Text(
                    '${model.counter}',
                  ),
                );
              },
            ),
            Consumer<MyModel>(
               // 获取到provider提供出来的值
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed:model.incrementCounter,
                    child: Icon(Icons.add));
              },
            ),
          ],
        ),
      ),
    );
  }
}

复制代码

点击FlatButton,model调用incrementCounter()函数,计数值加1。但是并不会重建UI,因为该Provider小部件不会监听其提供的值的更改。

 

 

 

打印出计数值的变化

 

 

 

ChangeNotifierProvider

与最基础的Provider小部件不同,ChangeNotifierProvider会监听其提供出去的模型对象中的更改。当有值更改后,它将重建下方所有的Consumer和使用Provider.of(context)监听并获取提供值的地方。

代码中更改Provider为ChangeNotifierProvider。MyModel混入ChangeNotifier(继承也一样)。然后更改counter之后调用notifyListeners(),这样ChangeNotifierProvider就会得到通知,并且Consumer和监听的地方将重建其小部件。

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyModel(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                MyModel _model = Provider.of<MyModel>(context);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('当前是:${_model.counter}'));
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return Container(
                  margin: const EdgeInsets.only(top: 20),
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(20),
                  alignment: Alignment.center,
                  color: Colors.lightGreen,
                  child: Text(
                    '${model.counter}',
                  ),
                );
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.incrementCounter,
                    child: Icon(Icons.add));
              },
            ),
          ],
        ),
      ),
    );
  }
}

class MyModel with ChangeNotifier{
  //                                               <--- MyModel
  MyModel({this.counter = 0});

  int counter = 0;

  Future<void> incrementCounter() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    print(counter);
    notifyListeners();
  }
}
复制代码

 

 

 

 

每次点击,都会更改计数器的值,如果第一行的计数值是保留初始值,不更新呢?很简单,把Provider.of的监听器设置为false,这样更改后就不会重新构建第一行的text: MyModel _model = Provider.of<MyModel>(context,listen: false);

 

 

 

FutureProvider

FutureProvider基本上只是普通FutureBuilder的包装。我们需要给它提供一些显示在UI中的初始数据,还要为它设置要提供值的Future。在Future完成的时候,FutureProvider会通知Consumer重建自己的小部件。

在下面的代码中,使用了一个counter为0的MyModel向UI提供一些初始数据,并且添加了一个Future函数,可在3秒后返回一个counter为1的MyModel。 和基类Provider一样,FutureProvider它不会监听模型本身内的任何更改。在下面的代码中依旧通过按钮点击事件使counter加1,但是对UI没有影响。

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureProvider(
      initialData: MyModel(counter: 0),
      create: (context) => someAsyncFunctionToGetMyModel(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                MyModel _model = Provider.of<MyModel>(context, listen: false);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('当前是:${_model.counter}'));
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return Container(
                  margin: const EdgeInsets.only(top: 20),
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(20),
                  alignment: Alignment.center,
                  color: Colors.lightGreen,
                  child: Text(
                    '${model.counter}',
                  ),
                );
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.incrementCounter,
                    child: Icon(Icons.add));
              },
            ),
          ],
        ),
      ),
    );
  }

  Future<MyModel> someAsyncFunctionToGetMyModel() async {
    //  <--- async function
    await Future.delayed(Duration(seconds: 3));
    return MyModel(counter: 1);
  }
}

class MyModel with ChangeNotifier {
  //                                               <--- MyModel
  MyModel({this.counter = 0});

  int counter = 0;

  Future<void> incrementCounter() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    print(counter);
    notifyListeners();
  }
}
复制代码

FutureProvider通过设置的Future完成后会通知Consumer,重新build。但是,Future完成后,点击按钮也不会更新UI。

FutureProvider适用于没有刷新和变更的页面,和FutureBuilder一样的作用。

StreamProvider

StreamProvider基本上是StreamBuilder的包装,和上面的FutureProvider一样。不同的是StreamProvider提供的是流,FutureProvider需要的一个Future。

StreamProvider也不会监听model本身的变化。它仅监听流中的新事件:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider(
      initialData: MyModel(counter: 0),
      create: (context) => getStreamOfMyModel(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                MyModel _model = Provider.of<MyModel>(context, listen: false);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('当前是:${_model.counter}'));
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return Container(
                  margin: const EdgeInsets.only(top: 20),
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(20),
                  alignment: Alignment.center,
                  color: Colors.lightGreen,
                  child: Text(
                    '${model.counter}',
                  ),
                );
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.incrementCounter,
                    child: Icon(Icons.add));
              },
            ),
          ],
        ),
      ),
    );
  }

  Stream<MyModel> getStreamOfMyModel() {
    return Stream<MyModel>.periodic(
        Duration(seconds: 1), (x) => MyModel(counter: x)).take(10);
  }
}

class MyModel with ChangeNotifier {
  //                                               <--- MyModel
  MyModel({this.counter = 0});

  int counter = 0;

  Future<void> incrementCounter() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    print(counter);
    notifyListeners();
  }
}
复制代码

给StreamProvider设置了一个每隔1秒更新一次的stream,ui上的计数值也是每隔一秒改变一次。但是点击按钮同样不会刷新ui。所以也可以认为是一个StreamBuilder。

ValueListenableProvider

ValueListenableProvider类似于ValueChange的封装,它的作用和ChangeNotifierProvider一样,在值改变的时候,会通知Consumer重新build,但是使用起来比ChangeNotifierProvider复杂,需要先用Provider提供MyModel给Consumer,然后把MyModel里的ValueNotifier给ValueListenableProvider:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<MyModel>(
      create: (context) => MyModel(),
      child: Consumer<MyModel>(
        builder: (context, myModel, child) {
          return ValueListenableProvider<int>.value(
            value: myModel.counter,
            child: Scaffold(
              appBar: AppBar(
                title: Text('provider'),
              ),
              body: Column(
                children: <Widget>[
                  Builder(
                    builder: (context) {
                      var count = Provider.of<int>(context);
                      return Container(
                          margin: const EdgeInsets.only(top: 20),
                          width: MediaQuery.of(context).size.width,
                          padding: const EdgeInsets.all(20),
                          alignment: Alignment.center,
                          color: Colors.lightBlueAccent,
                          child: Text('当前是:$count'));
                    },
                  ),
                  Consumer<int>(
                    builder: (context, value, child) {
                      return Container(
                        margin: const EdgeInsets.only(top: 20),
                        width: MediaQuery.of(context).size.width,
                        padding: const EdgeInsets.all(20),
                        alignment: Alignment.center,
                        color: Colors.lightGreen,
                        child: Text(
                          '$value',
                        ),
                      );
                    },
                  ),
                  Consumer<MyModel>(
                    builder: (context, model, child) {
                      return FlatButton(
                          color: Colors.tealAccent,
                          onPressed: model.incrementCounter,
                          child: Icon(Icons.add));
                    },
                  ),
                ],
              ),
            ),
          );
        }
      ),
    );
  }
}

class MyModel {
  ValueNotifier<int> counter = ValueNotifier(0);
  Future<void> incrementCounter() async {
    await Future.delayed(Duration(seconds: 2));
    print(counter.value++);
    counter.value = counter.value;
  }
}
复制代码

ListenableProvider

ListenableProvider和ChangeNotifierProvider一样, 区别在于,如果Model是一个复杂模型ChangeNotifierProvider 会在你需要的时候,自动调用其 _disposer 方法,所以一般还是使用ChangeNotifierProvider即可。

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListenableProvider<MyModel>(
      create: (context) => MyModel(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                MyModel modol = Provider.of<MyModel>(context);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('当前是:${modol.counter}'));
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return Container(
                  margin: const EdgeInsets.only(top: 20),
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(20),
                  alignment: Alignment.center,
                  color: Colors.lightGreen,
                  child: Text(
                    '${model.counter}',
                  ),
                );
              },
            ),
            Consumer<MyModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.incrementCounter,
                    child: Icon(Icons.add));
              },
            ),
          ],
        ),
      ),
    );
  }
}

class MyModel with ChangeNotifier {
  int counter = 0;

  Future<void> incrementCounter() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    notifyListeners();
    print(counter);
  }
}
复制代码

MultiProvider

上面的示例都仅使用了一个Model对象。如果需要提供第二种类型的Model对象,可以嵌套Provider。但是,嵌套迷之缩进,可读性低。这时候使用MultiProvider非常简洁,

我们改下上面的计数器,一般首页会有一个banner和列表。我们用上面的计数器模拟banner,下面的计数器模拟列表:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<BannerModel>(create: (context) => BannerModel()),
        ChangeNotifierProvider<ListModel>(create: (context) => ListModel()),
      ],
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                BannerModel modol = Provider.of<BannerModel>(context);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('当前Banner有几个:${modol.counter}'));
              },
            ),
            Consumer<ListModel>(
              builder: (context, model, child) {
                return Container(
                  margin: const EdgeInsets.only(top: 20),
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(20),
                  alignment: Alignment.center,
                  color: Colors.lightGreen,
                  child: Text(
                    '当前Banner有几个:${model.counter}',
                  ),
                );
              },
            ),
            Consumer<BannerModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.getBanner,
                    child: Text("获取banner"));
              },
            ),
            Consumer<ListModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.getList,
                    child: Text("获取列表"));
              },
            ),
          ],
        ),
      ),
    );
  }
}

class BannerModel with ChangeNotifier {
  int counter = 0;

  Future<void> getBanner() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    notifyListeners();
    print(counter);
  }
}

class ListModel with ChangeNotifier {
  int counter = 0;

  Future<void> getList() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    notifyListeners();
    print(counter);
  }
}
复制代码

 

 

 

 

按下banner按钮,就单独获取banner的数值,并更新banner的Consumer。列表的同理。

ProxyProvider

如果要提供两个Model,但是其中一个Model取决于另一个Model,在这种情况下,可以使用ProxyProvider。A ProxyProvider从一个Provider获取值,然后将其注入另一个Provider,

把上面的改下,比如的上传图片功能,需要先把图片提交到图片服务器,然后再把链接发送到后台服务器:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<PicModel>(create: (context) => PicModel()),
        ProxyProvider<PicModel, SubmitModel>(
          update: (context, myModel, anotherModel) => SubmitModel(myModel),
        ),
      ],
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Builder(
              builder: (context) {
                PicModel modol = Provider.of<PicModel>(context);
                return Container(
                    margin: const EdgeInsets.only(top: 20),
                    width: MediaQuery.of(context).size.width,
                    padding: const EdgeInsets.all(20),
                    alignment: Alignment.center,
                    color: Colors.lightBlueAccent,
                    child: Text('提交图片:${modol.counter}'));
              },
            ),

            Consumer<PicModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.upLoadPic,
                    child: Text("提交图片"));
              },
            ),
            Consumer<SubmitModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: model.subMit,
                    child: Text("提交"));
              },
            ),
          ],
        ),
      ),
    );
  }
}

class PicModel with ChangeNotifier {
  int counter = 0;

  Future<void> upLoadPic() async {
    await Future.delayed(Duration(seconds: 2));
    counter++;
    notifyListeners();
    print(counter);
  }
}

class SubmitModel {
  PicModel _model;

  SubmitModel(this._model);

  Future<void> subMit() async {
    await _model.upLoadPic();
  }
}
复制代码

基于MVVM模式封装Provider

相信大家都已经理解provider的流程,如下图:

 

 

 

上面已经演示完了Provider的用法,在开发中,我们需要Model充当ViewModel,处理业务逻辑,但是每次都写样板代码的话也很麻烦,所以需要封装下,易于使用。

 

 

 

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<LoginViewModel>(
      create: (BuildContext context) {
        return LoginViewModel(loginServive: LoginServive());
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            Consumer<LoginViewModel>(
              builder: (context, model, child) {
                return Text(model.info);
              },
            ),
            Consumer<LoginViewModel>(
              builder: (context, model, child) {
                return FlatButton(
                    color: Colors.tealAccent,
                    onPressed: () => model.login("pwd"),
                    child: Text("登录"));
              },
            ),
          ],
        ),
      ),
    );
  }
}

/// viewModel
class LoginViewModel extends ChangeNotifier {
  LoginServive _loginServive;
  String info = '请登录';

  LoginViewModel({@required LoginServive loginServive})
      : _loginServive = loginServive;

  Future<String> login(String pwd) async {
    info = await _loginServive.login(pwd);
    notifyListeners();
  }
}

/// api
class LoginServive {
  static const String Login_path = 'xxxxxx';

  Future<String> login(String pwd) async {
    return new Future.delayed(const Duration(seconds: 1), () => "登录成功");
  }
}
复制代码

这种页面写法,基本每个页面都要,下面我们一步一步开始封装。

  1. 一般页面载入的时候会显示一个loading,然后加载成功展示数据,失败就展示失败页面,所以枚举一个页面状态:
enum ViewState { Loading, Success,Failure }
复制代码
  1. ViewModel都会在页面状态属性改变后更新ui,通常会调用notifyListeners,把这一步移到BaseModel中:
class BaseModel extends ChangeNotifier {
  ViewState _state = ViewState.Loading;

  ViewState get state => _state;

  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }
}
复制代码
  1. 我们知道ui里需要ChangeNotifierProvider提供Model,并且用Consumer更新ui。因此我们也将其内置到BaseView中:
class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
  final Widget Function(BuildContext context, T value, Widget child) builder;
  final T model;
  final Widget child;

  BaseWidget({Key key, this.model, this.builder, this.child}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _BaseWidgetState();

}

class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {

  T model;

  @override
  void initState() {
    model = widget.model;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>.value(
      value: model,
      child: Consumer<T>(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }
}
复制代码
  1. 有时候我们的页面数据只是局部更新,Consumer的child属性就是模型更改时不需要重建的UI,所以我们将需要更新的ui放在builder里,不需要更新的写在child里:
Consumer<LoginViewModel>(
  // Pass the login header as a prebuilt-static child
  child: LoginHeader(controller: _controller),
  builder: (context, model, child) => Scaffold(
    ...
    body: Column (

      children: [
//不更新的部分
        child,
        ...
      ]
    )
复制代码
  1. 大多时候,我们已进入一个页面,就要获取数据,所以我们也把这个操作移入基类:
class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {

final Function(T) onModelReady;
...

 BaseWidget({
   ...
    this.onModelReady,
  });
  ...
}

...

@override
void initState() {
  model = widget.model;

  if (widget.onModelReady != null) {
    widget.onModelReady(model);
  }

  super.initState();
}
复制代码

现在,我们用封装的基类完成登录页面:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BaseWidget<LoginViewModel>(
      model: LoginViewModel(loginServive: LoginServive()),
      builder: (context, model, child) => Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            model.state == ViewState.Loading
                ? Center(
                    child: CircularProgressIndicator(),
                  )
                : Text(model.info),
            FlatButton(
                color: Colors.tealAccent,
                onPressed: () => model.login("pwd"),
                child: Text("登录")),
          ],
        ),
      ),
    );
  }
}

/// viewModel
class LoginViewModel extends BaseModel {
  LoginServive _loginServive;
  String info = '请登录';

  LoginViewModel({@required LoginServive loginServive})
      : _loginServive = loginServive;

  Future<String> login(String pwd) async {
    setState(ViewState.Loading);
    info = await _loginServive.login(pwd);
    setState(ViewState.Success);
  }
}

/// api
class LoginServive {
  static const String Login_path = 'xxxxxx';

  Future<String> login(String pwd) async {
    return new Future.delayed(const Duration(seconds: 1), () => "登录成功");
  }
}

enum ViewState { Loading, Success, Failure, None }

class BaseModel extends ChangeNotifier {
  ViewState _state = ViewState.None;

  ViewState get state => _state;

  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }
}

class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
  final Widget Function(BuildContext context, T model, Widget child) builder;
  final T model;
  final Widget child;
  final Function(T) onModelReady;

  BaseWidget({
    Key key,
    this.builder,
    this.model,
    this.child,
    this.onModelReady,
  }) : super(key: key);

  _BaseWidgetState<T> createState() => _BaseWidgetState<T>();
}

class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {
  T model;

  @override
  void initState() {
    model = widget.model;

    if (widget.onModelReady != null) {
      widget.onModelReady(model);
    }

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      create: (BuildContext context) => model,
      child: Consumer<T>(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }
}


作者:艾维码
链接:https://juejin.im/post/5ed3df5951882542fc624efb
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值