31

Flutter基础--状态管理

 4 years ago
source link: https://www.tuicool.com/articles/jEjqMj3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Flutter基础–状态管理

当我们使用编译器创建一个新Flutter应用的时候,我们可以在主界面看到两个小部件StatelessWidget和StatefulWidget。这是两个最常见使用最频繁的小部件了。

StatelessWidget ,StatefulWidget

  1. StatelessWidget 状态不可改变的,它内部的数值和UI都应该是常量不可改变
  2. StatefulWidget 状态可变,我们可以通过点击,或者网络获取数据等来动态的改变界面。

假如我们要实现如下图的功能,点击加一。如果使用StatelessWidget,会发现点击的时候count是加一了,但是界面没有刷新。应该使用StatefulWidget,当count加一的时候通过 setState(() { _count++;}); 方法来改变count的值,这时候就发现界面可以刷新了。

AV7vYvB.gif

比如

import 'package:flutter/material.dart';

class StateManagerDemo extends StatefulWidget {
  @override
  _StateManagerDemoState createState() => _StateManagerDemoState();
}

class _StateManagerDemoState extends State<StateManagerDemo> {
  int _count = 0;
  void countCallBack(){
    setState(() {
      _count++;
    });
    debugPrint('$_count');
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StateManagerDemo'),
        elevation: 0.0,
      ),
      body: Counter0(_count),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: countCallBack,
      ),
    );
  }
}
//------
class Counter0 extends StatelessWidget {
  final int count;
  Counter0(this.count);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Chip(
        label: Text('$count'),
      ),
    );
  }
}
//-----
class Counter extends StatelessWidget {
  final int count;
  final VoidCallback voidCallback;
  Counter(this.count,this.voidCallback);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ActionChip(
        label: Text('$count'),
        onPressed: voidCallback,
      ),
    );
  }
}

上面的代码中,StateManagerDemo有两个子部件Counter0和Counter。当我们点击按钮的时候,_count的值++,然后传递给子部件。

Counter0是直接接受父部件传过来的参数。

Counter不仅接收父部件传过来的参数,还有一个回调。这样点击它的时候,会执行父部件中的回调方法,也能改变自身的显示。

InheritedWidget

上面的情况是只有一层,比如Counter小部件中使用了父部件的count这个变量,假如Counter没有用到这个变量而是它的子类用到了这个变量,我们还要一层一层的传下去吗,这有点麻烦啊,这时候可以使用InheritedWidget这个类来管理。

import 'package:flutter/material.dart';

//使用InheritedWidget来管理状态,
class ContentProvider extends InheritedWidget {
  final int count;
  final VoidCallback countCallBack;
  final Widget child;
  const ContentProvider({
    this. count,
     this. countCallBack,
    this. child,
  }): assert(child != null),
        super(child: child);

  static ContentProvider of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(
        ContentProvider) as ContentProvider;
  }
  //是否通知继承该小部件的小部件更新
  @override
  bool updateShouldNotify(ContentProvider old) {
    return true;
  }
}
class StateManagerDemo extends StatefulWidget {
  @override
  _StateManagerDemoState createState() => _StateManagerDemoState();
}

class _StateManagerDemoState extends State<StateManagerDemo> {
  int _count = 0;
  void countCallBack(){
    setState(() {
      _count++;
    });
    debugPrint('$_count');
  }
  @override
  Widget build(BuildContext context) {
  //ContentProvider放在最外层,指定参数count和callback
    return ContentProvider(
      count: _count,
      countCallBack: countCallBack,
      child: Scaffold(
        appBar: AppBar(
          title: Text('StateManagerDemo'),
          elevation: 0.0,
        ),
        body: Counter1(),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: countCallBack,
        ),
      ),
    );
  }
}

class Counter1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //直接使用ContentProvider中的参数
    final int count = ContentProvider.of(context).count;
    final VoidCallback voidCallback = ContentProvider.of(context).countCallBack;
    return Center(
      child: ActionChip(
        label: Text('$count'),
        onPressed: voidCallback,
      ),
    );
  }
}

