flutter-managing-state
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseManaging State in Flutter
Flutter中的状态管理
Contents
目录
Core Concepts
核心概念
Flutter's UI is declarative; it is built to reflect the current state of the app (). When state changes, trigger a rebuild of the UI that depends on that state.
UI = f(state)Distinguish between two primary types of state to determine your management strategy:
- Ephemeral State (Local State): State contained neatly within a single widget (e.g., current page in a , current selected tab, animation progress). Manage this using a
PageViewandStatefulWidget.setState() - App State (Shared State): State shared across multiple parts of the app and maintained between user sessions (e.g., user preferences, login info, shopping cart contents). Manage this using advanced approaches like , the
InheritedWidgetpackage, and the MVVM architecture.provider
Flutter的UI是声明式的;它会根据应用的当前状态构建()。当状态发生变化时,会触发依赖该状态的UI重建。
UI = f(state)区分两种主要的状态类型,以确定对应的管理策略:
- 临时状态(局部状态): 仅存在于单个组件内的状态(例如的当前页面、当前选中的标签、动画进度)。使用
PageView和StatefulWidget管理此类状态。setState() - 应用状态(共享状态): 在应用多个部分共享、且在用户会话间保持的状态(例如用户偏好设置、登录信息、购物车内容)。使用、
InheritedWidget包以及MVVM架构等进阶方案管理此类状态。provider
Architecture and Data Flow
架构与数据流
Implement the Model-View-ViewModel (MVVM) design pattern combined with Unidirectional Data Flow (UDF) for scalable app state management.
- Unidirectional Data Flow (UDF): Enforce a strict flow where state flows down from the data layer, through the logic layer, to the UI layer. Events from user interactions flow up from the UI layer, to the logic layer, to the data layer.
- Single Source of Truth (SSOT): Ensure data changes always happen in the data layer (Repositories). The SSOT class must be the only class capable of modifying its respective data.
- Model (Data Layer): Handle low-level tasks like HTTP requests, data caching, and system resources using Repository classes.
- ViewModel (Logic Layer): Manage the UI state. Convert app data from the Model into UI State. Extend and call
ChangeNotifierto trigger UI rebuilds when data changes.notifyListeners() - View (UI Layer): Display the state provided by the ViewModel. Keep views lean; they should contain minimal logic (only routing, animations, or simple UI conditionals).
结合模型-视图-视图模型(MVVM)设计模式和单向数据流(UDF),实现可扩展的应用状态管理。
- 单向数据流(UDF): 强制严格的数据流方向,状态从数据层向下流经逻辑层,最终到达UI层。用户交互产生的事件则从UI层向上流经逻辑层,最终到达数据层。
- 单一数据源(SSOT): 确保数据变更始终在数据层(Repository)中进行。SSOT类必须是唯一能够修改对应数据的类。
- 模型(数据层): 使用Repository类处理底层任务,如HTTP请求、数据缓存和系统资源调用。
- 视图模型(逻辑层): 管理UI状态。将模型中的应用数据转换为UI状态。继承,并在数据变更时调用
ChangeNotifier触发UI重建。notifyListeners() - 视图(UI层): 展示视图模型提供的状态。保持视图轻量化;视图应仅包含最少的逻辑(如路由、动画或简单的UI条件判断)。
Workflow: Selecting a State Management Approach
工作流:选择状态管理方案
Evaluate the scope of the state to determine the correct implementation strategy.
- If managing Ephemeral State (single widget scope):
- Subclass and
StatefulWidget.State - Store mutable state as private fields within the class.
State - Mutate state exclusively inside a callback to mark the widget as dirty and schedule a rebuild.
setState()
- Subclass
- If managing App State (shared across widgets):
- Implement the MVVM pattern.
- Use the package (a wrapper around
provider) to inject state into the widget tree.InheritedWidget - Use to emit state updates.
ChangeNotifier
根据状态的作用范围评估,确定正确的实现策略。
- 若管理临时状态(单个组件范围):
- 继承和
StatefulWidget。State - 在类中以私有字段的形式存储可变状态。
State - 仅在回调中修改状态,标记组件为脏状态并调度重建。
setState()
- 继承
- 若管理应用状态(跨组件共享):
- 实现MVVM模式。
- 使用包(
provider的封装)将状态注入组件树。InheritedWidget - 使用发送状态更新通知。
ChangeNotifier
Workflow: Implementing MVVM with Provider
工作流:使用Provider实现MVVM
Follow this sequential workflow to implement app-level state management using MVVM and .
providerTask Progress:
- 1. Define the Model (Repository).
- 2. Create the ViewModel ().
ChangeNotifier - 3. Inject the ViewModel into the Widget Tree.
- 4. Consume the State in the View.
- 5. Validate the implementation.
遵循以下顺序工作流,使用MVVM和实现应用级状态管理。
provider任务进度:
- 1. 定义模型(Repository)。
- 2. 创建视图模型()。
ChangeNotifier - 3. 将视图模型注入组件树。
- 4. 在视图中消费状态。
- 5. 验证实现效果。
1. Define the Model (Repository)
1. 定义模型(Repository)
Create a repository class to act as the Single Source of Truth (SSOT) for the specific data domain. Handle all external API calls or database queries here.
创建一个Repository类,作为特定数据域的单一数据源(SSOT)。在此处理所有外部API调用或数据库查询。
2. Create the ViewModel (ChangeNotifier
)
ChangeNotifier2. 创建视图模型(ChangeNotifier
)
ChangeNotifierCreate a ViewModel class that extends .
ChangeNotifier- Pass the Repository into the ViewModel via dependency injection.
- Define properties for the UI state (e.g., ,
isLoading,data).errorMessage - Implement methods to handle UI events. Inside these methods, mutate the state and call to trigger UI rebuilds.
notifyListeners()
创建一个继承的视图模型类。
ChangeNotifier- 通过依赖注入将Repository传入视图模型。
- 定义UI状态的属性(例如、
isLoading、data)。errorMessage - 实现处理UI事件的方法。在这些方法中修改状态,并调用触发UI重建。
notifyListeners()
3. Inject the ViewModel into the Widget Tree
3. 将视图模型注入组件树
Use from the package to provide the ViewModel to the widget subtree that requires it. Place the provider as low in the widget tree as possible to avoid polluting the scope.
ChangeNotifierProviderprovider使用包中的,向需要视图模型的组件子树提供该实例。将Provider尽可能放在组件树的下层,避免污染作用域。
providerChangeNotifierProvider4. Consume the State in the View
4. 在视图中消费状态
Access the ViewModel in your or .
StatelessWidgetStatefulWidget- Use to rebuild specific parts of the UI when
Consumer<MyViewModel>is called.notifyListeners() - Use (or
context.read<MyViewModel>()) inside event handlers (likeProvider.of<MyViewModel>(context, listen: false)) to call ViewModel methods without triggering a rebuild of the calling widget.onPressed
在或中访问视图模型。
StatelessWidgetStatefulWidget- 使用,在调用
Consumer<MyViewModel>时重建UI的特定部分。notifyListeners() - 在事件处理程序(如)中使用
onPressed(或context.read<MyViewModel>)调用视图模型的方法,而不会触发调用组件的重建。Provider.of<MyViewModel>(context, listen: false)
5. Validate the implementation
5. 验证实现效果
Run the following feedback loop to ensure data flows correctly:
- Trigger a user action in the View.
- Verify the ViewModel receives the event and calls the Repository.
- Verify the Repository updates the SSOT and returns data.
- Verify the ViewModel updates its state and calls .
notifyListeners() - Verify the View rebuilds with the new state.
Run validator -> review errors -> fix missing calls or incorrect
notifyListeners()scopes.Provider
运行以下反馈循环,确保数据流正确:
- 在视图中触发用户操作。
- 验证视图模型接收事件并调用Repository。
- 验证Repository更新SSOT并返回数据。
- 验证视图模型更新其状态并调用。
notifyListeners() - 验证视图使用新状态重建。
运行验证器 -> 查看错误 -> 修复缺失的调用或不正确的
notifyListeners()作用域。Provider
Examples
示例
Ephemeral State Implementation (setState
)
setState临时状态实现(setState
)
setStateUse this pattern strictly for local, UI-only state.
dart
class EphemeralCounter extends StatefulWidget {
const EphemeralCounter({super.key});
State<EphemeralCounter> createState() => _EphemeralCounterState();
}
class _EphemeralCounterState extends State<EphemeralCounter> {
int _counter = 0; // Local state
void _increment() {
setState(() {
_counter++; // Mutate state and schedule rebuild
});
}
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('Count: $_counter'),
);
}
}此模式严格用于局部、仅UI相关的状态。
dart
class EphemeralCounter extends StatefulWidget {
const EphemeralCounter({super.key});
State<EphemeralCounter> createState() => _EphemeralCounterState();
}
class _EphemeralCounterState extends State<EphemeralCounter> {
int _counter = 0; // Local state
void _increment() {
setState(() {
_counter++; // Mutate state and schedule rebuild
});
}
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('Count: $_counter'),
);
}
}App State Implementation (MVVM + Provider)
应用状态实现(MVVM + Provider)
Use this pattern for shared data and complex business logic.
dart
// 1. Model (Repository)
class CartRepository {
Future<void> saveItemToCart(String item) async {
// Simulate network/database call
await Future.delayed(const Duration(milliseconds: 500));
}
}
// 2. ViewModel (ChangeNotifier)
class CartViewModel extends ChangeNotifier {
final CartRepository repository;
CartViewModel({required this.repository});
final List<String> _items = [];
bool isLoading = false;
String? errorMessage;
List<String> get items => List.unmodifiable(_items);
Future<void> addItem(String item) async {
isLoading = true;
errorMessage = null;
notifyListeners(); // Trigger loading UI
try {
await repository.saveItemToCart(item);
_items.add(item);
} catch (e) {
errorMessage = 'Failed to add item';
} finally {
isLoading = false;
notifyListeners(); // Trigger success/error UI
}
}
}
// 3. Injection & 4. View (UI)
class CartApp extends StatelessWidget {
const CartApp({super.key});
Widget build(BuildContext context) {
// Inject ViewModel
return ChangeNotifierProvider(
create: (_) => CartViewModel(repository: CartRepository()),
child: const CartScreen(),
);
}
}
class CartScreen extends StatelessWidget {
const CartScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<CartViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const CircularProgressIndicator();
}
if (viewModel.errorMessage != null) {
return Text(viewModel.errorMessage!);
}
return ListView.builder(
itemCount: viewModel.items.length,
itemBuilder: (_, index) => Text(viewModel.items[index]),
);
},
),
floatingActionButton: FloatingActionButton(
// Use read() to access methods without listening for rebuilds
onPressed: () => context.read<CartViewModel>().addItem('New Item'),
child: const Icon(Icons.add),
),
);
}
}此模式用于共享数据和复杂业务逻辑。
dart
// 1. Model (Repository)
class CartRepository {
Future<void> saveItemToCart(String item) async {
// Simulate network/database call
await Future.delayed(const Duration(milliseconds: 500));
}
}
// 2. ViewModel (ChangeNotifier)
class CartViewModel extends ChangeNotifier {
final CartRepository repository;
CartViewModel({required this.repository});
final List<String> _items = [];
bool isLoading = false;
String? errorMessage;
List<String> get items => List.unmodifiable(_items);
Future<void> addItem(String item) async {
isLoading = true;
errorMessage = null;
notifyListeners(); // Trigger loading UI
try {
await repository.saveItemToCart(item);
_items.add(item);
} catch (e) {
errorMessage = 'Failed to add item';
} finally {
isLoading = false;
notifyListeners(); // Trigger success/error UI
}
}
}
// 3. Injection & 4. View (UI)
class CartApp extends StatelessWidget {
const CartApp({super.key});
Widget build(BuildContext context) {
// Inject ViewModel
return ChangeNotifierProvider(
create: (_) => CartViewModel(repository: CartRepository()),
child: const CartScreen(),
);
}
}
class CartScreen extends StatelessWidget {
const CartScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<CartViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const CircularProgressIndicator();
}
if (viewModel.errorMessage != null) {
return Text(viewModel.errorMessage!);
}
return ListView.builder(
itemCount: viewModel.items.length,
itemBuilder: (_, index) => Text(viewModel.items[index]),
);
},
),
floatingActionButton: FloatingActionButton(
// Use read() to access methods without listening for rebuilds
onPressed: () => context.read<CartViewModel>().addItem('New Item'),
child: const Icon(Icons.add),
),
);
}
}