一、状态管理概述

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

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

如何决定使用哪种管理方法?下面是官方给出的一些原则可以帮助你做决定:

  • 如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父 Widget 管理。
  • 如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由 Widget 本身来管理。
  • 如果某一个状态是不同 Widget 共享的则最好由它们共同的父 Widget 管理。
  • 在 Widget 内部管理状态封装性会好一些,而在父 Widget 中管理会比较灵活。

两种组件的继承:

  • StatefulWidget 有状态组件
  • StatelessWidget 无状态组件

一个典型的有状态组件结构如下:

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
  3. void main() {
  4. runApp(new StartApp());
  5. }
  6. class StartApp extends StatefulWidget {
  7. @override
  8. State<StatefulWidget> createState() {
  9. return new _StartAppState();
  10. }
  11. }
  12. class _StartAppState extends State<StartApp> {
  13. @override
  14. Widget build(BuildContext context) {
  15. return MaterialApp(
  16. title: '首页',
  17. theme: new ThemeData(
  18. primarySwatch: Colors.blue,
  19. ),
  20. home: new Scaffold(
  21. appBar: new AppBar(
  22. title: new Text('Welcome to Flutter'),
  23. ),
  24. body: new Center(
  25. child: new Text('Hello World'),
  26. ),
  27. ),
  28. );
  29. }
  30. }

二、Widget 管理自身状态

setState:Flutter 中, 有状态组件通过 setState 设置自身状态, 传入一个匿名函数, 在函数内部的操作(比如赋值、往列表里面添加元素等)将会触发视图更新。

示例一:点击单词重新生成

  1. import 'package:english_words/english_words.dart';
  2. class RandomWords extends StatefulWidget {
  3. @override
  4. createState() => new _RandomWordsState();
  5. }
  6. class _RandomWordsState extends State<RandomWords> {
  7. var wordPair = new WordPair.random();
  8. _handlerTap () {
  9. setState(() {
  10. wordPair = new WordPair.random();
  11. });
  12. }
  13. @override
  14. Widget build(BuildContext context) {
  15. return new GestureDetector(
  16. onTap: _handlerTap,
  17. child: Center(
  18. child: Text(wordPair.asPascalCase),
  19. ),
  20. );
  21. }
  22. }

示例二:点击切换屏幕颜色

  1. class TapboxA extends StatefulWidget {
  2. TapboxA({Key key}) : super(key: key);
  3. @override
  4. createState() => new _TapboxAState();
  5. }
  6. class _TapboxAState extends State<TapboxA> {
  7. bool _active = false;
  8. void _handleTap() {
  9. setState(() {
  10. _active = !_active;
  11. });
  12. }
  13. Widget build(BuildContext context) {
  14. return new GestureDetector(
  15. onTap: _handleTap,
  16. child: new Container(
  17. child: new Center(
  18. child: new Text(
  19. _active ? 'Active' : 'Inactive',
  20. style: new TextStyle(fontSize: 32.0, color: Colors.white),
  21. ),
  22. ),
  23. width: 200.0,
  24. height: 200.0,
  25. decoration: new BoxDecoration(
  26. color: _active ? Colors.lightGreen[700] : Colors.grey[600],
  27. ),
  28. ),
  29. );
  30. }
  31. }

三、父 Widget 管理子 Widget 的状态

先上示例:

  1. // ParentWidget 为 TapboxB 管理状态.
  2. //------------------------ ParentWidget --------------------------------
  3. class ParentWidget extends StatefulWidget {
  4. @override
  5. _ParentWidgetState createState() => new _ParentWidgetState();
  6. }
  7. class _ParentWidgetState extends State<ParentWidget> {
  8. bool _active = false;
  9. void _handleTapboxChanged(bool newValue) {
  10. setState(() {
  11. _active = newValue;
  12. });
  13. }
  14. @override
  15. Widget build(BuildContext context) {
  16. return new Container(
  17. child: new TapboxB(
  18. active: _active,
  19. onChanged: _handleTapboxChanged,
  20. ),
  21. );
  22. }
  23. }
  24. //------------------------- TapboxB ----------------------------------
  25. class TapboxB extends StatelessWidget {
  26. TapboxB({Key key, this.active: false, @required this.onChanged})
  27. : super(key: key);
  28. final bool active;
  29. final ValueChanged<bool> onChanged;
  30. void _handleTap() {
  31. onChanged(!active);
  32. }
  33. Widget build(BuildContext context) {
  34. return new GestureDetector(
  35. onTap: _handleTap,
  36. child: new Container(
  37. child: new Center(
  38. child: new Text(
  39. active ? 'Active' : 'Inactive',
  40. style: new TextStyle(fontSize: 32.0, color: Colors.white),
  41. ),
  42. ),
  43. width: 200.0,
  44. height: 200.0,
  45. decoration: new BoxDecoration(
  46. color: active ? Colors.lightGreen[700] : Colors.grey[600],
  47. ),
  48. ),
  49. );
  50. }
  51. }

