Flutter中状态管理

在flutter开发中,我们对State来进行管理,通过State的改变,来更新界面的刷新,状态管理分为两大类:
1.短时状态管理(定时器,动画效果等)
2.应用状态管理(用户信息,购物车等)

● 共享状态管理flutter官方提供了两种方案:

1. InheritedWidget
2. Provider(Provider是目前官方推荐的全局状态管理工具)

一、InheritedWidget 用法示例:

1. InheritedWidget 用法
① 继承 InheritedWidget
② 实现updateShouldNotify方法
③ 定义一个需要共享的状态,如counter;
④ 通过静态对象拿到对象,如counter;

InheritedWidget 写法如下:

class FCounterWidget extends  InheritedWidget {
  // 1.共享的数据
  final int counter;

  // 2.定义构造方法
  FCounterWidget({this.counter, Widget child}): super(child: child);

  // 3.获取组件最近的当前InheritedWidget
  static FCounterWidget of(BuildContext context) {
    // 沿着Element树, 去找到最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }


  // 4.绝对要不要回调State中的didChangeDependencies
  // 如果返回true: 执行依赖当期的InheritedWidget的State中的didChangeDependencies
  @override
  bool updateShouldNotify(FCounterWidget oldWidget) {
    return oldWidget.counter != counter;
  }
}

DEMO用法:

class InheritedWidgetPage extends StatefulWidget {
  @override
  _InheritedWidgetPageState createState() => _InheritedWidgetPageState();
}

class _InheritedWidgetPageState extends State<InheritedWidgetPage> {
  int _counter = 100;
  @override
  Widget build(BuildContext context) {
    return  Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
      body: FCounterWidget(
        counter: _counter,
        child: Center(
          child: Column(
            children: <Widget>[
              Text("当前计数: $_counter", style: TextStyle(fontSize: 30),),
            ],
          ),
        ),
      ),

      floatingActionButton:FloatingActionButton(
        child: Icon(Icons.add),
          onPressed: () {
            setState(() {
              _counter += 1;
            });
          },
      ),
    );
  }
}
二、Provider 用法示例:

2.Provider 用法 [官方推荐]
前提: 在项目中添加依赖 provider: ^4.1.2
① 创建需要共享的数据
② 在main函数中ChangeNotifierProvider
③ 使用共享的数据

核心代码如下:

//1.创建自己要共享的数据
class ContentViewModel extends ChangeNotifier {
  int _counter = 100;
  int get counter => _counter;
  set counter(int value) {
    _counter = value;
    notifyListeners();
  }
}

//2.在应用的main函数中使用ChangeNotifierProvider
void main() {
  runApp(
      ChangeNotifierProvider(
        create: (ctx) => ContentViewModel(),
        child: MyApp(),
      )
  );
}

//3.在其他位置使用共享的数据
class ProviderWidget extends StatefulWidget {
  @override
  _ProviderWidgetState createState() => _ProviderWidgetState();
}

class _ProviderWidgetState extends State<ProviderWidget> {
  @override
  Widget build(BuildContext context) {
    int counter = Provider.of<ContentViewModel>(context).counter;
    return  Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
      body: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),

      floatingActionButton:Consumer<ContentViewModel>(
          builder:(context,contentVM,child) {
            return FloatingActionButton(
              child: Icon(Icons.adb),
              onPressed: () {
                contentVM.counter +=1;
              },
            );
          }
      )
    );
  }
}


int counter = Provider.of<ContentViewModel>(context).counter;这种共享数据的方式不好,写法不推荐,一般更推荐下面的写法:

class _ProviderWidgetState extends State<ProviderWidget> {
  @override
  Widget build(BuildContext context) {
    int counter = Provider.of<ContentViewModel>(context).counter;
    return  Scaffold(
        appBar: AppBar(
          title: Text("InheritedWidget"),
        ),
        body: Consumer<ContentViewModel>(
            builder:(context,contentVM,child) {
              return Text("当前计数: $counter", style: TextStyle(fontSize: 30));
            }
        ),

        floatingActionButton:Consumer<ContentViewModel>(
            builder:(context,contentVM,child) {
              return FloatingActionButton(
                child: Icon(Icons.adb),
                onPressed: () {
                  contentVM.counter +=1;
                },
              );
            }
        )
    );
  }
}
Provider.of(context) VS Consumer 对比:

Provider.of(context)中的数据发生改变时, Provider.of(context)所在的Widget将整个build方法重新构建;
Consumer方法不会执行整个build,只会重新执行Consumer里面的build,此外Consumer方法里面的child也可以优化控件是否重新构建。

所以一般项目开发中用Consumer居多

如上述floatingActionButton的代码可优化为如下代码:

  ///第一种优化方案 Icon 不需要重新构建
        floatingActionButton:Consumer<ContentViewModel>(
            builder:(context,contentVM,child) {
              return FloatingActionButton(
                child: child,
                onPressed: () {
                  contentVM.counter +=1;
                },
              );
            },
          child:Icon(Icons.adb),
        ),

  ///第二种优化方案 FloatingActionButton 也不需要重新构建
      floatingActionButton:Selector<ContentViewModel,ContentViewModel>(
        selector: (context,contentVM) => contentVM,
        //是否重新构建 true 是 false 否
        shouldRebuild: (prev,next) => false,
        builder:(context,contentVM,child) {
          return FloatingActionButton(
            child: child,
            onPressed: () {
              contentVM.counter +=1;
            },
          );
        },
        child:Icon(Icons.adb),
      ),

上述情况仅涉及一个ContentViewModel需要共享,但实际开发中有很多需要共享,比如用户信息,购物车,偏好设置等,假如我们又有一个用户信息的UserViewModel,所以此时main函数creaet方法需要优化为如下代码:

void main() {
  final userInfo = UserInfo("allison", "女", 18);
  runApp(
    //注册多个provider
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => ContentViewModel()),
          ChangeNotifierProvider(create: (context) => UserViewModel(userInfo)),
        ],
      ),
  );

  //注册单个通知
//  runApp(
//      ChangeNotifierProvider(
//        create: (ctx) => ContentViewModel(),
//        child: MyApp(),
//      )
//  );
}

这样我们就可以实现多个provider的注册了,实现数据的共享。
其实像类似Consumer还有一些高级的用法,如:Consumer2Consumer3Consumer4Consumer5Consumer6等一些高级函数,比如:Consumer2可以实现两个viewModel数据拼接的展示,如:
例:想要展示ContentViewModelUserViewModel的数据,可使用Consumer2,代码如下:

// Consumer常规用法
 Consumer<UserViewModel>(
        builder: (context,userVM,child) {
          return Text("当前计数: ${userVM.user.name}", style: TextStyle(fontSize: 30),);
        },
      )

//Consumer2高级用法
Consumer2<UserViewModel,ContentViewModel>(
        builder: (context,userVM,contentVM,child) {
          return Text("名字: ${userVM.user.name} 当前计数:${contentVM.counter}",
            style: TextStyle(fontSize: 30),);
        },
      )

更多高阶用法,期待后续发现中。
DEMO下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值