iOS- Flutter State状态管理及页面传值

100 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

1、Widget 管理方式有哪几种

  • Widget 自己管理。通常是不会和其他界面产生联动的Widget,比如Widget自身的背景色、动画等。。
  • Widget 父Widget 管理。在进行组件封装时常会这样,将一些属性开放出去交给父Widget控制,便于复用和灵活管理。
  • 混合管理 也就是父Widget和子Widget 都管理状态。 下面看源码例子:

1.1、Widget管理自身的状态

class TapboxA extends StatefulWidget {
  TapboxA({Key? key}) : super(key: key);

  @override
  _TapboxAState createState() => _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
  bool _active = false;//颜色高亮控制
  void _handleTap() {//点击事件
    setState(() {
      改变State状态,刷新UI界面
      _active = !_active;
    });
  }
  Widget build(BuildContext context) {
    return GestureDetector(//添加手势
      onTap: _handleTap,//手势响应事件
      child: Container(//事件管理的子Widget、Container:容器组件
        child: Center(
          child: Text(//文本控件
            _active ? 'Active' : 'Inactive',//根据_active显示文字内容
            style: TextStyle(fontSize: 32.0, color: Colors.white),//文字风格
          ),
        ),
        width: 200.0,//Container 组件宽高
        height: 200.0,
        decoration: BoxDecoration(//Container组件装饰器,用来添加一些特殊效果、比如阴影、特效等
          color: _active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}
  • 点击GestureDetector 包裹的子控件,事件响应_handleTap
  • 事件响应改变数据_active状态
  • 调用setState函数通知Flutter刷新UI 可以看到,整个UI的改变都是在_TapboxAState内部进行的,也就是State的状态都是自身在管理。

1.2、父Widget管理子Widget状态

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;//父Widget创建管理状态数据
  void _handleTapboxChanged(bool newValue) {//事件响应
    setState(() {
      _active = newValue;//赋值、改变状态,通知刷新UI
    });
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxB(
        active: _active,//状态传递
        onChanged: _handleTapboxChanged,//事件传递,
      ),
    );
  }
}
//------------------------- TapboxB ----------------------------------
class TapboxB extends StatelessWidget {
  TapboxB({Key? key, this.active: false, required this.onChanged})
      : super(key: key);//构造函数 required 为必须参数 active 默认为false,如果有传递值则会赋值为传递值。
  final bool active;
  final ValueChanged<bool> onChanged;//一个函数属性,类似于iOS中的block
  void _handleTap() {
    onChanged(!active);//事件回调
  }
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,//事件传递到_handleTap
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',//根据active状态显示文本值
            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 创建接收事件响应函数_handleTapboxChanged以及active。
  • 构建子Widget 将active和_handleTapboxChanged传递进子Widget
  • 子widget通过传递的active显示内容
  • 子widget通过点击事件_handleTap将事件传递回父函数_handleTapboxChanged处理
  • 父widget通过修改active状态,通知UI刷新,重新构建UI,完成界面刷新。 可以看到,父Widget负责状态事件处理,然后将状态数据传递给子Widget,子Widget则负责界面展示,不负责状态管理。

1.3、混合状态管理

class ParentWidgetC extends StatefulWidget {
  @override
  _ParentWidgetCState createState() => _ParentWidgetCState();
}
class _ParentWidgetCState extends State<ParentWidgetC> {
  bool _active = false;
  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxC(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}
//----------------------------- TapboxC ------------------------------
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;
  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,
        ),
      ),
    );
  }
}
  • 两种状态分开管理,子Widget管理自身的高亮状态,通过_highlight属性
  • 父Widget 通过active 管理子widget的内容显示和颜色。

2、页面之前传递数据

上面的例子可以看到页面之间数据的传递方式

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();
}
  • 在需要传递的Widget上创建对应的属性,然后在构造函数上带上需要接收的参数
  • required 带上这个关键字则为必须参数
  • Key? 带上?则为可为空参数