flutter-managing-state by flutter/skills
npx skills add https://github.com/flutter/skills --skill flutter-managing-stateFlutter 的 UI 是声明式的;其构建目的是反映应用的当前状态(UI = f(state))。当状态发生变化时,触发依赖于该状态的 UI 重建。
区分两种主要状态类型以确定管理策略:
PageView 中的当前页面、当前选中的标签页、动画进度)。使用 StatefulWidget 和 setState() 来管理此类状态。InheritedWidget、provider 包以及 MVVM 架构来管理此类状态。实现 设计模式并结合 ,以实现可扩展的应用状态管理。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
ChangeNotifier 并在数据更改时调用 notifyListeners() 以触发 UI 重建。评估状态的范围以确定正确的实现策略。
StatefulWidget 和 State。State 类中。setState() 回调内部修改状态,以将 widget 标记为脏并安排重建。provider 包(InheritedWidget 的包装器)将状态注入 widget 树。ChangeNotifier 发出状态更新。遵循此顺序工作流程,使用 MVVM 和 provider 实现应用级状态管理。
任务进度:
ChangeNotifier)。创建一个仓库类,作为特定数据域的单一数据源(SSOT)。在此处理所有外部 API 调用或数据库查询。
ChangeNotifier)创建一个扩展 ChangeNotifier 的视图模型类。
isLoading、data、errorMessage)。notifyListeners() 以触发 UI 重建。使用 provider 包中的 ChangeNotifierProvider 将视图模型提供给需要它的 widget 子树。将 provider 放置在 widget 树中尽可能低的位置,以避免污染作用域。
在你的 StatelessWidget 或 StatefulWidget 中访问视图模型。
Consumer<MyViewModel> 在调用 notifyListeners() 时重建 UI 的特定部分。onPressed)内部使用 context.read<MyViewModel>()(或 Provider.of<MyViewModel>(context, listen: false))来调用视图模型方法,而不会触发调用 widget 的重建。运行以下反馈循环以确保数据流正确:
notifyListeners()。notifyListeners() 调用或不正确的 Provider 作用域。setState)严格将此模式用于局部的、仅限 UI 的状态。
class EphemeralCounter extends StatefulWidget {
const EphemeralCounter({super.key});
@override
State<EphemeralCounter> createState() => _EphemeralCounterState();
}
class _EphemeralCounterState extends State<EphemeralCounter> {
int _counter = 0; // 局部状态
void _increment() {
setState(() {
_counter++; // 修改状态并安排重建
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('Count: $_counter'),
);
}
}
将此模式用于共享数据和复杂的业务逻辑。
// 1. 模型(仓库)
class CartRepository {
Future<void> saveItemToCart(String item) async {
// 模拟网络/数据库调用
await Future.delayed(const Duration(milliseconds: 500));
}
}
// 2. 视图模型(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(); // 触发加载 UI
try {
await repository.saveItemToCart(item);
_items.add(item);
} catch (e) {
errorMessage = 'Failed to add item';
} finally {
isLoading = false;
notifyListeners(); // 触发成功/错误 UI
}
}
}
// 3. 注入 & 4. 视图(UI)
class CartApp extends StatelessWidget {
const CartApp({super.key});
@override
Widget build(BuildContext context) {
// 注入视图模型
return ChangeNotifierProvider(
create: (_) => CartViewModel(repository: CartRepository()),
child: const CartScreen(),
);
}
}
class CartScreen extends StatelessWidget {
const CartScreen({super.key});
@override
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(
// 使用 read() 来访问方法,而无需监听重建
onPressed: () => context.read<CartViewModel>().addItem('New Item'),
child: const Icon(Icons.add),
),
);
}
}
每周安装量
2.0K
代码仓库
GitHub 星标数
784
首次出现
11 天前
安全审计
已安装于
codex2.0K
gemini-cli2.0K
opencode2.0K
github-copilot2.0K
kimi-cli2.0K
cursor2.0K
Flutter's UI is declarative; it is built to reflect the current state of the app (UI = f(state)). When state changes, trigger a rebuild of the UI that depends on that state.
Distinguish between two primary types of state to determine your management strategy:
PageView, current selected tab, animation progress). Manage this using a StatefulWidget and setState().InheritedWidget, the provider package, and the MVVM architecture.Implement the Model-View-ViewModel (MVVM) design pattern combined with Unidirectional Data Flow (UDF) for scalable app state management.
ChangeNotifier and call notifyListeners() to trigger UI rebuilds when data changes.Evaluate the scope of the state to determine the correct implementation strategy.
StatefulWidget and State.State class.setState() callback to mark the widget as dirty and schedule a rebuild.provider package (a wrapper around InheritedWidget) to inject state into the widget tree.ChangeNotifier to emit state updates.Follow this sequential workflow to implement app-level state management using MVVM and provider.
Task Progress:
ChangeNotifier).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.
ChangeNotifier)Create a ViewModel class that extends ChangeNotifier.
isLoading, data, errorMessage).notifyListeners() to trigger UI rebuilds.Use ChangeNotifierProvider from the provider 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.
Access the ViewModel in your StatelessWidget or StatefulWidget.
Consumer<MyViewModel> to rebuild specific parts of the UI when notifyListeners() is called.context.read<MyViewModel>() (or Provider.of<MyViewModel>(context, listen: false)) inside event handlers (like onPressed) to call ViewModel methods without triggering a rebuild of the calling widget.Run the following feedback loop to ensure data flows correctly:
notifyListeners().notifyListeners() calls or incorrect Provider scopes.setState)Use this pattern strictly for local, UI-only state.
class EphemeralCounter extends StatefulWidget {
const EphemeralCounter({super.key});
@override
State<EphemeralCounter> createState() => _EphemeralCounterState();
}
class _EphemeralCounterState extends State<EphemeralCounter> {
int _counter = 0; // Local state
void _increment() {
setState(() {
_counter++; // Mutate state and schedule rebuild
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('Count: $_counter'),
);
}
}
Use this pattern for shared data and complex business logic.
// 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});
@override
Widget build(BuildContext context) {
// Inject ViewModel
return ChangeNotifierProvider(
create: (_) => CartViewModel(repository: CartRepository()),
child: const CartScreen(),
);
}
}
class CartScreen extends StatelessWidget {
const CartScreen({super.key});
@override
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),
),
);
}
}
Weekly Installs
2.0K
Repository
GitHub Stars
784
First Seen
11 days ago
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex2.0K
gemini-cli2.0K
opencode2.0K
github-copilot2.0K
kimi-cli2.0K
cursor2.0K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装