[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7CS79P6GVEJ070E2FAFKB87":3,"skill-same:01M7CS79P6GVEJ070E2FAFKB87":49},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":29,"repositoryHtmlUrl":30,"repositoryHomepageUrl":31,"ownerHtmlUrl":32,"ownerBlogUrl":6,"ownerDescription":33,"updatedAt":34,"install":35,"resources":48},"01M7CS79P6GVEJ070E2FAFKB87","flutter-state-management",null,"Use when a Flutter screen needs app state or data - separate presentation from state, keep business logic out of widgets, and follow the app's existing state solution (Riverpod 3 Notifier\u002FAsyncNotifier, or the official ChangeNotifier+ListenableBuilder MVVM pattern)","makifbaysal\u002Ftasktrooper","makifbaysal","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F25031940?v=4","01M7CRG3ZCSYZSSEVDMW3DEYC8","Mehmet Akif Baysal","01M7CRG401FJ8YAXH5ER730XYV",122,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fmobile-developer\u002Fskills\u002Fflutter-state-management\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: flutter-state-management\ncategory: architecture\ndescription: Use when a Flutter screen needs app state or data - separate presentation from state, keep business logic out of widgets, and follow the app's existing state solution (Riverpod 3 Notifier\u002FAsyncNotifier, or the official ChangeNotifier+ListenableBuilder MVVM pattern)\ntech_stack: Flutter\n---\n# Flutter State Management\n\n## Overview\n\nWidgets render state; something else owns it. Keeping business logic and data out of the widget tree is what makes Flutter screens testable and rebuilds cheap.\n\n**Core principle:** Follow the app's existing state solution (Riverpod, Bloc, Provider, etc.) — do not introduce a second one. The pattern matters more than the library.\n\n## Rules (library-agnostic)\n\n- **One source of truth per piece of state**, owned by a controller\u002Fnotifier\u002Fbloc — never duplicated across widgets via `setState`.\n- **Presentation ↔ state via a thin interface:** the widget watches state and sends events\u002Fintents; it never contains the business rule or the network call.\n- **`StatefulWidget` is for ephemeral UI only** (animation controllers, focus, scroll) — anything that outlives a rebuild or comes from a service is app state.\n- **Handle all async states explicitly:** loading, data, empty, error. A `FutureBuilder`\u002F`AsyncValue` that only renders the happy path ships a spinner-forever bug.\n- **Dispose** controllers\u002Fsubscriptions to avoid leaks.\n- **Immutable state objects** (copyWith) so rebuilds are diff-friendly and predictable.\n\n## Worked Example — Riverpod 3 (`Notifier`\u002F`AsyncNotifier`, not `StateNotifier`)\n\n`StateNotifier`\u002F`StateNotifierProvider`\u002F`StateProvider`\u002F`ChangeNotifierProvider` are legacy as of Riverpod 3 — use `Notifier`\u002F`AsyncNotifier` in new code. If the repo still uses `StateNotifier`, follow it; don't migrate existing code on your own initiative.\n\n```dart\n\u002F\u002F state owner: no widgets, pure logic — unit-testable without pumping a widget\nclass TaskListNotifier extends AsyncNotifier\u003CList\u003CTask>> {\n  @override\n  Future\u003CList\u003CTask>> build() => ref.read(taskRepositoryProvider).list();\n\n  Future\u003Cvoid> reload() async {\n    state = const AsyncLoading();\n    state = await AsyncValue.guard(() => ref.read(taskRepositoryProvider).list());\n  }\n}\n\nfinal taskListProvider = AsyncNotifierProvider\u003CTaskListNotifier, List\u003CTask>>(TaskListNotifier.new);\n\n\u002F\u002F widget: watches state, renders each case, sends intents — no logic\nref.watch(taskListProvider).when(\n  loading: () => const AppSpinner(),\n  error:   (e, _) => AppErrorView(message: e.toString(), onRetry: () => ref.read(taskListProvider.notifier).reload()),\n  data:    (tasks) => tasks.isEmpty ? const EmptyView() : TaskList(tasks: tasks),\n);\n```\n\n## Alternative — official Flutter MVVM (`ChangeNotifier` + `ListenableBuilder`)\n\nFlutter's own architecture guide and the `flutter-apply-architecture-best-practices` skill teach this pattern when the app has no state-management package at all:\n\n```dart\nclass TaskListViewModel extends ChangeNotifier {\n  TaskListViewModel(this._repo);\n  final TaskRepository _repo;\n  List\u003CTask>? _tasks;\n  Object? _error;\n  bool _loading = false;\n\n  List\u003CTask>? get tasks => _tasks;\n  Object? get error => _error;\n  bool get loading => _loading;\n\n  Future\u003Cvoid> load() async {\n    _loading = true; _error = null; notifyListeners();\n    try { _tasks = await _repo.list(); }\n    catch (e) { _error = e; }\n    finally { _loading = false; notifyListeners(); }\n  }\n}\n\nListenableBuilder(\n  listenable: viewModel,\n  builder: (context, _) => switch (viewModel) {\n    _ when viewModel.loading => const AppSpinner(),\n    _ when viewModel.error != null => AppErrorView(message: '${viewModel.error}', onRetry: viewModel.load),\n    _ => TaskList(tasks: viewModel.tasks ?? const []),\n  },\n)\n```\n\nPick one pattern per app and stay consistent with what's already there — don't introduce a second state-management approach into a repo that already has one.\n\n## Common Mistakes\n\n- Business logic \u002F http calls inside `build` or `initState`.\n- `setState` juggling data that came from an API.\n- Introducing a new state library alongside the app's existing one.\n- Ignoring the error\u002Fempty branch of an async state.\n- Leaked controllers (no `dispose`).\n\n## Red Flags\n\n- A widget imports an http client or repository directly.\n- The same state mirrored in two widgets.\n- An async view with no error handling.\n","# Flutter State Management\n\n## Overview\n\nWidgets render state; something else owns it. Keeping business logic and data out of the widget tree is what makes Flutter screens testable and rebuilds cheap.\n\n**Core principle:** Follow the app's existing state solution (Riverpod, Bloc, Provider, etc.) — do not introduce a second one. The pattern matters more than the library.\n\n## Rules (library-agnostic)\n\n- **One source of truth per piece of state**, owned by a controller\u002Fnotifier\u002Fbloc — never duplicated across widgets via `setState`.\n- **Presentation ↔ state via a thin interface:** the widget watches state and sends events\u002Fintents; it never contains the business rule or the network call.\n- **`StatefulWidget` is for ephemeral UI only** (animation controllers, focus, scroll) — anything that outlives a rebuild or comes from a service is app state.\n- **Handle all async states explicitly:** loading, data, empty, error. A `FutureBuilder`\u002F`AsyncValue` that only renders the happy path ships a spinner-forever bug.\n- **Dispose** controllers\u002Fsubscriptions to avoid leaks.\n- **Immutable state objects** (copyWith) so rebuilds are diff-friendly and predictable.\n\n## Worked Example — Riverpod 3 (`Notifier`\u002F`AsyncNotifier`, not `StateNotifier`)\n\n`StateNotifier`\u002F`StateNotifierProvider`\u002F`StateProvider`\u002F`ChangeNotifierProvider` are legacy as of Riverpod 3 — use `Notifier`\u002F`AsyncNotifier` in new code. If the repo still uses `StateNotifier`, follow it; don't migrate existing code on your own initiative.\n\n```dart\n\u002F\u002F state owner: no widgets, pure logic — unit-testable without pumping a widget\nclass TaskListNotifier extends AsyncNotifier\u003CList\u003CTask>> {\n  @override\n  Future\u003CList\u003CTask>> build() => ref.read(taskRepositoryProvider).list();\n\n  Future\u003Cvoid> reload() async {\n    state = const AsyncLoading();\n    state = await AsyncValue.guard(() => ref.read(taskRepositoryProvider).list());\n  }\n}\n\nfinal taskListProvider = AsyncNotifierProvider\u003CTaskListNotifier, List\u003CTask>>(TaskListNotifier.new);\n\n\u002F\u002F widget: watches state, renders each case, sends intents — no logic\nref.watch(taskListProvider).when(\n  loading: () => const AppSpinner(),\n  error:   (e, _) => AppErrorView(message: e.toString(), onRetry: () => ref.read(taskListProvider.notifier).reload()),\n  data:    (tasks) => tasks.isEmpty ? const EmptyView() : TaskList(tasks: tasks),\n);\n```\n\n## Alternative — official Flutter MVVM (`ChangeNotifier` + `ListenableBuilder`)\n\nFlutter's own architecture guide and the `flutter-apply-architecture-best-practices` skill teach this pattern when the app has no state-management package at all:\n\n```dart\nclass TaskListViewModel extends ChangeNotifier {\n  TaskListViewModel(this._repo);\n  final TaskRepository _repo;\n  List\u003CTask>? _tasks;\n  Object? _error;\n  bool _loading = false;\n\n  List\u003CTask>? get tasks => _tasks;\n  Object? get error => _error;\n  bool get loading => _loading;\n\n  Future\u003Cvoid> load() async {\n    _loading = true; _error = null; notifyListeners();\n    try { _tasks = await _repo.list(); }\n    catch (e) { _error = e; }\n    finally { _loading = false; notifyListeners(); }\n  }\n}\n\nListenableBuilder(\n  listenable: viewModel,\n  builder: (context, _) => switch (viewModel) {\n    _ when viewModel.loading => const AppSpinner(),\n    _ when viewModel.error != null => AppErrorView(message: '${viewModel.error}', onRetry: viewModel.load),\n    _ => TaskList(tasks: viewModel.tasks ?? const []),\n  },\n)\n```\n\nPick one pattern per app and stay consistent with what's already there — don't introduce a second state-management approach into a repo that already has one.\n\n## Common Mistakes\n\n- Business logic \u002F http calls inside `build` or `initState`.\n- `setState` juggling data that came from an API.\n- Introducing a new state library alongside the app's existing one.\n- Ignoring the error\u002Fempty branch of an async state.\n- Leaked controllers (no `dispose`).\n\n## Red Flags\n\n- A widget imports an http client or repository directly.\n- The same state mirrored in two widgets.\n- An async view with no error handling.\n",{"name":5,"category":24,"tech_stack":25,"description":7},"architecture","Flutter","main","910394a0b6db9fc8c9a8f36328ec421d3ebd8b38","catalog\u002Fagents\u002Fmobile-developer\u002Fskills\u002Fflutter-state-management",18,"https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper","https:\u002F\u002Ftasktrooper.ai","https:\u002F\u002Fgithub.com\u002Fmakifbaysal","Senior Software Developer","2026-11-15 04:50:29",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add makifbaysal\u002Ftasktrooper --skill flutter-state-management",{"tool":41,"command":42},"bunx","bunx skills add makifbaysal\u002Ftasktrooper --skill flutter-state-management",{"tool":44,"command":45},"pnpm","pnpm dlx skills add makifbaysal\u002Ftasktrooper --skill flutter-state-management","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fmobile-developer\u002Fskills\u002Fflutter-state-management\u002FSKILL.md",true,[],[50,56,63,70,77,84],{"skillNo":51,"name":52,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":53,"categories":54,"keywords":55,"publishedAt":19},"01M7CSVEPQW6J6TD6CS2T5FPNZ","visual-craft","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fui-designer\u002Fskills\u002Fvisual-craft\u002FSKILL.md",[],[],{"skillNo":57,"name":58,"nameZh":6,"description":59,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":60,"categories":61,"keywords":62,"publishedAt":19},"01M7CSVAQG91ETHQ0HBNZDG5FR","ux-copy","Use when you write any words a user reads in a mockup or a hand-off - content language and voice, buttons, labels, errors, empty states, confirmations, numbers and dates, and the longest-string check","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fui-designer\u002Fskills\u002Fux-copy\u002FSKILL.md",[],[],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":69,"publishedAt":19},"01M7CSV6H0J38659NDXHJQ8396","screen-mockup-html","Use when you draw screens as the design task's HTML mockup - ONE canvas document holding every screen and every variant side by side, the sanitizer-safe recipe, phone and wide frames, the states grid, static control states, and the loopback self-check with screenshots at 375, 768 and 1440 attached to the task","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fui-designer\u002Fskills\u002Fscreen-mockup-html\u002FSKILL.md",[],[],{"skillNo":71,"name":72,"nameZh":6,"description":73,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":76,"publishedAt":19},"01M7CSV3JFRF3BM3XK52M7YEYR","performance-awareness","How the performance score moves for your role. Use when the score context shows a declining trend, or before handing off work you have not self-checked against every criterion.","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fui-designer\u002Fskills\u002Fperformance-awareness\u002FSKILL.md",[],[],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":19},"01M7CSTZDH00YRF5SCT7BRT9QD","design-variants","Use when a screen design needs variants - how many, variants as side-by-side rows on the one canvas, what makes two variants genuinely different, recommending one, how the human's choice is read, and narrowing to the chosen one in revision","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fui-designer\u002Fskills\u002Fdesign-variants\u002FSKILL.md",[],[],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":19},"01M7CSTV6P3SM3PAE28DB0RF77","design-system-authoring","Use when you derive, change or extend a design system - where tokens live per stack, the DTCG token tree, role-based names for both themes, base vs layer vs drift, the DESIGN.md section order, the inventory format, the contrast check and the lint every proposal returns","https:\u002F\u002Fgithub.com\u002Fmakifbaysal\u002Ftasktrooper\u002Fblob\u002Fmain\u002Fcatalog\u002Fagents\u002Fui-designer\u002Fskills\u002Fdesign-system-authoring\u002FSKILL.md",[],[]]