flutter-state-management by flutter/skills
npx skills add https://github.com/flutter/skills --skill flutter-state-management在 Flutter 应用中实现健壮的状态管理和架构模式,使用单向数据流(UDF)和模型-视图-视图模型(MVVM)设计模式。评估状态复杂性以区分瞬时(本地)状态和应用(共享)状态,并应用适当的机制(setState、ChangeNotifier 或 provider 包)。确保 UI 保持为不可变状态的纯函数,并且数据层作为单一数据源(SSOT)。
评估功能的数据需求,以确定适当的状态管理方法。使用以下决策树:
PageView 中的当前页面、动画进度、本地表单输入)
StatefulWidget + setState)。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
ChangeNotifier + provider)。暂停并询问用户: 如果根据提供的需求,状态的范围(瞬时状态与应用范围状态)不明确,请暂停并询问用户,以澄清数据的预期范围和生命周期。
对于本地 UI 状态,使用 StatefulWidget。确保在修改内部状态时立即调用 setState(),以将小部件标记为脏并安排重建。
class LocalStateWidget extends StatefulWidget {
const LocalStateWidget({super.key});
@override
State<LocalStateWidget> createState() => _LocalStateWidgetState();
}
class _LocalStateWidgetState extends State<LocalStateWidget> {
bool _isToggled = false;
void _handleToggle() {
// Validate-and-Fix: 确保 setState 包装了状态变更。
setState(() {
_isToggled = !_isToggled;
});
}
@override
Widget build(BuildContext context) {
return Switch(
value: _isToggled,
onChanged: (value) => _handleToggle(),
);
}
}
对于共享状态,实现 MVVM 模式并强制执行单向数据流(UDF)。
A. 创建模型(数据层 / SSOT) 在 Repository 类中处理底层任务(HTTP、缓存)。
class UserRepository {
Future<User> fetchUser(String id) async {
// 获取用户数据的实现
}
}
B. 创建视图模型(逻辑层) 继承 ChangeNotifier。视图模型将应用数据转换为 UI 状态,并暴露供视图调用的命令(方法)。
class UserViewModel extends ChangeNotifier {
UserViewModel({required this.userRepository});
final UserRepository userRepository;
User? _user;
User? get user => _user;
bool _isLoading = false;
bool get isLoading => _isLoading;
String? _errorMessage;
String? get errorMessage => _errorMessage;
// UI 调用的命令
Future<void> loadUser(String id) async {
_isLoading = true;
_errorMessage = null;
notifyListeners(); // 触发加载 UI
try {
_user = await userRepository.fetchUser(id);
} catch (e) {
_errorMessage = e.toString();
} finally {
_isLoading = false;
notifyListeners(); // 触发成功/错误 UI
}
}
}
使用 provider 包将视图模型注入到需要访问它的部件上方的小部件树中。
void main() {
runApp(
MultiProvider(
providers: [
Provider(create: (_) => UserRepository()),
ChangeNotifierProvider(
create: (context) => UserViewModel(
userRepository: context.read<UserRepository>(),
),
),
],
child: const MyApp(),
),
);
}
将 UI 构建为视图模型状态的函数。使用 Consumer 仅重建依赖于该状态的特定 UI 部分。
class UserProfileView extends StatelessWidget {
const UserProfileView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<UserViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (viewModel.errorMessage != null) {
return Center(child: Text('Error: ${viewModel.errorMessage}'));
}
if (viewModel.user != null) {
return Center(child: Text('Hello, ${viewModel.user!.name}'));
}
return const Center(child: Text('No user loaded.'));
},
),
floatingActionButton: FloatingActionButton(
// 在 build 方法之外调用命令时使用 listen: false
onPressed: () => context.read<UserViewModel>().loadUser('123'),
child: const Icon(Icons.refresh),
),
);
}
}
验证与修复: 确保 Consumer 尽可能放置在部件树的深层,以防止大型部件子树的不必要重建。
StatelessWidget 和 StatefulWidget 类必须仅包含 UI、布局和路由逻辑。所有数据转换和业务逻辑必须位于视图模型中。build 方法的根节点使用 Provider.of<T>(context) 且 listen: true。使用 Consumer<T> 或 Selector<T, R> 来限定重建范围。onPressed)调用视图模型方法时,必须使用 context.read<T>() 或 Provider.of<T>(context, listen: false)。notifyListeners()。每周安装量
1.1K
代码仓库
GitHub 星标
843
首次出现
2026年3月4日
安全审计
安装于
codex1.1K
cursor1.1K
opencode1.1K
github-copilot1.0K
gemini-cli1.0K
amp1.0K
Implements robust state management and architectural patterns in Flutter applications using Unidirectional Data Flow (UDF) and the Model-View-ViewModel (MVVM) design pattern. Evaluates state complexity to differentiate between ephemeral (local) state and app (shared) state, applying the appropriate mechanisms (setState, ChangeNotifier, or the provider package). Ensures that the UI remains a pure function of immutable state and that the data layer acts as the Single Source of Truth (SSOT).
Evaluate the data requirements of the feature to determine the appropriate state management approach. Use the following decision tree:
PageView, animation progress, local form input)
StatefulWidget + setState).ChangeNotifier + provider).STOP AND ASK THE USER: If the scope of the state (ephemeral vs. app-wide) is ambiguous based on the provided requirements, pause and ask the user to clarify the intended scope and lifecycle of the data.
For local UI state, use a StatefulWidget. Ensure that setState() is called immediately when the internal state is modified to mark the widget as dirty and schedule a rebuild.
class LocalStateWidget extends StatefulWidget {
const LocalStateWidget({super.key});
@override
State<LocalStateWidget> createState() => _LocalStateWidgetState();
}
class _LocalStateWidgetState extends State<LocalStateWidget> {
bool _isToggled = false;
void _handleToggle() {
// Validate-and-Fix: Ensure setState wraps the mutation.
setState(() {
_isToggled = !_isToggled;
});
}
@override
Widget build(BuildContext context) {
return Switch(
value: _isToggled,
onChanged: (value) => _handleToggle(),
);
}
}
For shared state, implement the MVVM pattern enforcing Unidirectional Data Flow (UDF).
A. Create the Model (Data Layer / SSOT) Handle low-level tasks (HTTP, caching) in a Repository class.
class UserRepository {
Future<User> fetchUser(String id) async {
// Implementation for fetching user data
}
}
B. Create the ViewModel (Logic Layer) Extend ChangeNotifier. The ViewModel converts app data into UI state and exposes commands (methods) for the View to invoke.
class UserViewModel extends ChangeNotifier {
UserViewModel({required this.userRepository});
final UserRepository userRepository;
User? _user;
User? get user => _user;
bool _isLoading = false;
bool get isLoading => _isLoading;
String? _errorMessage;
String? get errorMessage => _errorMessage;
// Command invoked by the UI
Future<void> loadUser(String id) async {
_isLoading = true;
_errorMessage = null;
notifyListeners(); // Trigger loading UI
try {
_user = await userRepository.fetchUser(id);
} catch (e) {
_errorMessage = e.toString();
} finally {
_isLoading = false;
notifyListeners(); // Trigger success/error UI
}
}
}
Use the provider package to inject the ViewModel into the widget tree above the widgets that require access to it.
void main() {
runApp(
MultiProvider(
providers: [
Provider(create: (_) => UserRepository()),
ChangeNotifierProvider(
create: (context) => UserViewModel(
userRepository: context.read<UserRepository>(),
),
),
],
child: const MyApp(),
),
);
}
Build the UI as a function of the ViewModel's state. Use Consumer to rebuild only the specific parts of the UI that depend on the state.
class UserProfileView extends StatelessWidget {
const UserProfileView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<UserViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (viewModel.errorMessage != null) {
return Center(child: Text('Error: ${viewModel.errorMessage}'));
}
if (viewModel.user != null) {
return Center(child: Text('Hello, ${viewModel.user!.name}'));
}
return const Center(child: Text('No user loaded.'));
},
),
floatingActionButton: FloatingActionButton(
// Use listen: false when invoking commands outside the build method
onPressed: () => context.read<UserViewModel>().loadUser('123'),
child: const Icon(Icons.refresh),
),
);
}
}
Validate-and-Fix: Verify that Consumer is placed as deep in the widget tree as possible to prevent unnecessary rebuilds of large widget subtrees.
StatelessWidget and StatefulWidget classes must only contain UI, layout, and routing logic. All data transformation and business logic MUST reside in the ViewModel.Provider.of<T>(context) with listen: true at the root of a large build method if only a small child needs the data. Use Consumer<T> or Selector<T, R> to scope rebuilds.Weekly Installs
1.1K
Repository
GitHub Stars
843
First Seen
Mar 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex1.1K
cursor1.1K
opencode1.1K
github-copilot1.0K
gemini-cli1.0K
amp1.0K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
103,800 周安装
Notion规范转实现计划工具:AI驱动项目管理,自动生成任务与跟踪进度
446 周安装
Three.js 3D Web开发教程 - WebGL/WebGPU图形编程、动画与性能优化指南
447 周安装
批判性思维与逻辑推理指南 - 提升AI智能体分析能力,避免信号稀释与语境坍塌
447 周安装
Tailwind v4 + shadcn/ui 生产级技术栈配置指南:5分钟快速搭建,避免常见错误
447 周安装
React Native 测试模式与工具:TDD、工厂模式、模拟模块实战指南
447 周安装
SaaS收入增长指标框架:产品经理必备的收入、留存与扩张指标指南
448 周安装
onPressedcontext.read<T>()Provider.of<T>(context, listen: false)notifyListeners().