鸿蒙Flutter 局部状态与全局状态
概述在 Flutter 开发中状态可以分为局部状态和全局状态。理解这两种状态的区别和适用场景对于构建高效、可维护的应用至关重要。概念对比局部状态局部状态是指只影响单个组件或少数紧密相关组件的状态。这种状态通常使用setState来管理。特点作用域小只在组件内部或少数子组件中使用生命周期短随着组件的创建和销毁而产生和消失修改简单直接使用setState修改不需要共享其他组件不需要访问此状态示例// 展开面板的状态只影响当前组件classExpandablePanelextendsStatefulWidget{constExpandablePanel({super.key});overrideStateExpandablePanelcreateState()_ExpandablePanelState();}class_ExpandablePanelStateextendsStateExpandablePanel{bool _isExpandedfalse;overrideWidgetbuild(BuildContextcontext){returnColumn(children:[ElevatedButton(onPressed:()setState(()_isExpanded!_isExpanded),child:Text(_isExpanded?收起:展开),),AnimatedContainer(duration:constDuration(milliseconds:300),height:_isExpanded?200:0,child:_isExpanded?constText(面板内容):null,),],);}}全局状态全局状态是指需要在整个应用中共享的状态。这种状态通常使用状态管理库来管理。特点作用域大在多个页面或组件中使用生命周期长贯穿整个应用的生命周期修改复杂需要通过状态管理库提供的方法修改需要共享多个组件需要访问和修改此状态示例// 用户登录状态全局共享classUserStateextendsChangeNotifier{User?_user;bool _isLoggedInfalse;User?getuser_user;boolgetisLoggedIn_isLoggedIn;voidlogin(Useruser){_useruser;_isLoggedIntrue;notifyListeners();}voidlogout(){_usernull;_isLoggedInfalse;notifyListeners();}}// 在应用中使用ChangeNotifierProvider(create:(context)UserState(),child:MyApp(),)// 在组件中访问finaluserStateProvider.ofUserState(context);对比表格特性局部状态全局状态作用域单个组件或少数子组件多个页面或整个应用生命周期与组件绑定贯穿应用生命周期管理方式setStateProvider/Riverpod/Bloc 等修改方式直接修改通过状态管理库提供的方法共享性不需要共享需要在多个组件间共享复杂度低中到高性能影响小重建范围可控需要注意优化状态分类实践典型的局部状态UI 交互状态展开/收起、选中/未选中、滑动位置等表单输入状态输入框内容、校验状态等动画状态动画进度、过渡状态等临时状态对话框显示、加载状态等示例表单输入状态classLoginFormextendsStatefulWidget{constLoginForm({super.key});overrideStateLoginFormcreateState()_LoginFormState();}class_LoginFormStateextendsStateLoginForm{final_emailControllerTextEditingController();final_passwordControllerTextEditingController();bool _isSubmittingfalse;Futurevoid_submit()async{setState(()_isSubmittingtrue);// 提交逻辑...setState(()_isSubmittingfalse);}overrideWidgetbuild(BuildContextcontext){returnColumn(children:[TextField(controller:_emailController,decoration:constInputDecoration(labelText:邮箱)),TextField(controller:_passwordController,decoration:constInputDecoration(labelText:密码)),ElevatedButton(onPressed:_isSubmitting?null:_submit,child:_isSubmitting?constCircularProgressIndicator():constText(登录),),],);}}典型的全局状态用户状态登录状态、用户信息等应用配置主题、语言、设置等业务数据购物车、订单、收藏等导航状态路由栈、页面状态等示例购物车状态classCartStateextendsChangeNotifier{ListCartItem_items[];ListCartItemgetitems_items;doublegettotalPrice_items.fold(0,(sum,item)sumitem.price*item.quantity);intgetitemCount_items.length;voidaddItem(CartItemitem){_items.add(item);notifyListeners();}voidremoveItem(Stringid){_items.removeWhere((item)item.idid);notifyListeners();}voidupdateQuantity(Stringid,int quantity){finalitem_items.firstWhere((item)item.idid);item.quantityquantity;notifyListeners();}voidclearCart(){_items[];notifyListeners();}}状态管理方案选择选择指南┌─────────────────────────────────────────────────────────────────┐ │ 状态管理方案选择流程 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ 是否需要跨组件共享 │ ┌───────────────┴───────────────┐ ▼ ▼ 否 是 │ │ ▼ ▼ 使用 setState 是否需要跨页面共享 │ │ └───────────────┬───────────────┘ ▼ 否 │ ▼ 使用状态提升 │ ▼ 是 │ ▼ 选择状态管理库 │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ Provider Riverpod Bloc │ │ │ 简单易用 灵活可测 复杂流程方案对比方案适用场景优点缺点setState单个组件的简单状态简单直接无需额外依赖无法共享状态状态提升少数组件共享状态状态集中易于维护回调链过长Provider中小型应用的全局状态API友好学习曲线平缓依赖上下文Riverpod中大型应用的全局状态灵活可测不依赖上下文学习曲线较陡Bloc复杂状态流管理可预测性强易于测试代码量大状态分层管理在大型应用中通常需要将状态分为多个层次┌─────────────────────────────────────────────────────────────────┐ │ 应用层状态全局 │ │ 用户状态、应用配置、核心业务数据 │ │ 使用 Riverpod/Bloc │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 页面层状态局部全局 │ │ 页面级别的数据和交互状态 │ │ 使用 Provider/状态提升 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 组件层状态局部 │ │ 组件内部的UI交互状态 │ │ 使用 setState │ └─────────────────────────────────────────────────────────────────┘实践示例// 应用层状态 - 用户状态finaluserProviderStateNotifierProviderUserNotifier,User?((ref)UserNotifier());// 页面层状态 - 购物车状态finalcartProviderStateNotifierProviderCartNotifier,ListCartItem((ref)CartNotifier());// 组件层状态 - 展开面板classExpandablePanelextendsStatefulWidget{overrideStateExpandablePanelcreateState()_ExpandablePanelState();}class_ExpandablePanelStateextendsStateExpandablePanel{bool _isExpandedfalse;overrideWidgetbuild(BuildContextcontext){returnColumn(children:[...]);}}状态管理最佳实践实践 1保持状态最小化只在必要时使用状态避免冗余状态。// 不好的示例存储计算结果double _totalPrice0;void_updateTotal(){_totalPrice_items.fold(0,(sum,item)sumitem.price*item.quantity);}// 好的示例使用 getter 计算doubleget_totalPrice_items.fold(0,(sum,item)sumitem.price*item.quantity);实践 2合理划分状态作用域将状态放在最小需要它的组件中。// 不好的示例状态在父组件classParentWidgetextendsStatefulWidget{overrideStateParentWidgetcreateState()_ParentWidgetState();}class_ParentWidgetStateextendsStateParentWidget{int _count0;overrideWidgetbuild(BuildContextcontext){returnColumn(children:[constLargeList(),// 不必要地重建Text(Count:$_count),],);}}// 好的示例状态在子组件classParentWidgetextendsStatelessWidget{overrideWidgetbuild(BuildContextcontext){returnColumn(children:[constLargeList(),// 不会重建Counter(),// 只重建这个组件],);}}实践 3使用不可变状态对于全局状态使用不可变设计可以避免意外修改。immutableclassCartItem{finalStringid;finalStringname;finaldouble price;finalint quantity;constCartItem({requiredthis.id,requiredthis.name,requiredthis.price,this.quantity1,});CartItemcopyWith({String?id,String?name,double?price,int?quantity,}){returnCartItem(id:id??this.id,name:name??this.name,price:price??this.price,quantity:quantity??this.quantity,);}}实践 4封装状态操作将状态操作封装为方法提高代码可读性和可维护性。classCartNotifierextendsStateNotifierListCartItem{CartNotifier():super([]);voidaddItem(CartItemitem){state[...state,item];}voidremoveItem(Stringid){statestate.where((item)item.id!id).toList();}voidupdateQuantity(Stringid,int quantity){if(quantity0){removeItem(id);return;}statestate.map((item)item.idid?item.copyWith(quantity:quantity):item).toList();}}实践 5避免过度使用全局状态不要将所有状态都放在全局状态管理中只共享真正需要共享的状态。// 不好的示例将所有状态都放在全局finalloginFormProviderStateProviderLoginFormState((ref)LoginFormState());// 好的示例登录表单状态是局部的不需要共享classLoginScreenextendsStatefulWidget{overrideStateLoginScreencreateState()_LoginScreenState();}总结理解局部状态和全局状态的区别是构建高效应用的关键局部状态使用setState适用于单个组件的简单状态全局状态使用状态管理库适用于需要跨组件共享的状态合理划分根据状态的作用域和生命周期选择合适的管理方式分层管理在大型应用中将状态分为应用层、页面层和组件层在下一节中我们将探讨状态管理模式的选择策略。