[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71MS69WD1SD7MWH6QXZR0YY":3,"skill-same:01M71MS69WD1SD7MWH6QXZR0YY":78},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":15,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":53,"repositoryHtmlUrl":54,"repositoryHomepageUrl":55,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":6,"updatedAt":58,"install":59,"resources":72},"01M71MS69WD1SD7MWH6QXZR0YY","dart-flutter-patterns",null,"Use when writing or reviewing Dart and Flutter code with copy-paste idioms: null safety, sealed state, BLoC or Riverpod, GoRouter guards, Dio and widget tests. A pattern catalogue; for building a Flutter app end to end with reference files use flutter-expert.","monoes\u002Fmonomind","monoes","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F276744696?v=4","01M71KWF37AJ2NJBP3KWBVSDGT","01M71KWF4CSF8ADH0M6HA09VD2",23,"MIT","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fdart-flutter-patterns\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fdart-flutter-patterns\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"patterns","dart","flutter","dart flutter patterns","catalogue","standards","coding","flutter app","Safety","pattern","mobile","paste","code","when","writing","guards","files","end","expert","app","2026-11-10 10:56:23",false,"---\nname: dart-flutter-patterns\ndescription: \"Use when writing or reviewing Dart and Flutter code with copy-paste idioms: null safety, sealed state, BLoC or Riverpod, GoRouter guards, Dio and widget tests. A pattern catalogue; for building a Flutter app end to end with reference files use flutter-expert.\"\ntags: [\"mobile\",\"flutter\",\"coding-standards\"]\ntools: []\nlicense: MIT\nsource: https:\u002F\u002Fgithub.com\u002Faffaan-m\u002FECC\nsource_path: \"skills\u002Fdart-flutter-patterns\"\nsource_commit: bf70150eb2df8070024e5bdf08e4aa08959e2735\n---\n# Dart\u002FFlutter Patterns\n\n## When to Use\n\nUse this skill when:\n- Starting a new Flutter feature and need idiomatic patterns for state management, navigation, or data access\n- Reviewing or writing Dart code and need guidance on null safety, sealed types, or async composition\n- Setting up a new Flutter project and choosing between BLoC, Riverpod, or Provider\n- Implementing secure HTTP clients, WebView integration, or local storage\n- Writing tests for Flutter widgets, Cubits, or Riverpod providers\n- Wiring up GoRouter with authentication guards\n\n## How It Works\n\nThis skill provides copy-paste-ready Dart\u002FFlutter code patterns organized by concern:\n1. **Null safety** — avoid `!`, prefer `?.`\u002F`??`\u002Fpattern matching\n2. **Immutable state** — sealed classes, `freezed`, `copyWith`\n3. **Async composition** — concurrent `Future.wait`, safe `BuildContext` after `await`\n4. **Widget architecture** — extract to classes (not methods), `const` propagation, scoped rebuilds\n5. **State management** — BLoC\u002FCubit events, Riverpod notifiers and derived providers\n6. **Navigation** — GoRouter with reactive auth guards via `refreshListenable`\n7. **Networking** — Dio with interceptors, token refresh with one-time retry guard\n8. **Error handling** — global capture, `ErrorWidget.builder`, crashlytics wiring\n9. **Testing** — unit (BLoC test), widget (ProviderScope overrides), fakes over mocks\n\n## Examples\n\n```dart\n\u002F\u002F Sealed state — prevents impossible states\nsealed class AsyncState\u003CT> {}\nfinal class Loading\u003CT> extends AsyncState\u003CT> {}\nfinal class Success\u003CT> extends AsyncState\u003CT> { final T data; const Success(this.data); }\nfinal class Failure\u003CT> extends AsyncState\u003CT> { final Object error; const Failure(this.error); }\n\n\u002F\u002F GoRouter with reactive auth redirect\nfinal router = GoRouter(\n  refreshListenable: GoRouterRefreshStream(authCubit.stream),\n  redirect: (context, state) {\n    final authed = context.read\u003CAuthCubit>().state is AuthAuthenticated;\n    if (!authed && !state.matchedLocation.startsWith('\u002Flogin')) return '\u002Flogin';\n    return null;\n  },\n  routes: [...],\n);\n\n\u002F\u002F Riverpod derived provider with safe firstWhereOrNull\n@riverpod\ndouble cartTotal(Ref ref) {\n  final cart = ref.watch(cartNotifierProvider);\n  final products = ref.watch(productsProvider).valueOrNull ?? [];\n  return cart.fold(0.0, (total, item) {\n    final product = products.firstWhereOrNull((p) => p.id == item.productId);\n    return total + (product?.price ?? 0) * item.quantity;\n  });\n}\n```\n\n---\n\nPractical, production-ready patterns for Dart and Flutter applications. Library-agnostic where possible, with explicit coverage of the most common ecosystem packages.\n\n---\n\n## 1. Null Safety Fundamentals\n\n### Prefer Patterns Over Bang Operator\n\n```dart\n\u002F\u002F BAD — crashes at runtime if null\nfinal name = user!.name;\n\n\u002F\u002F GOOD — provide fallback\nfinal name = user?.name ?? 'Unknown';\n\n\u002F\u002F GOOD — Dart 3 pattern matching (preferred for complex cases)\nfinal display = switch (user) {\n  User(:final name, :final email) => '$name \u003C$email>',\n  null => 'Guest',\n};\n\n\u002F\u002F GOOD — guard early return\nString getUserName(User? user) {\n  if (user == null) return 'Unknown';\n  return user.name; \u002F\u002F promoted to non-null after check\n}\n```\n\n### Avoid `late` Overuse\n\n```dart\n\u002F\u002F BAD — defers null error to runtime\nlate String userId;\n\n\u002F\u002F GOOD — nullable with explicit initialization\nString? userId;\n\n\u002F\u002F OK — use late only when initialization is guaranteed before first access\n\u002F\u002F (e.g., in initState() before any widget interaction)\nlate final AnimationController _controller;\n\n@override\nvoid initState() {\n  super.initState();\n  _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));\n}\n```\n\n---\n\n## 2. Immutable State\n\n### Sealed Classes for State Hierarchies\n\n```dart\nsealed class UserState {}\n\nfinal class UserInitial extends UserState {}\n\nfinal class UserLoading extends UserState {}\n\nfinal class UserLoaded extends UserState {\n  const UserLoaded(this.user);\n  final User user;\n}\n\nfinal class UserError extends UserState {\n  const UserError(this.message);\n  final String message;\n}\n\n\u002F\u002F Exhaustive switch — compiler enforces all branches\nWidget buildFrom(UserState state) => switch (state) {\n  UserInitial() => const SizedBox.shrink(),\n  UserLoading() => const CircularProgressIndicator(),\n  UserLoaded(:final user) => UserCard(user: user),\n  UserError(:final message) => ErrorText(message),\n};\n```\n\n### Freezed for Boilerplate-Free Immutability\n\n```dart\nimport 'package:freezed_annotation\u002Ffreezed_annotation.dart';\n\npart 'user.freezed.dart';\npart 'user.g.dart';\n\n@freezed\nclass User with _$User {\n  const factory User({\n    required String id,\n    required String name,\n    required String email,\n    @Default(false) bool isAdmin,\n  }) = _User;\n\n  factory User.fromJson(Map\u003CString, dynamic> json) => _$UserFromJson(json);\n}\n\n\u002F\u002F Usage\nfinal user = User(id: '1', name: 'Alice', email: 'alice@example.com');\nfinal updated = user.copyWith(name: 'Alice Smith'); \u002F\u002F immutable update\nfinal json = user.toJson();\nfinal fromJson = User.fromJson(json);\n```\n\n---\n\n## 3. Async Composition\n\n### Structured Concurrency with Future.wait\n\n```dart\nFuture\u003CDashboardData> loadDashboard(UserRepository users, OrderRepository orders) async {\n  \u002F\u002F Run concurrently — don't await sequentially\n  final (userList, orderList) = await (\n    users.getAll(),\n    orders.getRecent(),\n  ).wait; \u002F\u002F Dart 3 record destructuring + Future.wait extension\n\n  return DashboardData(users: userList, orders: orderList);\n}\n```\n\n### Stream Patterns\n\n```dart\n\u002F\u002F Repository exposes reactive streams for live data\nStream\u003CList\u003CItem>> watchCartItems() => _db\n    .watchTable('cart_items')\n    .map((rows) => rows.map(Item.fromRow).toList());\n\n\u002F\u002F In widget layer — declarative, no manual subscription\nStreamBuilder\u003CList\u003CItem>>(\n  stream: cartRepository.watchCartItems(),\n  builder: (context, snapshot) => switch (snapshot) {\n    AsyncSnapshot(connectionState: ConnectionState.waiting) =>\n        const CircularProgressIndicator(),\n    AsyncSnapshot(:final error?) => ErrorWidget(error.toString()),\n    AsyncSnapshot(:final data?) => CartList(items: data),\n    _ => const SizedBox.shrink(),\n  },\n)\n```\n\n### BuildContext After Await\n\n```dart\n\u002F\u002F CRITICAL — always check mounted after any await in StatefulWidget\nFuture\u003Cvoid> _handleSubmit() async {\n  setState(() => _isLoading = true);\n  try {\n    await authService.login(_email, _password);\n    if (!mounted) return; \u002F\u002F ← guard before using context\n    context.go('\u002Fhome');\n  } on AuthException catch (e) {\n    if (!mounted) return;\n    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message)));\n  } finally {\n    if (mounted) setState(() => _isLoading = false);\n  }\n}\n```\n\n---\n\n## 4. Widget Architecture\n\n### Extract to Classes, Not Methods\n\n```dart\n\u002F\u002F BAD — private method returning widget, prevents optimization\nWidget _buildHeader() {\n  return Container(\n    padding: const EdgeInsets.all(16),\n    child: Text(title, style: Theme.of(context).textTheme.headlineMedium),\n  );\n}\n\n\u002F\u002F GOOD — separate widget class, enables const, element reuse\nclass _PageHeader extends StatelessWidget {\n  const _PageHeader(this.title);\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      padding: const EdgeInsets.all(16),\n      child: Text(title, style: Theme.of(context).textTheme.headlineMedium),\n    );\n  }\n}\n```\n\n### const Propagation\n\n```dart\n\u002F\u002F BAD — new instances every rebuild\nchild: Padding(\n  padding: EdgeInsets.all(16.0),       \u002F\u002F not const\n  child: Icon(Icons.home, size: 24.0), \u002F\u002F not const\n)\n\n\u002F\u002F GOOD — const stops rebuild propagation\nchild: const Padding(\n  padding: EdgeInsets.all(16.0),\n  child: Icon(Icons.home, size: 24.0),\n)\n```\n\n### Scoped Rebuilds\n\n```dart\n\u002F\u002F BAD — entire page rebuilds on every counter change\nclass CounterPage extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final count = ref.watch(counterProvider); \u002F\u002F rebuilds everything\n    return Scaffold(\n      body: Column(children: [\n        const ExpensiveHeader(), \u002F\u002F unnecessarily rebuilt\n        Text('$count'),\n        const ExpensiveFooter(), \u002F\u002F unnecessarily rebuilt\n      ]),\n    );\n  }\n}\n\n\u002F\u002F GOOD — isolate the rebuilding part\nclass CounterPage extends StatelessWidget {\n  const CounterPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return const Scaffold(\n      body: Column(children: [\n        ExpensiveHeader(),        \u002F\u002F never rebuilt (const)\n        _CounterDisplay(),        \u002F\u002F only this rebuilds\n        ExpensiveFooter(),        \u002F\u002F never rebuilt (const)\n      ]),\n    );\n  }\n}\n\nclass _CounterDisplay extends ConsumerWidget {\n  const _CounterDisplay();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final count = ref.watch(counterProvider);\n    return Text('$count');\n  }\n}\n```\n\n---\n\n## 5. State Management: BLoC\u002FCubit\n\n```dart\n\u002F\u002F Cubit — synchronous or simple async state\nclass AuthCubit extends Cubit\u003CAuthState> {\n  AuthCubit(this._authService) : super(const AuthState.initial());\n  final AuthService _authService;\n\n  Future\u003Cvoid> login(String email, String password) async {\n    emit(const AuthState.loading());\n    try {\n      final user = await _authService.login(email, password);\n      emit(AuthState.authenticated(user));\n    } on AuthException catch (e) {\n      emit(AuthState.error(e.message));\n    }\n  }\n\n  void logout() {\n    _authService.logout();\n    emit(const AuthState.initial());\n  }\n}\n\n\u002F\u002F In widget\nBlocBuilder\u003CAuthCubit, AuthState>(\n  builder: (context, state) => switch (state) {\n    AuthInitial() => const LoginForm(),\n    AuthLoading() => const CircularProgressIndicator(),\n    AuthAuthenticated(:final user) => HomePage(user: user),\n    AuthError(:final message) => ErrorView(message: message),\n  },\n)\n```\n\n---\n\n## 6. State Management: Riverpod\n\n```dart\n\u002F\u002F Auto-dispose async provider\n@riverpod\nFuture\u003CList\u003CProduct>> products(Ref ref) async {\n  final repo = ref.watch(productRepositoryProvider);\n  return repo.getAll();\n}\n\n\u002F\u002F Notifier with complex mutations\n@riverpod\nclass CartNotifier extends _$CartNotifier {\n  @override\n  List\u003CCartItem> build() => [];\n\n  void add(Product product) {\n    final existing = state.where((i) => i.productId == product.id).firstOrNull;\n    if (existing != null) {\n      state = [\n        for (final item in state)\n          if (item.productId == product.id) item.copyWith(quantity: item.quantity + 1)\n          else item,\n      ];\n    } else {\n      state = [...state, CartItem(productId: product.id, quantity: 1)];\n    }\n  }\n\n  void remove(String productId) =>\n      state = state.where((i) => i.productId != productId).toList();\n\n  void clear() => state = [];\n}\n\n\u002F\u002F Derived provider (selector pattern)\n@riverpod\nint cartCount(Ref ref) => ref.watch(cartNotifierProvider).length;\n\n@riverpod\ndouble cartTotal(Ref ref) {\n  final cart = ref.watch(cartNotifierProvider);\n  final products = ref.watch(productsProvider).valueOrNull ?? [];\n  return cart.fold(0.0, (total, item) {\n    \u002F\u002F firstWhereOrNull (from collection package) avoids StateError when product is missing\n    final product = products.firstWhereOrNull((p) => p.id == item.productId);\n    return total + (product?.price ?? 0) * item.quantity;\n  });\n}\n```\n\n---\n\n## 7. Navigation with GoRouter\n\n```dart\nfinal router = GoRouter(\n  initialLocation: '\u002F',\n  \u002F\u002F refreshListenable re-evaluates redirect whenever auth state changes\n  refreshListenable: GoRouterRefreshStream(authCubit.stream),\n  redirect: (context, state) {\n    final isLoggedIn = context.read\u003CAuthCubit>().state is AuthAuthenticated;\n    final isGoingToLogin = state.matchedLocation == '\u002Flogin';\n    if (!isLoggedIn && !isGoingToLogin) return '\u002Flogin';\n    if (isLoggedIn && isGoingToLogin) return '\u002F';\n    return null;\n  },\n  routes: [\n    GoRoute(path: '\u002Flogin', builder: (_, __) => const LoginPage()),\n    ShellRoute(\n      builder: (context, state, child) => AppShell(child: child),\n      routes: [\n        GoRoute(path: '\u002F', builder: (_, __) => const HomePage()),\n        GoRoute(\n          path: '\u002Fproducts\u002F:id',\n          builder: (context, state) =>\n              ProductDetailPage(id: state.pathParameters['id']!),\n        ),\n      ],\n    ),\n  ],\n);\n```\n\n---\n\n## 8. HTTP with Dio\n\n```dart\nfinal dio = Dio(BaseOptions(\n  baseUrl: const String.fromEnvironment('API_URL'),\n  connectTimeout: const Duration(seconds: 10),\n  receiveTimeout: const Duration(seconds: 30),\n  headers: {'Content-Type': 'application\u002Fjson'},\n));\n\n\u002F\u002F Add auth interceptor\ndio.interceptors.add(InterceptorsWrapper(\n  onRequest: (options, handler) async {\n    final token = await secureStorage.read(key: 'auth_token');\n    if (token != null) options.headers['Authorization'] = 'Bearer $token';\n    handler.next(options);\n  },\n  onError: (error, handler) async {\n    \u002F\u002F Guard against infinite retry loops: only attempt refresh once per request\n    final isRetry = error.requestOptions.extra['_isRetry'] == true;\n    if (!isRetry && error.response?.statusCode == 401) {\n      final refreshed = await attemptTokenRefresh();\n      if (refreshed) {\n        error.requestOptions.extra['_isRetry'] = true;\n        return handler.resolve(await dio.fetch(error.requestOptions));\n      }\n    }\n    handler.next(error);\n  },\n));\n\n\u002F\u002F Repository using Dio\nclass UserApiDataSource {\n  const UserApiDataSource(this._dio);\n  final Dio _dio;\n\n  Future\u003CUser> getById(String id) async {\n    final response = await _dio.get\u003CMap\u003CString, dynamic>>('\u002Fusers\u002F$id');\n    return User.fromJson(response.data!);\n  }\n}\n```\n\n---\n\n## 9. Error Handling Architecture\n\n```dart\n\u002F\u002F Global error capture — set up in main()\nvoid main() {\n  FlutterError.onError = (details) {\n    FlutterError.presentError(details);\n    crashlytics.recordFlutterFatalError(details);\n  };\n\n  PlatformDispatcher.instance.onError = (error, stack) {\n    crashlytics.recordError(error, stack, fatal: true);\n    return true;\n  };\n\n  runApp(const App());\n}\n\n\u002F\u002F Custom ErrorWidget for production\nclass App extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    ErrorWidget.builder = (details) => ProductionErrorWidget(details);\n    return MaterialApp.router(routerConfig: router);\n  }\n}\n```\n\n---\n\n## 10. Testing Quick Reference\n\n```dart\n\u002F\u002F Unit test — use case\ntest('GetUserUseCase returns null for missing user', () async {\n  final repo = FakeUserRepository();\n  final useCase = GetUserUseCase(repo);\n  expect(await useCase('missing-id'), isNull);\n});\n\n\u002F\u002F BLoC test\nblocTest\u003CAuthCubit, AuthState>(\n  'emits loading then error on failed login',\n  build: () => AuthCubit(FakeAuthService(throwsOn: 'login')),\n  act: (cubit) => cubit.login('user@test.com', 'wrong'),\n  expect: () => [const AuthState.loading(), isA\u003CAuthError>()],\n);\n\n\u002F\u002F Widget test\ntestWidgets('CartBadge shows item count', (tester) async {\n  await tester.pumpWidget(\n    ProviderScope(\n      overrides: [cartNotifierProvider.overrideWith(() => FakeCartNotifier(count: 3))],\n      child: const MaterialApp(home: CartBadge()),\n    ),\n  );\n  expect(find.text('3'), findsOneWidget);\n});\n```\n\n---\n\n## References\n\n- [Effective Dart: Design](https:\u002F\u002Fdart.dev\u002Feffective-dart\u002Fdesign)\n- [Flutter Performance Best Practices](https:\u002F\u002Fdocs.flutter.dev\u002Fperf\u002Fbest-practices)\n- [Riverpod Documentation](https:\u002F\u002Friverpod.dev\u002F)\n- [BLoC Library](https:\u002F\u002Fbloclibrary.dev\u002F)\n- [GoRouter](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fgo_router)\n- [Freezed](https:\u002F\u002Fpub.dev\u002Fpackages\u002Ffreezed)\n- Skill: `flutter-dart-code-review` — comprehensive review checklist\n- Rules: `rules\u002Fdart\u002F` — coding style, patterns, security, testing, hooks\n","# Dart\u002FFlutter Patterns\n\n## When to Use\n\nUse this skill when:\n- Starting a new Flutter feature and need idiomatic patterns for state management, navigation, or data access\n- Reviewing or writing Dart code and need guidance on null safety, sealed types, or async composition\n- Setting up a new Flutter project and choosing between BLoC, Riverpod, or Provider\n- Implementing secure HTTP clients, WebView integration, or local storage\n- Writing tests for Flutter widgets, Cubits, or Riverpod providers\n- Wiring up GoRouter with authentication guards\n\n## How It Works\n\nThis skill provides copy-paste-ready Dart\u002FFlutter code patterns organized by concern:\n1. **Null safety** — avoid `!`, prefer `?.`\u002F`??`\u002Fpattern matching\n2. **Immutable state** — sealed classes, `freezed`, `copyWith`\n3. **Async composition** — concurrent `Future.wait`, safe `BuildContext` after `await`\n4. **Widget architecture** — extract to classes (not methods), `const` propagation, scoped rebuilds\n5. **State management** — BLoC\u002FCubit events, Riverpod notifiers and derived providers\n6. **Navigation** — GoRouter with reactive auth guards via `refreshListenable`\n7. **Networking** — Dio with interceptors, token refresh with one-time retry guard\n8. **Error handling** — global capture, `ErrorWidget.builder`, crashlytics wiring\n9. **Testing** — unit (BLoC test), widget (ProviderScope overrides), fakes over mocks\n\n## Examples\n\n```dart\n\u002F\u002F Sealed state — prevents impossible states\nsealed class AsyncState\u003CT> {}\nfinal class Loading\u003CT> extends AsyncState\u003CT> {}\nfinal class Success\u003CT> extends AsyncState\u003CT> { final T data; const Success(this.data); }\nfinal class Failure\u003CT> extends AsyncState\u003CT> { final Object error; const Failure(this.error); }\n\n\u002F\u002F GoRouter with reactive auth redirect\nfinal router = GoRouter(\n  refreshListenable: GoRouterRefreshStream(authCubit.stream),\n  redirect: (context, state) {\n    final authed = context.read\u003CAuthCubit>().state is AuthAuthenticated;\n    if (!authed && !state.matchedLocation.startsWith('\u002Flogin')) return '\u002Flogin';\n    return null;\n  },\n  routes: [...],\n);\n\n\u002F\u002F Riverpod derived provider with safe firstWhereOrNull\n@riverpod\ndouble cartTotal(Ref ref) {\n  final cart = ref.watch(cartNotifierProvider);\n  final products = ref.watch(productsProvider).valueOrNull ?? [];\n  return cart.fold(0.0, (total, item) {\n    final product = products.firstWhereOrNull((p) => p.id == item.productId);\n    return total + (product?.price ?? 0) * item.quantity;\n  });\n}\n```\n\n---\n\nPractical, production-ready patterns for Dart and Flutter applications. Library-agnostic where possible, with explicit coverage of the most common ecosystem packages.\n\n---\n\n## 1. Null Safety Fundamentals\n\n### Prefer Patterns Over Bang Operator\n\n```dart\n\u002F\u002F BAD — crashes at runtime if null\nfinal name = user!.name;\n\n\u002F\u002F GOOD — provide fallback\nfinal name = user?.name ?? 'Unknown';\n\n\u002F\u002F GOOD — Dart 3 pattern matching (preferred for complex cases)\nfinal display = switch (user) {\n  User(:final name, :final email) => '$name \u003C$email>',\n  null => 'Guest',\n};\n\n\u002F\u002F GOOD — guard early return\nString getUserName(User? user) {\n  if (user == null) return 'Unknown';\n  return user.name; \u002F\u002F promoted to non-null after check\n}\n```\n\n### Avoid `late` Overuse\n\n```dart\n\u002F\u002F BAD — defers null error to runtime\nlate String userId;\n\n\u002F\u002F GOOD — nullable with explicit initialization\nString? userId;\n\n\u002F\u002F OK — use late only when initialization is guaranteed before first access\n\u002F\u002F (e.g., in initState() before any widget interaction)\nlate final AnimationController _controller;\n\n@override\nvoid initState() {\n  super.initState();\n  _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));\n}\n```\n\n---\n\n## 2. Immutable State\n\n### Sealed Classes for State Hierarchies\n\n```dart\nsealed class UserState {}\n\nfinal class UserInitial extends UserState {}\n\nfinal class UserLoading extends UserState {}\n\nfinal class UserLoaded extends UserState {\n  const UserLoaded(this.user);\n  final User user;\n}\n\nfinal class UserError extends UserState {\n  const UserError(this.message);\n  final String message;\n}\n\n\u002F\u002F Exhaustive switch — compiler enforces all branches\nWidget buildFrom(UserState state) => switch (state) {\n  UserInitial() => const SizedBox.shrink(),\n  UserLoading() => const CircularProgressIndicator(),\n  UserLoaded(:final user) => UserCard(user: user),\n  UserError(:final message) => ErrorText(message),\n};\n```\n\n### Freezed for Boilerplate-Free Immutability\n\n```dart\nimport 'package:freezed_annotation\u002Ffreezed_annotation.dart';\n\npart 'user.freezed.dart';\npart 'user.g.dart';\n\n@freezed\nclass User with _$User {\n  const factory User({\n    required String id,\n    required String name,\n    required String email,\n    @Default(false) bool isAdmin,\n  }) = _User;\n\n  factory User.fromJson(Map\u003CString, dynamic> json) => _$UserFromJson(json);\n}\n\n\u002F\u002F Usage\nfinal user = User(id: '1', name: 'Alice', email: 'alice@example.com');\nfinal updated = user.copyWith(name: 'Alice Smith'); \u002F\u002F immutable update\nfinal json = user.toJson();\nfinal fromJson = User.fromJson(json);\n```\n\n---\n\n## 3. Async Composition\n\n### Structured Concurrency with Future.wait\n\n```dart\nFuture\u003CDashboardData> loadDashboard(UserRepository users, OrderRepository orders) async {\n  \u002F\u002F Run concurrently — don't await sequentially\n  final (userList, orderList) = await (\n    users.getAll(),\n    orders.getRecent(),\n  ).wait; \u002F\u002F Dart 3 record destructuring + Future.wait extension\n\n  return DashboardData(users: userList, orders: orderList);\n}\n```\n\n### Stream Patterns\n\n```dart\n\u002F\u002F Repository exposes reactive streams for live data\nStream\u003CList\u003CItem>> watchCartItems() => _db\n    .watchTable('cart_items')\n    .map((rows) => rows.map(Item.fromRow).toList());\n\n\u002F\u002F In widget layer — declarative, no manual subscription\nStreamBuilder\u003CList\u003CItem>>(\n  stream: cartRepository.watchCartItems(),\n  builder: (context, snapshot) => switch (snapshot) {\n    AsyncSnapshot(connectionState: ConnectionState.waiting) =>\n        const CircularProgressIndicator(),\n    AsyncSnapshot(:final error?) => ErrorWidget(error.toString()),\n    AsyncSnapshot(:final data?) => CartList(items: data),\n    _ => const SizedBox.shrink(),\n  },\n)\n```\n\n### BuildContext After Await\n\n```dart\n\u002F\u002F CRITICAL — always check mounted after any await in StatefulWidget\nFuture\u003Cvoid> _handleSubmit() async {\n  setState(() => _isLoading = true);\n  try {\n    await authService.login(_email, _password);\n    if (!mounted) return; \u002F\u002F ← guard before using context\n    context.go('\u002Fhome');\n  } on AuthException catch (e) {\n    if (!mounted) return;\n    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message)));\n  } finally {\n    if (mounted) setState(() => _isLoading = false);\n  }\n}\n```\n\n---\n\n## 4. Widget Architecture\n\n### Extract to Classes, Not Methods\n\n```dart\n\u002F\u002F BAD — private method returning widget, prevents optimization\nWidget _buildHeader() {\n  return Container(\n    padding: const EdgeInsets.all(16),\n    child: Text(title, style: Theme.of(context).textTheme.headlineMedium),\n  );\n}\n\n\u002F\u002F GOOD — separate widget class, enables const, element reuse\nclass _PageHeader extends StatelessWidget {\n  const _PageHeader(this.title);\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      padding: const EdgeInsets.all(16),\n      child: Text(title, style: Theme.of(context).textTheme.headlineMedium),\n    );\n  }\n}\n```\n\n### const Propagation\n\n```dart\n\u002F\u002F BAD — new instances every rebuild\nchild: Padding(\n  padding: EdgeInsets.all(16.0),       \u002F\u002F not const\n  child: Icon(Icons.home, size: 24.0), \u002F\u002F not const\n)\n\n\u002F\u002F GOOD — const stops rebuild propagation\nchild: const Padding(\n  padding: EdgeInsets.all(16.0),\n  child: Icon(Icons.home, size: 24.0),\n)\n```\n\n### Scoped Rebuilds\n\n```dart\n\u002F\u002F BAD — entire page rebuilds on every counter change\nclass CounterPage extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final count = ref.watch(counterProvider); \u002F\u002F rebuilds everything\n    return Scaffold(\n      body: Column(children: [\n        const ExpensiveHeader(), \u002F\u002F unnecessarily rebuilt\n        Text('$count'),\n        const ExpensiveFooter(), \u002F\u002F unnecessarily rebuilt\n      ]),\n    );\n  }\n}\n\n\u002F\u002F GOOD — isolate the rebuilding part\nclass CounterPage extends StatelessWidget {\n  const CounterPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return const Scaffold(\n      body: Column(children: [\n        ExpensiveHeader(),        \u002F\u002F never rebuilt (const)\n        _CounterDisplay(),        \u002F\u002F only this rebuilds\n        ExpensiveFooter(),        \u002F\u002F never rebuilt (const)\n      ]),\n    );\n  }\n}\n\nclass _CounterDisplay extends ConsumerWidget {\n  const _CounterDisplay();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final count = ref.watch(counterProvider);\n    return Text('$count');\n  }\n}\n```\n\n---\n\n## 5. State Management: BLoC\u002FCubit\n\n```dart\n\u002F\u002F Cubit — synchronous or simple async state\nclass AuthCubit extends Cubit\u003CAuthState> {\n  AuthCubit(this._authService) : super(const AuthState.initial());\n  final AuthService _authService;\n\n  Future\u003Cvoid> login(String email, String password) async {\n    emit(const AuthState.loading());\n    try {\n      final user = await _authService.login(email, password);\n      emit(AuthState.authenticated(user));\n    } on AuthException catch (e) {\n      emit(AuthState.error(e.message));\n    }\n  }\n\n  void logout() {\n    _authService.logout();\n    emit(const AuthState.initial());\n  }\n}\n\n\u002F\u002F In widget\nBlocBuilder\u003CAuthCubit, AuthState>(\n  builder: (context, state) => switch (state) {\n    AuthInitial() => const LoginForm(),\n    AuthLoading() => const CircularProgressIndicator(),\n    AuthAuthenticated(:final user) => HomePage(user: user),\n    AuthError(:final message) => ErrorView(message: message),\n  },\n)\n```\n\n---\n\n## 6. State Management: Riverpod\n\n```dart\n\u002F\u002F Auto-dispose async provider\n@riverpod\nFuture\u003CList\u003CProduct>> products(Ref ref) async {\n  final repo = ref.watch(productRepositoryProvider);\n  return repo.getAll();\n}\n\n\u002F\u002F Notifier with complex mutations\n@riverpod\nclass CartNotifier extends _$CartNotifier {\n  @override\n  List\u003CCartItem> build() => [];\n\n  void add(Product product) {\n    final existing = state.where((i) => i.productId == product.id).firstOrNull;\n    if (existing != null) {\n      state = [\n        for (final item in state)\n          if (item.productId == product.id) item.copyWith(quantity: item.quantity + 1)\n          else item,\n      ];\n    } else {\n      state = [...state, CartItem(productId: product.id, quantity: 1)];\n    }\n  }\n\n  void remove(String productId) =>\n      state = state.where((i) => i.productId != productId).toList();\n\n  void clear() => state = [];\n}\n\n\u002F\u002F Derived provider (selector pattern)\n@riverpod\nint cartCount(Ref ref) => ref.watch(cartNotifierProvider).length;\n\n@riverpod\ndouble cartTotal(Ref ref) {\n  final cart = ref.watch(cartNotifierProvider);\n  final products = ref.watch(productsProvider).valueOrNull ?? [];\n  return cart.fold(0.0, (total, item) {\n    \u002F\u002F firstWhereOrNull (from collection package) avoids StateError when product is missing\n    final product = products.firstWhereOrNull((p) => p.id == item.productId);\n    return total + (product?.price ?? 0) * item.quantity;\n  });\n}\n```\n\n---\n\n## 7. Navigation with GoRouter\n\n```dart\nfinal router = GoRouter(\n  initialLocation: '\u002F',\n  \u002F\u002F refreshListenable re-evaluates redirect whenever auth state changes\n  refreshListenable: GoRouterRefreshStream(authCubit.stream),\n  redirect: (context, state) {\n    final isLoggedIn = context.read\u003CAuthCubit>().state is AuthAuthenticated;\n    final isGoingToLogin = state.matchedLocation == '\u002Flogin';\n    if (!isLoggedIn && !isGoingToLogin) return '\u002Flogin';\n    if (isLoggedIn && isGoingToLogin) return '\u002F';\n    return null;\n  },\n  routes: [\n    GoRoute(path: '\u002Flogin', builder: (_, __) => const LoginPage()),\n    ShellRoute(\n      builder: (context, state, child) => AppShell(child: child),\n      routes: [\n        GoRoute(path: '\u002F', builder: (_, __) => const HomePage()),\n        GoRoute(\n          path: '\u002Fproducts\u002F:id',\n          builder: (context, state) =>\n              ProductDetailPage(id: state.pathParameters['id']!),\n        ),\n      ],\n    ),\n  ],\n);\n```\n\n---\n\n## 8. HTTP with Dio\n\n```dart\nfinal dio = Dio(BaseOptions(\n  baseUrl: const String.fromEnvironment('API_URL'),\n  connectTimeout: const Duration(seconds: 10),\n  receiveTimeout: const Duration(seconds: 30),\n  headers: {'Content-Type': 'application\u002Fjson'},\n));\n\n\u002F\u002F Add auth interceptor\ndio.interceptors.add(InterceptorsWrapper(\n  onRequest: (options, handler) async {\n    final token = await secureStorage.read(key: 'auth_token');\n    if (token != null) options.headers['Authorization'] = 'Bearer $token';\n    handler.next(options);\n  },\n  onError: (error, handler) async {\n    \u002F\u002F Guard against infinite retry loops: only attempt refresh once per request\n    final isRetry = error.requestOptions.extra['_isRetry'] == true;\n    if (!isRetry && error.response?.statusCode == 401) {\n      final refreshed = await attemptTokenRefresh();\n      if (refreshed) {\n        error.requestOptions.extra['_isRetry'] = true;\n        return handler.resolve(await dio.fetch(error.requestOptions));\n      }\n    }\n    handler.next(error);\n  },\n));\n\n\u002F\u002F Repository using Dio\nclass UserApiDataSource {\n  const UserApiDataSource(this._dio);\n  final Dio _dio;\n\n  Future\u003CUser> getById(String id) async {\n    final response = await _dio.get\u003CMap\u003CString, dynamic>>('\u002Fusers\u002F$id');\n    return User.fromJson(response.data!);\n  }\n}\n```\n\n---\n\n## 9. Error Handling Architecture\n\n```dart\n\u002F\u002F Global error capture — set up in main()\nvoid main() {\n  FlutterError.onError = (details) {\n    FlutterError.presentError(details);\n    crashlytics.recordFlutterFatalError(details);\n  };\n\n  PlatformDispatcher.instance.onError = (error, stack) {\n    crashlytics.recordError(error, stack, fatal: true);\n    return true;\n  };\n\n  runApp(const App());\n}\n\n\u002F\u002F Custom ErrorWidget for production\nclass App extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    ErrorWidget.builder = (details) => ProductionErrorWidget(details);\n    return MaterialApp.router(routerConfig: router);\n  }\n}\n```\n\n---\n\n## 10. Testing Quick Reference\n\n```dart\n\u002F\u002F Unit test — use case\ntest('GetUserUseCase returns null for missing user', () async {\n  final repo = FakeUserRepository();\n  final useCase = GetUserUseCase(repo);\n  expect(await useCase('missing-id'), isNull);\n});\n\n\u002F\u002F BLoC test\nblocTest\u003CAuthCubit, AuthState>(\n  'emits loading then error on failed login',\n  build: () => AuthCubit(FakeAuthService(throwsOn: 'login')),\n  act: (cubit) => cubit.login('user@test.com', 'wrong'),\n  expect: () => [const AuthState.loading(), isA\u003CAuthError>()],\n);\n\n\u002F\u002F Widget test\ntestWidgets('CartBadge shows item count', (tester) async {\n  await tester.pumpWidget(\n    ProviderScope(\n      overrides: [cartNotifierProvider.overrideWith(() => FakeCartNotifier(count: 3))],\n      child: const MaterialApp(home: CartBadge()),\n    ),\n  );\n  expect(find.text('3'), findsOneWidget);\n});\n```\n\n---\n\n## References\n\n- [Effective Dart: Design](https:\u002F\u002Fdart.dev\u002Feffective-dart\u002Fdesign)\n- [Flutter Performance Best Practices](https:\u002F\u002Fdocs.flutter.dev\u002Fperf\u002Fbest-practices)\n- [Riverpod Documentation](https:\u002F\u002Friverpod.dev\u002F)\n- [BLoC Library](https:\u002F\u002Fbloclibrary.dev\u002F)\n- [GoRouter](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fgo_router)\n- [Freezed](https:\u002F\u002Fpub.dev\u002Fpackages\u002Ffreezed)\n- Skill: `flutter-dart-code-review` — comprehensive review checklist\n- Rules: `rules\u002Fdart\u002F` — coding style, patterns, security, testing, hooks\n",{"name":5,"tags":44,"tools":46,"source":47,"license":14,"description":7,"source_path":48,"source_commit":49},[29,21,45],"coding-standards",[],"https:\u002F\u002Fgithub.com\u002Faffaan-m\u002FECC","skills\u002Fdart-flutter-patterns","bf70150eb2df8070024e5bdf08e4aa08959e2735","main","f7cd4595d0f8959822163e14f9787117765c931a","packages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fdart-flutter-patterns",2,"https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind","https:\u002F\u002Fmonoes.github.io\u002Fmonomind\u002F","https:\u002F\u002Fgithub.com\u002Fmonoes","https:\u002F\u002Fmonoes.me","2026-11-10 01:53:48",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add monoes\u002Fmonomind --skill dart-flutter-patterns",{"tool":65,"command":66},"bunx","bunx skills add monoes\u002Fmonomind --skill dart-flutter-patterns",{"tool":68,"command":69},"pnpm","pnpm dlx skills add monoes\u002Fmonomind --skill dart-flutter-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fdart-flutter-patterns\u002FSKILL.md",true,[73],{"path":74,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":77,"sourceHtmlUrl":15},"LICENSE.txt","LICENSE","许可证",1071,[79,108,140,167,192,219],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":83,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":107},"01M71PB3SHWKMNJM6MD65VX8D6","zk-steward","Org role guidance for a ZK steward: engineer zero-knowledge proof systems with circuit design, SNARK vs STARK choice and security review. This is zero-knowledge cryptography, not Zettelkasten note-taking.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fzk-steward\u002FSKILL.md",[],[87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106],"zk","zk steward","steward","zero-knowledge","engineer","choice","design","knowledge","impact","query","snark","review","monomind","security review","stark","zettelkasten","cryptography","security","org","blockchain","2026-11-10 13:02:02",{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":112,"sourceHtmlUrl":113,"categories":114,"keywords":125,"publishedAt":107},"01M71PB18SDG28WBKPKJNB814K","writing-plans","Use when you have a spec or requirements for a multi-step coding task and must write an implementation plan before touching code. Produces bite-sized TDD tasks with exact file paths, code and commands for an engineer with zero context.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwriting-plans\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwriting-plans\u002FSKILL.md",[115,120],{"categoryNo":116,"categoryName":117,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":121,"categoryName":122,"taxonomyType":123,"taxonomyTypeDesc":124},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[33,126,127,91,128,129,32,130,96,31,131,132,133,134,135,136,137,25,138,139],"writing plans","plans","superpowers","implementation plan","you","tasks","plan","product","write","commands","paths","suggest","context","ZeRO",{"skillNo":141,"name":142,"nameZh":6,"description":143,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":83,"licenseFileUrl":144,"sourceHtmlUrl":145,"categories":146,"keywords":150,"publishedAt":107},"01M71PAXFM488X9ARXK0T04TF5","write-spec","Write a feature spec or PRD from a problem statement or feature idea. Use when turning a vague idea or user request into a structured document, scoping a feature with goals and non-goals, defining success metrics and acceptance criteria, or breaking a big ask into a phased spec.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-spec\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-spec\u002FSKILL.md",[147],{"categoryNo":148,"categoryName":149,"taxonomyType":123,"taxonomyTypeDesc":124},"01JBSEEDCAT000000000000031","产品经理",[134,151,152,153,154,94,155,32,156,157,158,133,159,160,161,162,163,164,165,166],"write spec","spec","user request","problem","acceptance","ask","vague","work","request","scoping","idea","metrics","feature","problem statement","use","document",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":83,"licenseFileUrl":171,"sourceHtmlUrl":172,"categories":173,"keywords":177,"publishedAt":107},"01M71PASSZY800C0RPEARBS918","write-query","Write optimized SQL for your dialect with best practices. Use when translating a natural-language data need into SQL, building a multi-CTE query with joins and aggregations, optimizing a query against a large partitioned table, or getting dialect-specific syntax for Snowflake, BigQuery, Postgres, etc.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-query\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-query\u002FSKILL.md",[174],{"categoryNo":175,"categoryName":176,"taxonomyType":123,"taxonomyTypeDesc":124},"01JBSEEDCAT000000000000033","数据分析师",[96,134,178,179,180,181,94,165,32,182,183,184,185,158,186,187,188,189,190,191],"write query","postgres","for","snowflake","best practices","natural-language","with","multi","github","sql","https","syntax","plugins","data",{"skillNo":193,"name":194,"nameZh":6,"description":195,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":83,"licenseFileUrl":6,"sourceHtmlUrl":196,"categories":197,"keywords":204,"publishedAt":107},"01M71PAPTX0T3PF6ETFVR96SJJ","workflow-automation","Org role guidance for workflow automation: build and maintain CI\u002FCD definitions in GitHub Actions or equivalent: triggers, jobs, matrices, caching. Covers path filters, dependency caching, parallel jobs, timeouts and fail-fast ordering.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fworkflow-automation\u002FSKILL.md",[198,201],{"categoryNo":199,"categoryName":200,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":202,"categoryName":203,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000023","Skill与智能体开发",[205,206,207,208,180,209,210,211,212,95,213,214,215,96,99,186,216,217,218,188],"Workflow Automation","automation","workflow","parallel","actions","ci","triggers","github actions","maintain","filters","caching","build","dependency","devops",{"skillNo":220,"name":221,"nameZh":6,"description":222,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":83,"licenseFileUrl":6,"sourceHtmlUrl":223,"categories":224,"keywords":226,"publishedAt":107},"01M71PAM19Z1XTV9NPCFSE2MY8","workflow-architect","Org role guidance for a workflow architect: map full workflow trees for systems, user journeys and agents, covering every branch and failure. Covers discovering workflows from code, handoff contracts, timeouts, recovery paths and observable state.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fworkflow-architect\u002FSKILL.md",[225],{"categoryNo":202,"categoryName":203,"taxonomyType":118,"taxonomyTypeDesc":119},[207,227,228,229,230,231,232,180,233,234,31,235,236,237,99,186,238,239,188,240],"workflow architect","architect","trees","user journeys","branch","planning","map","workflows","architecture","state","leadership","recovery","operations","systems"]