flutter-architecture by flutter/skills
npx skills add https://github.com/flutter/skills --skill flutter-architecture使用 MVVM 模式、单向数据流以及 UI、领域和数据层之间的严格关注点分离,实现一个可扩展、可维护的 Flutter 应用程序架构。假设使用标准的 Flutter 环境,利用 provider 进行依赖注入,并使用 ListenableBuilder 进行响应式 UI 更新。
在实现一个功能之前,使用以下逻辑评估架构需求:
分析功能需求 评估请求的功能,以确定必要的数据模型、服务和 UI 状态。暂停并询问用户: "请提供此功能所需的具体数据模型、API 端点或本地存储要求,并确认复杂的业务逻辑是否需要专用的领域层。"
实现数据层:服务 创建一个无状态的服务类来包装外部 API 或本地存储。此类不得包含业务逻辑或状态。
class SharedPreferencesService { static const String _kDarkMode = 'darkMode';
Future<void> setDarkMode(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kDarkMode, value); }
Future<bool> isDarkMode() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kDarkMode) ?? false; } }
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
实现数据层:存储库 创建一个存储库作为单一数据源。存储库使用服务,使用 Result 对象处理错误,并暴露领域模型或流。
class ThemeRepository { ThemeRepository(this._service);
final _darkModeController = StreamController<bool>.broadcast(); final SharedPreferencesService _service;
Future<Result<bool>> isDarkMode() async { try { final value = await _service.isDarkMode(); return Result.ok(value); } on Exception catch (e) { return Result.error(e); } }
Future<Result<void>> setDarkMode(bool value) async { try { await _service.setDarkMode(value); _darkModeController.add(value); return Result.ok(null); } on Exception catch (e) { return Result.error(e); } }
Stream<bool> observeDarkMode() => _darkModeController.stream; }
实现 UI 层:视图模型 创建一个 ChangeNotifier 来管理 UI 状态。使用命令模式处理用户交互和异步存储库调用。
class ThemeSwitchViewModel extends ChangeNotifier { ThemeSwitchViewModel(this._themeRepository) { load = Command0(_load)..execute(); toggle = Command0(_toggle); }
final ThemeRepository _themeRepository; bool _isDarkMode = false;
bool get isDarkMode => _isDarkMode;
late final Command0<void> load; late final Command0<void> toggle;
Future<Result<void>> _load() async { final result = await _themeRepository.isDarkMode(); if (result is Ok<bool>) { _isDarkMode = result.value; } notifyListeners(); return result; }
Future<Result<void>> _toggle() async { _isDarkMode = !_isDarkMode; final result = await _themeRepository.setDarkMode(_isDarkMode); notifyListeners(); return result; } }
实现 UI 层:视图 创建一个 StatelessWidget,使用 ListenableBuilder 观察 ViewModel。视图必须不包含任何业务逻辑。
class ThemeSwitch extends StatelessWidget { const ThemeSwitch({super.key, required this.viewmodel});
final ThemeSwitchViewModel viewmodel;
@override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Row( children: [ const Text('Dark Mode'), ListenableBuilder( listenable: viewmodel, builder: (context, ) { return Switch( value: viewmodel.isDarkMode, onChanged: () { viewmodel.toggle.execute(); }, ); }, ), ], ), ); } }
连接依赖项 在应用程序或路由级别使用构造函数注入或像 provider 这样的依赖注入框架来注入依赖项。
void main() { runApp( MainApp( themeRepository: ThemeRepository(SharedPreferencesService()), ), ); }
验证与修复 根据约束条件审查生成的实现。确保数据严格向下流动,事件严格向上流动。如果视图包含数据变更逻辑,请将其提取到 ViewModel 中。如果 ViewModel 直接访问 API,请将其提取到服务中并通过存储库进行路由。
Result 对象。每周安装量
1.1K
代码仓库
GitHub 星标数
833
首次出现
2026年3月4日
安全审计
安装于
codex1.0K
cursor1.0K
opencode1.0K
gemini-cli1.0K
github-copilot1.0K
amp1.0K
Implements a scalable, maintainable Flutter application architecture using the MVVM pattern, unidirectional data flow, and strict separation of concerns across UI, Domain, and Data layers. Assumes a standard Flutter environment utilizing provider for dependency injection and ListenableBuilder for reactive UI updates.
Before implementing a feature, evaluate the architectural requirements using the following logic:
Analyze Feature Requirements Evaluate the requested feature to determine the necessary data models, services, and UI state. STOP AND ASK THE USER: "Please provide the specific data models, API endpoints, or local storage requirements for this feature, and confirm if complex business logic requires a dedicated Domain (UseCase) layer."
Implement the Data Layer: Services Create a stateless service class to wrap the external API or local storage. This class must not contain business logic or state.
class SharedPreferencesService { static const String _kDarkMode = 'darkMode';
Future<void> setDarkMode(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kDarkMode, value); }
Future<bool> isDarkMode() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kDarkMode) ?? false; } }
Implement the Data Layer: Repositories Create a repository to act as the single source of truth. The repository consumes the service, handles errors using Result objects, and exposes domain models or streams.
class ThemeRepository { ThemeRepository(this._service);
final _darkModeController = StreamController<bool>.broadcast(); final SharedPreferencesService _service;
Future<Result<bool>> isDarkMode() async { try { final value = await _service.isDarkMode(); return Result.ok(value); } on Exception catch (e) { return Result.error(e); } }
Future<Result<void>> setDarkMode(bool value) async { try { await _service.setDarkMode(value); _darkModeController.add(value); return Result.ok(null); } on Exception catch (e) { return Result.error(e); } }
Stream<bool> observeDarkMode() => _darkModeController.stream; }
Implement the UI Layer: ViewModels Create a ChangeNotifier to manage UI state. Use the Command pattern to handle user interactions and asynchronous repository calls.
class ThemeSwitchViewModel extends ChangeNotifier { ThemeSwitchViewModel(this._themeRepository) { load = Command0(_load)..execute(); toggle = Command0(_toggle); }
final ThemeRepository _themeRepository; bool _isDarkMode = false;
bool get isDarkMode => _isDarkMode;
late final Command0<void> load; late final Command0<void> toggle;
Future<Result<void>> _load() async { final result = await _themeRepository.isDarkMode(); if (result is Ok<bool>) { _isDarkMode = result.value; } notifyListeners(); return result; }
Future<Result<void>> _toggle() async { _isDarkMode = !_isDarkMode; final result = await _themeRepository.setDarkMode(_isDarkMode); notifyListeners(); return result; } }
Implement the UI Layer: Views Create a StatelessWidget that observes the ViewModel using ListenableBuilder. The View must contain zero business logic.
class ThemeSwitch extends StatelessWidget { const ThemeSwitch({super.key, required this.viewmodel});
final ThemeSwitchViewModel viewmodel;
@override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Row( children: [ const Text('Dark Mode'), ListenableBuilder( listenable: viewmodel, builder: (context, ) { return Switch( value: viewmodel.isDarkMode, onChanged: () { viewmodel.toggle.execute(); }, ); }, ), ], ), ); } }
Wire Dependencies Inject the dependencies at the application or route level using constructor injection or a dependency injection framework like provider.
void main() { runApp( MainApp( themeRepository: ThemeRepository(SharedPreferencesService()), ), ); }
Validate and Fix Review the generated implementation against the constraints. Ensure that data flows strictly downwards (Repository -> ViewModel -> View) and events flow strictly upwards (View -> ViewModel -> Repository). If a View contains data mutation logic, extract it to the ViewModel. If a ViewModel directly accesses an API, extract it to a Service and route it through a Repository.
Result (Ok/Error) objects to the ViewModels.Weekly Installs
1.1K
Repository
GitHub Stars
833
First Seen
Mar 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex1.0K
cursor1.0K
opencode1.0K
gemini-cli1.0K
github-copilot1.0K
amp1.0K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
103,800 周安装
企业级智能体运维指南:云端AI系统生命周期管理、可观测性与安全控制
657 周安装
Amazon Aurora DSQL 技能:无服务器分布式 SQL 数据库管理与 MCP 工具集成
SQL查询优化指南:PostgreSQL、Snowflake、BigQuery高性能SQL编写技巧与方言参考
657 周安装
Gemini Live API 开发指南:实时语音视频交互、WebSockets集成与SDK使用
657 周安装
WordPress渗透测试指南:WPScan工具使用与漏洞扫描实战教程
Excel MCP Server:通过Model Context Protocol实现227项Excel自动化操作指南
657 周安装