news 2026/3/5 10:06:57

从 0 到 1:Flutter 状态管理实战 —— 打造高性能待办清单应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 0 到 1:Flutter 状态管理实战 —— 打造高性能待办清单应用

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

在 Flutter 开发中,状态管理始终是核心且容易让开发者困惑的话题。很多初学者会陷入 “setState 够用吗?”“Provider 和 Bloc 该选哪个?” 的纠结中。本文将以一个高性能待办清单(Todo List)应用为例,从基础的 setState 到进阶的 Provider 状态管理,一步步拆解 Flutter 状态管理的核心逻辑,结合完整的代码实现和深度解析,让你不仅能写出可运行的代码,更能理解背后的设计思想。

一、项目背景与技术选型

待办清单是典型的状态驱动型应用:用户添加、删除、标记完成待办项,界面需要实时响应状态变化。我们将分两个阶段实现:

  1. 基础版:使用 setState 管理局部状态,理解状态更新的基本原理;
  2. 进阶版:使用 Provider + ChangeNotifier 实现全局状态管理,解决跨组件状态共享问题;
  3. 性能优化:通过 Selector 减少不必要的重建,提升应用性能。

技术栈:Flutter 3.16+、Dart 3.2+、Provider 6.1+(主流且轻量的状态管理库)。

二、基础版:setState 实现局部状态管理

核心思路

setState 是 Flutter 最基础的状态管理方式,适用于单一 Widget 内的状态变化。我们先实现一个极简版 Todo 应用,包含 “添加待办”“删除待办”“标记完成” 三个核心功能。

完整代码实现

dart

import '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 State<TodoHomePage> createState() => _TodoHomePageState(); } class _TodoHomePageState extends State<TodoHomePage> { // 待办列表数据源(核心状态) final List<TodoItem> _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(); } }

代码深度解析

  1. 数据模型设计TodoItem类封装了待办项的核心属性,其中isCompleted是可变状态,用于标记是否完成。
  2. 状态管理核心_todoList是待办列表的数据源,属于 Widget 的核心状态。所有修改状态的方法(_addTodo/_deleteTodo/_toggleTodoStatus)都包裹在setState中 ——setState会触发 Widget 的build方法重新执行,从而更新 UI。
  3. 资源管理TextFieldTextEditingController是持有资源的对象,必须在dispose方法中释放,避免内存泄漏。
  4. UI 渲染逻辑ListView.builder是按需渲染列表的核心组件,仅构建当前可视区域的 Item,避免一次性渲染大量数据导致性能问题。

基础版的局限性

虽然setState实现简单,但存在明显短板:

  • 状态只能在当前 Widget 内部共享,若需要在多个页面(如待办详情页)修改状态,会导致组件耦合严重;
  • 每次调用setState会重建整个 Widget 树(当前 Widget 及其子 Widget),即使只有一个待办项状态变化,整个列表都会重新构建,性能损耗随列表长度增加而加剧。

三、进阶版:Provider 实现全局状态管理

核心思路

Provider 是基于 InheritedWidget 实现的状态管理库,核心思想是 “状态上提 + 依赖注入”:

  1. 将共享状态抽离到独立的 ViewModel 类(继承ChangeNotifier);
  2. 通过ChangeNotifierProvider将 ViewModel 注入到 Widget 树中;
  3. 子 Widget 通过Consumer/Provider.of获取 ViewModel,监听状态变化并更新 UI。

步骤 1:引入 Provider 依赖

pubspec.yaml中添加:

yaml

dependencies: flutter: sdk: flutter provider: ^6.1.1

执行flutter pub get安装依赖。

步骤 2:实现 TodoViewModel(状态管理核心)

dart

import '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 List<TodoItem> _todoList = []; // 对外暴露只读的列表(避免外部直接修改) List<TodoItem> 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 层(使用 Provider)

dart

import '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.of<TodoViewModel>(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.of<TodoViewModel>(context, listen: false) .addTodo(content); inputController.clear(); }, child: const Text('添加'), ), ], ), const SizedBox(height: 20), // 待办列表(使用Consumer监听状态变化) Expanded( child: Consumer<TodoViewModel>( 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.of<TodoViewModel>(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.of<TodoViewModel>(context, listen: false) .deleteTodo(todo.id); }, ), ); } }

核心知识点解析

  1. ChangeNotifier:ViewModel 继承此类,通过notifyListeners()通知所有监听者状态变化。相比setState,它实现了 “精准通知”—— 只有依赖该状态的 Widget 会重建。
  2. ChangeNotifierProvider:将 ViewModel 注入到 Widget 树中,使其子 Widget 可以通过Provider.ofConsumer获取。create方法用于创建 ViewModel 实例。
  3. Consumer 的使用Consumer<TodoViewModel>会监听 ViewModel 的状态变化,仅当状态变化时重建其内部的 Widget(本例中是 ListView),而非整个 TodoHomePage。
  4. listen: false:当仅需要调用 ViewModel 的方法(不监听状态)时,设置listen: false,避免不必要的监听和重建。
  5. 状态封装:ViewModel 将状态(_todoList)私有化,对外暴露只读的todoList和修改状态的方法,保证状态修改的可控性(单一数据源原则)。

四、性能优化:使用 Selector 减少重建

即使使用了 Provider,默认的 Consumer 仍会在 ViewModel 的任何状态变化时重建整个 ListView。例如,修改一个待办项的完成状态,整个列表都会重新构建。我们可以通过Selector优化这一问题。

