news 2026/4/3 6:33:30

从 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/11 20:42:43

Windsurf开发工具集成FLUX.1-dev:打造一体化AI创作平台

Windsurf集成FLUX.1-dev&#xff1a;构建下一代AI创作生态 在创意设计领域&#xff0c;我们正经历一场由生成式AI驱动的范式变革。过去&#xff0c;设计师需要在多个工具间切换——用Stable Diffusion生成初稿&#xff0c;靠BLIP描述图像内容&#xff0c;再手动调整细节。这种割…

作者头像 李华
网站建设 2026/4/1 5:34:35

Windsurf开发工具对接FLUX.1-dev API,构建可视化生成界面

Windsurf对接FLUX.1-dev API&#xff1a;打造低代码文生图可视化平台 在AI生成内容&#xff08;AIGC&#xff09;浪潮席卷创意产业的今天&#xff0c;一个设计师是否还需要精通Python、熟悉REST API才能使用最先进的图像生成模型&#xff1f;答案正变得越来越明确&#xff1a;不…

作者头像 李华
网站建设 2026/4/3 2:44:37

2025年工业路由器选型推荐:销量冠军蒲公英R300系列

在工业4.0与数字化转型的宏大叙事中&#xff0c;稳定可靠的网络连接是支撑智能制造、远程运维与数据智能的无声基石。然而&#xff0c;工厂车间、户外能源站、偏远矿山等复杂工业环境&#xff0c;对网络设备提出了极端严苛的要求&#xff1a;不仅要抵御粉尘、潮湿、高低温的物理…

作者头像 李华
网站建设 2026/3/21 15:54:54

CFCA谢宗晓介绍“基于一图四库的网络安全产品服务”

12月4日下午&#xff0c;“2025企业家博鳌论坛-数字金融安全发展大会暨数字金融联合宣传年年度活动”在海南博鳌举行&#xff0c;大会由新华网主办&#xff0c;以“数聚新势 智汇金融”为主题&#xff0c;围绕金融科技突破、数字安全、数字金融创新、金融高质量发展等核心命题展…

作者头像 李华
网站建设 2026/4/2 5:30:27

9 个继续教育论文工具,AI 降重查重率推荐

9 个继续教育论文工具&#xff0c;AI 降重查重率推荐 论文写作的困局&#xff1a;时间、重复率与效率的三重挑战 对于继续教育领域的学生来说&#xff0c;撰写毕业论文往往是一场艰难的战役。从选题到文献综述&#xff0c;从大纲构建到内容撰写&#xff0c;每一个环节都充满了挑…

作者头像 李华
网站建设 2026/3/30 2:13:22

基于Matlab的永磁同步电机磁场定向控制仿真模型探索

基于matlab的永磁同步电机(PMSM)&#xff0c;磁场定向控制仿真模型。 永磁同步电机&#xff08;PMSM&#xff09;是一个非线性系统&#xff0c;具有多变量、强耦合的特点。永磁同步电机&#xff08;PMSM&#xff09;可是个很有意思的东西&#xff0c;它属于非线性系统&#xff…

作者头像 李华