首先定义一个数据提供者ContentProvider继承InheritedWidget,里面定义我们需要的count和回调。提供一个of方法让外界可以拿到它的实例,方便拿到方法。

然后将这个ContentProvider放在主布局的最外层,并传入需要的参数count和callBack。这样它的子部件中就都能访问到这个参数了。

最后在子部件Counter1中直接使用ContentProvider中的参数。

ScopedModel

还可以使用ScopedModel来完成状态管理

这是一个第三方的库,该库最初是从Fuchsia代码库中提取的,使用时需要先导入包,在pubspec.yaml文件中添加依赖

dependencies:
   scoped_model: 1.0.1

https://pub.dev/packages?q=scoped_model 这里可以看到最新版本。使用这个库的时候我们在StatelessWidget中也可以改变UI

该库主要分为三个部分

  1. Model class 扩展此类以创建自己的模型,例如SearchModel或UserModel。我们可以监听数据的变化!
  2. ScopedModel 小部件 把Model包装到ScopedModel中,它内部的所有部件都能拿到model中的数据
  3. ScopedModelDescendant 只要Model发生改变 它就会改变

比如用ScopedModel实现前面的功能

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

class CountModel extends Model{
   int _count = 0;
   int get count =>_count;

   void countIncrease(){
     _count ++;
     //通知改变
     notifyListeners();
   }
}

class StateModelDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModel<CountModel>(
      model: CountModel() ,
      child: Scaffold(
        appBar: AppBar(
          title: Text('StateModelDemo'),
        ),
        body: Counter2(),
        floatingActionButton: ScopedModelDescendant<CountModel>(
          rebuildOnChange: false,
          builder: (context, child, model) => FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: (){
              model.countIncrease();
            },
          ),
        ),
      ),
    );
  }
}

class Counter2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<CountModel>(
      builder: (context, child, model) => Center(
        child: ActionChip(
          label: Text('${model.count}'),
          onPressed: (){
            model.countIncrease();
          },
        ),
      ),
    );
  }
}

Mobx

Mobx在前端中用的多,且很好用,所以Flutter也引入了,对于我们Android开发者来说跟学前面的成本都一样哈哈。

查看版本

https://pub.dev/packages?q=Mobx

github

https://github.com/mobxjs/mobx.dart/tree/master/mobx_codegen

首先需要去pubspec.yaml文件中引入依赖

dependencies:
  mobx: ^0.3.8
  flutter_mobx: ^0.3.3
dev_dependencies:
  mobx_codegen: ^0.3.9
  build_runner: ^1.7.0

然后开始使用mobx完成之前的功能

首先创建一个store类

import 'package:mobx/mobx.dart';
//包含生成的文件
part 'state_manager_demo.g.dart';

class Counter = _Counter3 with _$Counter;

abstract class _Counter3 with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}

创建完成之后,我们会发现part后面的内容和_$Counter都会报错。说是找不到,需要我们生成。来到AndroidStudio的terminal窗口执行下面命令来生成文件

flutter packages pub run build_runner build

这时候就可以看到我们自己的文件state_manager_demo.dart文件旁边生成了一个新文件state_manager_demo.g.dart,而且不在报错了。

如果想要修改后.g.dart文件也能自动修改执行

pub run build_runner watch

下面去界面中使用它

class MobxDemo extends StatefulWidget {
  @override
  _MobxDemoState createState() => _MobxDemoState();
}

class _MobxDemoState extends State<MobxDemo> {
  final Counter counter = Counter();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StateModelDemo'),
      ),

      body: Center(
        child: Observer(
            builder: (_)=>ActionChip(
              label: Text('${counter.value}'),
              onPressed: (){
                counter.increment();
              },
            ),
        ),
      ),

      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed:(){
          counter.increment();
        },
      ),
    );
  }
}

很简单继承StatefulWidget,需要监听的小部件使用Observer包裹起来。然后创建一个成员变量 final Counter counter = Counter(); 内部就可以直接使用Counter中的变量和方法了。运行效果跟前面的一样。

综合来看这几种状态管理的方式 ,我感觉Mobx是最好用的一个。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK