Flutter Widget:State 状态管理

响应式的编程框架永恒的主题------"状态(State)管理"

无论是在 React/Vue/Flutter 中讨论的问题和解决的思想都是一致的。

StatefulWidget的状态应该被谁管理?Widget本身?父 Widget ?都会?还是另一个对象?

下面是官方给出的一些原则:

  • 如果状态是用户数据如复选框的选中状态、滑块的位置,则最好由父 Widget 管理。
  • 如果状态是有关界面外观效果如颜色、动画,最好由 Widget 本身来管理。
  • 如果状态是不同 Widget 共享的,最好由它们共同的父 Widget 管理。

以下是管理状态的最常见的方法:

  • Widget 管理自身的状态。
  • Widget 管理子 Widget 状态。
  • 混合管理(父 Widget 和子 Widget 都管理状态)。

接下来,我们将通过例子说明管理状态的不同方式:创建一个盒子,当点击它时,盒子背景会在绿色与灰色之间切换。状态 _active确定颜色:绿色为true ,灰色为false。如图所示:


Widget管理自身状态

我们实现一个TapboxA,对应的状态类_TapboxAState :

Dart 复制代码
class TapboxA extends StatefulWidget {
  TapboxA({Key? key}) : super(key: key);
  @override
  _TapboxAState createState() => _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
  bool _active = false;//确定盒子的当前颜色的布尔值。
  void _handleTap() {//更新_active,并调用setState()更新UI。
    setState(() { _active = !_active;});
  }
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            _active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ), ),
        width: 200.0,height: 200.0,
        decoration: BoxDecoration(color: _active ? Colors.lightGreen[700] : Colors.grey[600],),
      ),);
  }
}

Widget管理子widget状态

父Widget管理状态并告诉其子Widget何时更新。👇示例中:TapboxB类继承StatelessWidget不管理任何状态。状态由父组件管理,因此它的父组件为StatefulWidget

Dart 复制代码
class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;//管理_active状态
  void _handleTapboxChanged(bool newValue) {
    setState(() {_active = newValue;});//当状态改变时,调用setState()更新UI。
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxB( active: _active,onChanged: _handleTapboxChanged,),);
  }
}
class TapboxB extends StatelessWidget {
  TapboxB({Key? key, this.active: false, required this.onChanged}): super(key: key);
  final bool active;
  final ValueChanged<bool> onChanged;
  void _handleTap() {onChanged(!active);}
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,//回调父级类的函数
      child: Container(
        child: Center(
          child: Text(active ? 'Active' : 'Inactive',style: TextStyle(fontSize: 32.0, color: Colors.white),),),
        width: 200.0,height: 200.0,
        decoration: BoxDecoration(color: active ? Colors.lightGreen[700] : Colors.grey[600],),),
    );
  }
}

混合状态管理

对于一些组件来说,自身管理一些外观状态,而父组件管理一些其他外部状态。

在例子中,增加一个子widget点击是有高亮效果状态_highlight。当按下、抬起、或者取消点击时更新_highlight状态。

Dart 复制代码
class ParentWidgetC extends StatefulWidget {
  @override
  _ParentWidgetCState createState() => _ParentWidgetCState();
}
class _ParentWidgetCState extends State<ParentWidgetC> {
  bool _active = false;//点击盒子_active状态改变,调用setState()更新UI。
  void _handleTapboxChanged(bool newValue) {
    setState(() { _active = newValue;});}
  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxC(active: _active,onChanged: _handleTapboxChanged,),
    );}
}
class TapboxC extends StatefulWidget {
  TapboxC({Key? key, this.active: false, required this.onChanged}): super(key: key);
  final bool active;
  final ValueChanged<bool> onChanged;
  @override
  _TapboxCState createState() => _TapboxCState();
}
class _TapboxCState extends State<TapboxC> {
  bool _highlight = false;//管理_highlight 状态
  void _handleTapDown(TapDownDetails details) {
    setState(() {_highlight = true;});
  }
  void _handleTapUp(TapUpDetails details) {
    setState(() {_highlight = false;});
  }
  void _handleTapCancel() {
    setState(() { _highlight = false;});
  }
  void _handleTap() {
    widget.onChanged(!widget.active);//改变父级的值
  }
  @override
  Widget build(BuildContext context) {
    return GestureDetector(//手势:按下时添加绿色边框,当抬起时,取消高亮  
      onTapDown: _handleTapDown, // 处理按下事件
      onTapUp: _handleTapUp, // 处理抬起事件
      onTap: _handleTap,//点击
      onTapCancel: _handleTapCancel,//处理取消点击
      child: Container(
        child: Center(
          child: Text(widget.active ? 'Active' : 'Inactive',style: TextStyle(fontSize: 32.0, color: Colors.white),),),
        width: 200.0,height: 200.0,
        decoration: BoxDecoration(
          color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight? Border.all(color: Colors.teal[700],width: 10.0,): null,),),);
  }
}

在这个案例中,我们复习了一个知识点:

Flutter Widget:StatefulWidget&StatelessWidget&State

一个 StatefulWidget 类可以有一个或多个State实例,而State实例只关联一个widget 实例。


全局状态管理

当应用中需要一些跨组件(包括跨路由)的状态需要同步时,上面介绍的方法便很难胜任了。

比如,我们有一个设置页,里面可以设置应用的语言,我们为了让设置实时生效,我们期望在语言状态发生改变时,App中依赖应用语言的组件能够重新 build 一下,但这些依赖应用语言的组件和设置页并不在一起,所以这种情况用上面的方法很难管理。

这时,正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。

目前主要有两种办法:

  1. 实现一个全局的事件总线。将语言状态改变对应为一个事件,然后在APP中依赖应用语言的组件的initState 方法中订阅语言改变的事件。当用户在设置页切换语言后,我们发布语言改变事件,而订阅了此事件的组件就会收到通知,收到通知后调用setState(...)方法重新build一下自身即可。
  2. 使用一些专门用于状态管理的包,如 Provider、Redux。
相关推荐
Moment5 分钟前
面试官:一个接口使用postman这些测试很快,但是页面加载很慢怎么回事 😤😤😤
前端·后端·面试
诗书画唱9 分钟前
【前端面试题】JavaScript 核心知识点解析(第二十二题到第六十一题)
开发语言·前端·javascript
excel15 分钟前
前端必备:从能力检测到 UA-CH,浏览器客户端检测的完整指南
前端
前端小巷子22 分钟前
Vue 3全面提速剖析
前端·vue.js·面试
悟空聊架构29 分钟前
我的网站被攻击了,被干掉了 120G 流量,还在持续攻击中...
java·前端·架构
CodeSheep30 分钟前
国内 IT 公司时薪排行榜。
前端·后端·程序员
尖椒土豆sss34 分钟前
踩坑vue项目中使用 iframe 嵌套子系统无法登录,不报错问题!
前端·vue.js
遗悲风35 分钟前
html二次作业
前端·html
江城开朗的豌豆38 分钟前
React输入框优化:如何精准获取用户输入完成后的最终值?
前端·javascript·全栈
CF14年老兵38 分钟前
从卡顿到飞驰:我是如何用WebAssembly引爆React性能的
前端·react.js·trae