Flutter 从入门到精通(水)

239 阅读1分钟

第6章:状态管理

在 Flutter 中,状态(State) 决定了 Widget 的显示内容。所谓“状态管理”,就是在 UI 和数据之间建立可靠的更新机制,让数据变化时 UI 自动更新。


一、什么是状态?

Flutter 中状态大致分为两种:

类型示例生命周期
临时状态输入框内容、选中项等短暂存在
应用级状态登录信息、主题设置、购物车等长时间存在

二、使用 setState 进行局部状态管理(最基础)

适用于小范围状态更新,如按钮点击、单个组件变更。

示例:

class CounterPage extends StatefulWidget {
  @override
  _CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  void _increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $count'),
        ElevatedButton(onPressed: _increment, child: Text('Add')),
      ],
    );
  }
}

⚠️ 注意事项:

  • 每次 setState() 都会触发 build() 重建整个 Widget 树
  • 不适用于跨组件或大范围共享状态

三、使用 Provider 进行全局或多组件状态共享

Provider 是 Google 推荐的轻量状态管理方案,适合实际项目中使用。

1. 添加依赖

dependencies:
  provider: ^6.0.0

2. 创建状态类

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知监听者刷新 UI
  }
}

3. 注册状态提供者(根节点)

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => Counter(),
      child: MyApp(),
    ),
  );
}

4. 在 Widget 中使用状态

Consumer<Counter>(
  builder: (context, counter, child) {
    return Text('Count: ${counter.count}');
  },
)

ElevatedButton(
  onPressed: () => context.read<Counter>().increment(),
  child: Text('Add'),
)

四、其他常见状态管理方案(了解即可)

方案特点
RiverpodProvider 升级版,类型安全、全局共享
Bloc适合中大型项目,状态集中、事件驱动
GetX上手简单,语法简洁,适合快速开发
Redux借鉴 React Redux,逻辑清晰但冗长复杂

五、常见问题解析

❗ 问题 1:setState 不生效

原因:
  • 使用 setState 的组件是 StatelessWidget
  • setState() 调用后数据未真正变化
  • 被覆盖的变量是局部变量而非状态字段
示例:
// 错误
onPressed: () {
  int count = 0;
  setState(() {
    count++; // 实际是局部变量
  });
}

❗ 问题 2:Provider 更新后 UI 不刷新

检查:
  • 是否调用了 notifyListeners()
  • UI 中是否用 Consumercontext.watch<T>()
  • 是否多层嵌套导致无法监听?
示例解决方案:
Consumer<Counter>(...) ✅ 推荐使用
context.watch<Counter>().count ✅ 实时更新

❗ 问题 3:多个页面共享状态失败

  • 根节点未正确注册 Provider
  • 页面跳转使用了 Navigator.push,但未继承 Provider 上下文
  • 可以使用 MultiProvider 注册多个状态对象