父 Widget 将 activeonChanged 传入子 Widget, 分别使用本身的 _active_handleTapboxChanged 作映射, 当点击子 Widget 时, 触发 _handleTapboxChanged 并改变 _active 的值。

四、混合状态管理

_ParentWidgetStateC 类:

  • 管理_active 状态。
  • 实现 _handleTapboxChanged() ,当盒子被点击时调用。
  • 当点击盒子并且_active 状态改变时调用 setState()更新 UI。

_TapboxCState 对象:

  • 管理_highlight 状态。
  • GestureDetector 监听所有 tap 事件。当用户点下时,它添加高亮(深绿色边框);当用户释放时,会移除高亮。
  • 当按下、抬起、或者取消点击时更新_highlight 状态,调用 setState()更新 UI。
  • 当点击时,将状态的改变传递给父组件。
  1. //---------------------------- ParentWidget ----------------------------
  2. class ParentWidgetC extends StatefulWidget {
  3. @override
  4. _ParentWidgetCState createState() => new _ParentWidgetCState();
  5. }
  6. class _ParentWidgetCState extends State<ParentWidgetC> {
  7. bool _active = false;
  8. // 处理子Widget的点击事件
  9. void _handleTapboxChanged(bool newValue) {
  10. setState(() {
  11. _active = newValue;
  12. });
  13. }
  14. @override
  15. Widget build(BuildContext context) {
  16. return new Container(
  17. child: new TapboxC(
  18. active: _active,
  19. onChanged: _handleTapboxChanged,
  20. ),
  21. );
  22. }
  23. }
  24. //----------------------------- TapboxC ------------------------------
  25. class TapboxC extends StatefulWidget {
  26. // 接收父Widget传入的状态
  27. TapboxC({Key key, this.active: false, @required this.onChanged})
  28. : super(key: key);
  29. final bool active;
  30. final ValueChanged<bool> onChanged;
  31. @override
  32. _TapboxCState createState() => new _TapboxCState();
  33. }
  34. class _TapboxCState extends State<TapboxC> {
  35. bool _highlight = false;
  36. // 自己处理本Widget的按下、抬起事件
  37. void _handleTapDown(TapDownDetails details) {
  38. setState(() {
  39. this._highlight = true;
  40. });
  41. }
  42. void _handleTapUp(TapUpDetails details) {
  43. setState(() {
  44. _highlight = false;
  45. });
  46. }
  47. void _handleTapCancel() {
  48. setState(() {
  49. _highlight = false;
  50. });
  51. }
  52. // 交由父 Widget 处理点击事件
  53. void _handleTap() {
  54. // 使用Widget中的状态(此处为父Widget传入的状态)时, 不能用this, 而是直接用widget
  55. widget.onChanged(!widget.active);
  56. }
  57. @override
  58. Widget build(BuildContext context) {
  59. // 在按下时添加绿色边框,当抬起时,取消高亮
  60. return new GestureDetector(
  61. onTapDown: _handleTapDown, // 处理按下事件
  62. onTapUp: _handleTapUp, // 处理抬起事件
  63. onTap: _handleTap,
  64. onTapCancel: _handleTapCancel,
  65. child: new Container(
  66. child: new Center(
  67. child: new Text(widget.active ? 'Active' : 'Inactive',
  68. style: new TextStyle(fontSize: 32.0, color: Colors.white)),
  69. ),
  70. width: 200.0,
  71. height: 200.0,
  72. decoration: new BoxDecoration(
  73. color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
  74. border: _highlight
  75. ? new Border.all(
  76. color: Colors.teal[700],
  77. width: 10.0,
  78. ) : null,
  79. ),
  80. ),
  81. );
  82. }
  83. }

五、全局状态管理

以下部分选读, 可能在项目中根本不需要用到,参见: