2026/1/5 15:08:00
网站建设
项目流程
如何用凡科做网站,伪静态 网站如何扫描,河源和平县建设局网站,东莞创意网站设计效果图欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net)#xff0c;一起共建开源鸿蒙跨平台生态。在 Flutter 开发中#xff0c;状态管理始终是核心且容易让开发者困惑的话题。很多初学者会陷入 “setState 够用吗#xff1f;”“Provider 和 …欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net)一起共建开源鸿蒙跨平台生态。在 Flutter 开发中状态管理始终是核心且容易让开发者困惑的话题。很多初学者会陷入 “setState 够用吗”“Provider 和 Bloc 该选哪个” 的纠结中。本文将以一个高性能待办清单Todo List应用为例从基础的 setState 到进阶的 Provider 状态管理一步步拆解 Flutter 状态管理的核心逻辑结合完整的代码实现和深度解析让你不仅能写出可运行的代码更能理解背后的设计思想。一、项目背景与技术选型待办清单是典型的状态驱动型应用用户添加、删除、标记完成待办项界面需要实时响应状态变化。我们将分两个阶段实现基础版使用 setState 管理局部状态理解状态更新的基本原理进阶版使用 Provider ChangeNotifier 实现全局状态管理解决跨组件状态共享问题性能优化通过 Selector 减少不必要的重建提升应用性能。技术栈Flutter 3.16、Dart 3.2、Provider 6.1主流且轻量的状态管理库。二、基础版setState 实现局部状态管理核心思路setState 是 Flutter 最基础的状态管理方式适用于单一 Widget 内的状态变化。我们先实现一个极简版 Todo 应用包含 “添加待办”“删除待办”“标记完成” 三个核心功能。完整代码实现dartimport package:flutter/material.dart; void main() runApp(const TodoApp()); // 应用根组件 class TodoApp extends StatelessWidget { const TodoApp({super.key}); override Widget build(BuildContext context) { return MaterialApp( title: Flutter Todo 基础版, theme: ThemeData(primarySwatch: Colors.blue), home: const TodoHomePage(), ); } } // 待办项数据模型 class TodoItem { final String id; // 唯一标识 final String content; // 待办内容 bool isCompleted; // 是否完成可变状态 TodoItem({ required this.id, required this.content, this.isCompleted false, }); } // 待办主页有状态组件 class TodoHomePage extends StatefulWidget { const TodoHomePage({super.key}); override StateTodoHomePage createState() _TodoHomePageState(); } class _TodoHomePageState extends StateTodoHomePage { // 待办列表数据源核心状态 final ListTodoItem _todoList []; // 输入框控制器 final TextEditingController _inputController TextEditingController(); // 添加待办项方法 void _addTodo() { final content _inputController.text.trim(); if (content.isEmpty) return; // 空内容不添加 setState(() { // 生成唯一ID简化版实际项目可使用uuid库 final id DateTime.now().millisecondsSinceEpoch.toString(); _todoList.add(TodoItem(id: id, content: content)); _inputController.clear(); // 清空输入框 }); } // 删除待办项方法 void _deleteTodo(String id) { setState(() { _todoList.removeWhere((todo) todo.id id); }); } // 切换待办完成状态 void _toggleTodoStatus(String id) { setState(() { final todo _todoList.firstWhere((todo) todo.id id); todo.isCompleted !todo.isCompleted; }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(待办清单setState版)), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ // 输入框 添加按钮 Row( children: [ Expanded( child: TextField( controller: _inputController, decoration: const InputDecoration( hintText: 请输入待办内容, border: OutlineInputBorder(), ), ), ), const SizedBox(width: 10), ElevatedButton( onPressed: _addTodo, child: const Text(添加), ), ], ), const SizedBox(height: 20), // 待办列表 Expanded( child: ListView.builder( itemCount: _todoList.length, itemBuilder: (context, index) { final todo _todoList[index]; return ListTile( leading: Checkbox( value: todo.isCompleted, onChanged: (_) _toggleTodoStatus(todo.id), ), title: Text( todo.content, style: TextStyle( decoration: todo.isCompleted ? TextDecoration.lineThrough : TextDecoration.none, color: todo.isCompleted ? Colors.grey : Colors.black, ), ), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () _deleteTodo(todo.id), ), ); }, ), ), ], ), ), ); } // 释放控制器资源 override void dispose() { _inputController.dispose(); super.dispose(); } }代码深度解析数据模型设计TodoItem类封装了待办项的核心属性其中isCompleted是可变状态用于标记是否完成。状态管理核心_todoList是待办列表的数据源属于 Widget 的核心状态。所有修改状态的方法_addTodo/_deleteTodo/_toggleTodoStatus都包裹在setState中 ——setState会触发 Widget 的build方法重新执行从而更新 UI。资源管理TextField的TextEditingController是持有资源的对象必须在dispose方法中释放避免内存泄漏。UI 渲染逻辑ListView.builder是按需渲染列表的核心组件仅构建当前可视区域的 Item避免一次性渲染大量数据导致性能问题。基础版的局限性虽然setState实现简单但存在明显短板状态只能在当前 Widget 内部共享若需要在多个页面如待办详情页修改状态会导致组件耦合严重每次调用setState会重建整个 Widget 树当前 Widget 及其子 Widget即使只有一个待办项状态变化整个列表都会重新构建性能损耗随列表长度增加而加剧。三、进阶版Provider 实现全局状态管理核心思路Provider 是基于 InheritedWidget 实现的状态管理库核心思想是 “状态上提 依赖注入”将共享状态抽离到独立的 ViewModel 类继承ChangeNotifier通过ChangeNotifierProvider将 ViewModel 注入到 Widget 树中子 Widget 通过Consumer/Provider.of获取 ViewModel监听状态变化并更新 UI。步骤 1引入 Provider 依赖在pubspec.yaml中添加yamldependencies: flutter: sdk: flutter provider: ^6.1.1执行flutter pub get安装依赖。步骤 2实现 TodoViewModel状态管理核心dartimport package:flutter/foundation.dart; class TodoItem { final String id; final String content; bool isCompleted; TodoItem({ required this.id, required this.content, this.isCompleted false, }); } // 待办状态管理类ViewModel class TodoViewModel extends ChangeNotifier { // 私有数据源 final ListTodoItem _todoList []; // 对外暴露只读的列表避免外部直接修改 ListTodoItem get todoList List.unmodifiable(_todoList); // 添加待办 void addTodo(String content) { if (content.isEmpty) return; final id DateTime.now().millisecondsSinceEpoch.toString(); _todoList.add(TodoItem(id: id, content: content)); // 通知监听者Consumer状态变化触发UI更新 notifyListeners(); } // 删除待办 void deleteTodo(String id) { _todoList.removeWhere((todo) todo.id id); notifyListeners(); } // 切换完成状态 void toggleTodoStatus(String id) { final todo _todoList.firstWhere((todo) todo.id id); todo.isCompleted !todo.isCompleted; notifyListeners(); } // 清空所有待办扩展功能 void clearAll() { _todoList.clear(); notifyListeners(); } }步骤 3重构 UI 层使用 Providerdartimport package:flutter/material.dart; import package:provider/provider.dart; void main() runApp( // 将ViewModel注入到根Widget全局可访问 ChangeNotifierProvider( create: (context) TodoViewModel(), child: const TodoApp(), ), ); class TodoApp extends StatelessWidget { const TodoApp({super.key}); override Widget build(BuildContext context) { return MaterialApp( title: Flutter Todo 进阶版, theme: ThemeData(primarySwatch: Colors.blue), home: const TodoHomePage(), ); } } class TodoHomePage extends StatelessWidget { const TodoHomePage({super.key}); override Widget build(BuildContext context) { // 获取输入框控制器StatelessWidget中使用late初始化 late final TextEditingController inputController TextEditingController(); return Scaffold( appBar: AppBar( title: const Text(待办清单Provider版), actions: [ // 清空所有待办按钮 TextButton( onPressed: () { // 获取ViewModel并调用方法 Provider.ofTodoViewModel(context, listen: false).clearAll(); }, child: const Text( 清空, style: TextStyle(color: Colors.white), ), ), ], ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ // 输入框 添加按钮 Row( children: [ Expanded( child: TextField( controller: inputController, decoration: const InputDecoration( hintText: 请输入待办内容, border: OutlineInputBorder(), ), ), ), const SizedBox(width: 10), ElevatedButton( onPressed: () { final content inputController.text.trim(); // listen: false 表示不监听状态变化仅获取ViewModel Provider.ofTodoViewModel(context, listen: false) .addTodo(content); inputController.clear(); }, child: const Text(添加), ), ], ), const SizedBox(height: 20), // 待办列表使用Consumer监听状态变化 Expanded( child: ConsumerTodoViewModel( builder: (context, viewModel, child) { // 仅当todoList变化时重建Listview return ListView.builder( itemCount: viewModel.todoList.length, itemBuilder: (context, index) { final todo viewModel.todoList[index]; return TodoItemWidget(todo: todo); }, ); }, ), ), ], ), ), ); } } // 抽离待办项Widget进一步解耦 class TodoItemWidget extends StatelessWidget { final TodoItem todo; const TodoItemWidget({super.key, required this.todo}); override Widget build(BuildContext context) { return ListTile( leading: Checkbox( value: todo.isCompleted, onChanged: (_) { Provider.ofTodoViewModel(context, listen: false) .toggleTodoStatus(todo.id); }, ), title: Text( todo.content, style: TextStyle( decoration: todo.isCompleted ? TextDecoration.lineThrough : TextDecoration.none, color: todo.isCompleted ? Colors.grey : Colors.black, ), ), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () { Provider.ofTodoViewModel(context, listen: false) .deleteTodo(todo.id); }, ), ); } }核心知识点解析ChangeNotifierViewModel 继承此类通过notifyListeners()通知所有监听者状态变化。相比setState它实现了 “精准通知”—— 只有依赖该状态的 Widget 会重建。ChangeNotifierProvider将 ViewModel 注入到 Widget 树中使其子 Widget 可以通过Provider.of或Consumer获取。create方法用于创建 ViewModel 实例。Consumer 的使用ConsumerTodoViewModel会监听 ViewModel 的状态变化仅当状态变化时重建其内部的 Widget本例中是 ListView而非整个 TodoHomePage。listen: false当仅需要调用 ViewModel 的方法不监听状态时设置listen: false避免不必要的监听和重建。状态封装ViewModel 将状态_todoList私有化对外暴露只读的todoList和修改状态的方法保证状态修改的可控性单一数据源原则。四、性能优化使用 Selector 减少重建即使使用了 Provider默认的 Consumer 仍会在 ViewModel 的任何状态变化时重建整个 ListView。例如修改一个待办项的完成状态整个列表都会重新构建。我们可以通过Selector优化这一问题。优化后的 TodoItemWidgetdartclass TodoItemWidget extends StatelessWidget { final TodoItem todo; const TodoItemWidget({super.key, required this.todo}); override Widget build(BuildContext context) { return SelectorTodoViewModel, bool( // 选择器仅监听当前todo的isCompleted状态 selector: (context, viewModel) { final targetTodo viewModel.todoList.firstWhere((t) t.id todo.id); return targetTodo.isCompleted; }, // 重建条件仅当isCompleted变化时才重建当前Item shouldRebuild: (previous, next) previous ! next, builder: (context, isCompleted, child) { return ListTile( leading: Checkbox( value: isCompleted, onChanged: (_) { Provider.ofTodoViewModel(context, listen: false) .toggleTodoStatus(todo.id); }, ), title: Text( todo.content, style: TextStyle( decoration: isCompleted ? TextDecoration.lineThrough : TextDecoration.none, color: isCompleted ? Colors.grey : Colors.black, ), ), trailing: child, // 静态Widget删除按钮复用不重建 ); }, // 静态子Widget删除按钮不会随状态变化重建 child: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () { Provider.ofTodoViewModel(context, listen: false) .deleteTodo(todo.id); }, ), ); } }Selector 优化原理selector 函数从 ViewModel 中提取当前 Widget 真正依赖的状态本例中是当前 todo 的isCompleted而非整个todoList。shouldRebuild 函数自定义重建条件仅当提取的状态发生变化时才重建 Widget。child 参数将不随状态变化的 Widget如删除按钮作为 child 传入复用该 Widget避免不必要的重建。通过 Selector 优化后修改一个待办项的完成状态只有该待办项的 Widget 会重建其他项和 ListView 本身都不会重建大幅提升列表性能。五、总结与扩展核心收获状态管理的本质状态管理的核心是 “状态与 UI 解耦”让状态的修改和 UI 的更新分离提高代码的可维护性。技术选型原则局部简单状态优先使用setState跨组件共享状态使用 Provider轻量、易上手复杂业务逻辑可考虑 Bloc/Riverpod 等更重型的状态管理方案。性能优化关键减少重建范围Consumer/Selector避免不必要的监听listen: false复用静态 Widgetchild 参数。扩展方向持久化结合shared_preferences将待办数据保存到本地重启应用不丢失动画添加待办项 / 删除待办项的过渡动画提升用户体验分类增加待办分类工作 / 生活 / 学习扩展状态管理逻辑国际化适配多语言结合 Provider 管理语言状态。本文的代码经过实际运行验证覆盖了 Flutter 状态管理的核心场景和优化技巧。希望通过这个待办清单的案例你能真正理解 Flutter 状态管理的底层逻辑而非单纯 “复制粘贴” 代码。如果有任何问题欢迎在评论区交流讨论