前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter状态管理

Flutter状态管理

作者头像
flyou
发布2020-06-16 15:24:44
1.6K0
发布2020-06-16 15:24:44
举报
文章被收录于专栏:flutter开发者flutter开发者

在前面的文章中我们学习了Flutter中事件传递的方法,让我们可以在数据流向简单的业务场景中使用InheritedWidget、Notification 或者 EventBus。

但是随着业务逻辑的复杂,面对不同组件与不同页面之间的数据传递如果还使用前面讲到数据传递的方法就会显得异常繁琐,更会让页面的嵌套增多和数据流向的混乱,所以这个时候我们就需要有一种方案来管理我们需要跨界面传递的数据,于是便有了“状态管理”这个概念。

在前端开发中我们都会接触redux ,借助于redux 我们可以很轻松地完成多界面数据维护和获取,在Flutter中也有很多状态管理的第三方库,如Provider、Scoped Mode、flutter_redux、flutter_mobx 、BLoC、fish_redux等。

Provider作为官方推荐的状态管理工具具有使用简单和管理方便的特点,今天我们就先来看下Provider如何使用。

Provider实现原理

在前面的文章中我们学习过InheritedWidget的用法,通过对InheritedWidget的封装,使得Provider允许在 Widget 树中更加灵活地处理和传递数据。

Provider借助于ChangeNotifier实现发布者-订阅者模式。

代码语言:javascript
复制
class ChangeNotifier implements Listenable {
  List listeners=[];
  @override
  void addListener(VoidCallback listener) {
     //添加监听器
     listeners.add(listener);
  }
@override
void removeListener(VoidCallback listener) {
//移除监听器
    listeners.remove(listener);
  }
void notifyListeners() {
//通知所有监听器,触发监听器回调
    listeners.forEach((item)=>item());
  }

  ...
}

具体的细节我们不再具体去探讨,今天就来看看如何使用。

首先,我们假定这样一个场景,第一个界面显示用户的昵称,然后我们在第二个界面修改昵称再返回观察第一个界面的显示情况。

首先我们建立一个用户信息操作类UserInfoModel使它继承ChangeNotifier

代码语言:javascript
复制
class UserInfoModel with ChangeNotifier {
  String _nickName = "userName";
  // 读方法
Stringget nickName => _nickName;
// 写方法
void updateNickName(String nickName) {
    _nickName=nickName;
    notifyListeners();// 通知听众刷新
  }
}
数据更新

可以看到我们在UserInfoModel中定义了_nickName属性并设置相关获取与设置属性的方法,在设置属性方法中我们通过notifyListeners方法告知数据刷新。

因为Provider 是InheritedWidget实现的,所以数据也是有流向的,所以我们需要把ChangeNotifierProvider.value放在两个界面上面的位置,这样我们一旦更新一个页面的数据另外一个页面就也可以获取到。

首先,我们定一个入口

代码语言:javascript
复制
void main() {
  runApp(new MaterialApp(
    home:   MyApp(),
  ));
}

然后定义入口Widget

代码语言:javascript
复制
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
        value: UserInfoModel(),
        child: MaterialApp(
          home: FirstPage(),
        )
    );
  }
}

第一个界面我们定义一个按钮和一个Text用来显示第二个界面更新的数据

我们使用context.watch()方法来获取到对象,并监听

代码语言:javascript
复制
class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderTitle"),
      ),
      body: Center(
        child: Column(
          children: [
            Text( context.watch<UserInfoModel>().nickName),
            RaisedButton(
              child: Text("去设置界面"),
              onPressed: () {
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return SecondPage();
                }));
              },
            )
          ],
        ),
      ),
    );
  }
}

第二个界面我们定义一个输入框和一个按钮,点击按钮就把输入框的值设置给Provider

我们使用 Provider.of(context)方法来获取监听对象并进行修改操作。

代码语言:javascript
复制
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    TextEditingController _unameController = TextEditingController();
  var userModel=  Provider.of<UserInfoModel>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderTitle"),
      ),
      body: Column(
        children: [
          TextField(
            autofocus: true,
            controller: _unameController,
            decoration: InputDecoration(
                labelText: "用户名",
                hintText: "用户名或邮箱",
                prefixIcon: Icon(Icons.person)),
          ),
          RaisedButton(
            onPressed: () {
              userModel.updateNickName(_unameController.text);

            },
            child: Text("设置"),
          )
        ],
      ),
    );
  }
}
同时管理多个数据