优化后的 TodoItemWidget

dart

class TodoItemWidget extends StatelessWidget { final TodoItem todo; const TodoItemWidget({super.key, required this.todo}); @override Widget build(BuildContext context) { return Selector<TodoViewModel, 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.of<TodoViewModel>(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.of<TodoViewModel>(context, listen: false) .deleteTodo(todo.id); }, ), ); } }

Selector 优化原理

  1. selector 函数:从 ViewModel 中提取当前 Widget 真正依赖的状态(本例中是当前 todo 的isCompleted),而非整个todoList
  2. shouldRebuild 函数:自定义重建条件,仅当提取的状态发生变化时,才重建 Widget。
  3. child 参数:将不随状态变化的 Widget(如删除按钮)作为 child 传入,复用该 Widget,避免不必要的重建。

通过 Selector 优化后,修改一个待办项的完成状态,只有该待办项的 Widget 会重建,其他项和 ListView 本身都不会重建,大幅提升列表性能。

五、总结与扩展

核心收获

  1. 状态管理的本质:状态管理的核心是 “状态与 UI 解耦”,让状态的修改和 UI 的更新分离,提高代码的可维护性。
  2. 技术选型原则
    • 局部简单状态:优先使用setState
    • 跨组件共享状态:使用 Provider(轻量、易上手);
    • 复杂业务逻辑:可考虑 Bloc/Riverpod 等更重型的状态管理方案。
  3. 性能优化关键
    • 减少重建范围(Consumer/Selector);
    • 避免不必要的监听(listen: false);
    • 复用静态 Widget(child 参数)。

扩展方向

  1. 持久化:结合shared_preferences将待办数据保存到本地,重启应用不丢失;
  2. 动画:添加待办项 / 删除待办项的过渡动画,提升用户体验;
  3. 分类:增加待办分类(工作 / 生活 / 学习),扩展状态管理逻辑;
  4. 国际化:适配多语言,结合 Provider 管理语言状态。

本文的代码经过实际运行验证,覆盖了 Flutter 状态管理的核心场景和优化技巧。希望通过这个待办清单的案例,你能真正理解 Flutter 状态管理的底层逻辑,而非单纯 “复制粘贴” 代码。如果有任何问题,欢迎在评论区交流讨论!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/3 14:18:25

基于80亿参数的代码专用模型:Seed-Coder-8B-Base性能实测

基于80亿参数的代码专用模型&#xff1a;Seed-Coder-8B-Base性能实测 在现代软件开发节奏日益加快的今天&#xff0c;开发者对“写得更快、错得更少”的需求从未如此迫切。传统的IDE补全功能早已触达能力天花板——它们能识别变量名和函数签名&#xff0c;却无法理解“我正想实…

作者头像 李华
网站建设 2026/3/4 0:07:59

Git rebase保持Qwen-Image-Edit-2509代码提交历史整洁

Git Rebase&#xff1a;打造清晰、可维护的Qwen-Image-Edit-2509开发流程 在AI模型快速迭代的今天&#xff0c;一个功能分支从创建到上线往往经历数十次提交——“修复拼写”、“临时调试”、“合并冲突”……这些琐碎记录若不加整理&#xff0c;最终会变成代码审查时的一团乱麻…

作者头像 李华
网站建设 2026/3/3 14:18:34

老师讲不清的局部变量作用域,这篇用代码帮你讲明白

摘要 在学习 C 语言时&#xff0c;很多人第一次接触“局部变量”“作用域”时会觉得概念抽象&#xff0c;甚至觉得“记住规则就行”。但在真实开发中&#xff0c;如果对变量的作用范围理解不清楚&#xff0c;轻则程序逻辑混乱&#xff0c;重则直接导致数据错误、难以排查的 Bug…

作者头像 李华
网站建设 2026/3/3 14:18:33

FLUX.1-dev镜像部署常见问题汇总:git下载失败怎么办?

FLUX.1-dev镜像部署常见问题汇总&#xff1a;git下载失败怎么办&#xff1f; 在多模态生成模型快速演进的今天&#xff0c;开发者对高质量文生图系统的部署效率提出了更高要求。以FLUX.1-dev为代表的前沿模型镜像&#xff0c;集成了Flow Transformer架构与大规模训练成果&#…

作者头像 李华
网站建设 2026/3/4 5:23:18

dify平台智能对话延迟高?换vLLM镜像立竿见影

dify平台智能对话延迟高&#xff1f;换vLLM镜像立竿见影 在构建企业级AI应用的今天&#xff0c;一个看似简单的“智能客服”功能背后&#xff0c;往往隐藏着复杂的性能挑战。尤其是当用户期待的是秒级响应、多轮连贯对话时&#xff0c;传统的模型推理架构很容易成为系统瓶颈——…

作者头像 李华
网站建设 2026/3/3 14:18:33

《把脉行业与技术趋势》-47- 通用人工智能的核心关键词:通用、自主、创新:“当机器不再只是执行指令的工具,而是开始提出问题、设定目标并创造新可能时——真正的智能才真正降临。”

在人工智能飞速演进的今天&#xff0c;我们常被各种术语包围&#xff1a;大模型、深度学习、生成式AI…… 但当我们拨开技术迷雾&#xff0c;追问“什么是通用人工智能&#xff08;AGI&#xff09;”的本质特征时&#xff0c;三个关键词脱颖而出&#xff1a;&#x1f511; 通用…

作者头像 李华