在上面我们介绍了如何通过Provider来管理用户名数据,那么如果涉及多个数据我们该如何来管理呢?

通常情况下我们可以把多个数据封装成一个完整的数据来进行操作,这种方法在数据间相互关联性比较接近的情况下是可以实现的,但是如何遇到数据关系不大的情况下还采用这种方法的话就会造成界面Widget不必要的重绘。

当然,Provider也为我们提供了解决方法,MultiProvider可以让我们同时管理多个数据。

还是以上面的例子来进行说明,我们在前面用户名的基础上又增加了一个“家庭地址”,在第一个界面新增一个Text用来显示家庭地址,在第二个界面新增一个输入框用来输入家庭地址。

首先我们定义一个用来管理地址的Model

代码语言:javascript
复制
class UserLocationModel with ChangeNotifier {
  String _address = "address";

  // 读方法
Stringget address => _address;

// 写方法
void setAddress(String address) {
    _address = address;
    notifyListeners(); // 通知听众刷新
  }
}

然后我们使用MultiProvider来管理多个Model

代码语言:javascript
复制
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 通过 Provider 组件封装数据资源
return MultiProvider(providers: [
      ChangeNotifierProvider.value(value: UserInfoModel()),
      ChangeNotifierProvider.value(value: UserLocationModel())
    ], child: MaterialApp(home: FirstPage()));
  }
}

然后在第一个界面接收并显示数据

代码语言:javascript
复制
class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderTitle"),
      ),
      body: Center(
        child: Column(
          children: [
            Text("用户名:${context.watch<UserInfoModel>().nickName}"),
            Text("家庭地址:${context.watch<UserLocationModel>().address}"),
            RaisedButton(
              child: Text("去设置界面"),
              onPressed: () {
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return SecondPage();
                }));
              },
            )
          ],
        ),
      ),
    );
  }
}

在第二个界面设置数据

代码语言:javascript
复制
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    TextEditingController _unameController = TextEditingController();
    TextEditingController _homeController = TextEditingController();
    var userModel = Provider.of<UserInfoModel>(context);
    var homeModel = Provider.of<UserLocationModel>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderTitle"),
      ),
      body: Column(
        children: [
          TextField(
            autofocus: true,
            controller: _unameController,
            decoration: InputDecoration(
                labelText: "用户名",
                hintText: "用户名或邮箱",
                prefixIcon: Icon(Icons.person)),
          ),

          RaisedButton(
            onPressed: () {
                userModel.updateNickName(_unameController.text);
            },
            child: Text("设置用户名"),
          ),
          TextField(
            autofocus: true,
            controller: _homeController,
            decoration: InputDecoration(
                labelText: "家庭地址",
                hintText: "请输入家庭地址",
                prefixIcon: Icon(Icons.person)),
          ),
          RaisedButton(
            onPressed: () {
            homeModel.setAddress(_homeController.text);

            },
            child: Text("设置家庭地址"),
          ),

        ],
      ),
    );
  }
}

当然我们也可以使用Consumer2方法来获取多个数据的传递,这样就不需要再创建UserInfoModel和UserLocationModel了。

代码语言:javascript
复制
class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderTitle"),
      ),
      body: Center(
          child: Consumer2<UserInfoModel, UserLocationModel>(
//builder 函数以参数的形式提供了数据资源
              builder: (context, UserInfoModel userInfoModel,
                      UserLocationModel userLocationModel, _) =>
                  Column(
                    children: [
                      Text("用户名:${userInfoModel.nickName}"),
                      Text("家庭地址:${userLocationModel.address}"),
                      RaisedButton(
                        child: Text("去设置界面"),
                        onPressed: () {
                          Navigator.push(context,
                              MaterialPageRoute(builder: (context) {
                            return SecondPage();
                          }));
                        },
                      )
                    ],
                  ))),
    );
  }
}

小结

  • Provider是对InheritedWidget的封装方便我们在多个界面间传递数据
  • Provider支持同时管理多个数据的状态
  • 可以借助与Consumer-Consumer6方法来管理多个数据状态
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 flutter开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Provider实现原理
  • 在前面的文章中我们学习过InheritedWidget的用法,通过对InheritedWidget的封装,使得Provider允许在 Widget 树中更加灵活地处理和传递数据。
    • 数据更新
      • 同时管理多个数据
      • 小结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档