Flutter Development Guide A practical guide for building cross-platform applications with Flutter 3 and Dart. Focuses on proven patterns, state management, and performance optimization. Quick Reference Widget Patterns | Purpose | Component | |---------|-----------| | State management (simple) | + | | State management (complex) | / | | Async data | / | | Real-time streams | | | Navigation | + | | Responsive layout | + breakpoints | | List display | | | Complex scrolling | + Slivers | | Hooks | + | | Forms | + + validation | Performance Patterns | Purpose | Solution | |---------|----------| | P…

);\n if (!regex.hasMatch(value.trim())) {\n return 'Enter a valid email address';\n }\n return null;\n }\n\n static String? password(String? value) {\n if (value == null || value.isEmpty) {\n return 'Password is required';\n }\n if (value.length \u003c 8) {\n return 'Password must be at least 8 characters';\n }\n return null;\n }\n\n static String? strongPassword(String? value) {\n if (value == null || value.isEmpty) {\n return 'Password is required';\n }\n if (value.length \u003c 8) {\n return 'Password must be at least 8 characters';\n }\n if (!RegExp(r'[A-Z]').hasMatch(value)) {\n return 'Password must contain an uppercase letter';\n }\n if (!RegExp(r'[a-z]').hasMatch(value)) {\n return 'Password must contain a lowercase letter';\n }\n if (!RegExp(r'[0-9]').hasMatch(value)) {\n return 'Password must contain a number';\n }\n return null;\n }\n\n static String? phone(String? value) {\n if (value == null || value.trim().isEmpty) {\n return 'Phone number is required';\n }\n final digits = value.replaceAll(RegExp(r'\\D'), '');\n if (digits.length \u003c 10 || digits.length > 15) {\n return 'Enter a valid phone number';\n }\n return null;\n }\n\n static String? minLength(int min) {\n return (String? value) {\n if (value == null || value.length \u003c min) {\n return 'Must be at least $min characters';\n }\n return null;\n };\n }\n\n static String? maxLength(int max) {\n return (String? value) {\n if (value != null && value.length > max) {\n return 'Must be at most $max characters';\n }\n return null;\n };\n }\n\n static String? Function(String?) combine(List\u003cString? Function(String?)> validators) {\n return (String? value) {\n for (final validator in validators) {\n final error = validator(value);\n if (error != null) return error;\n }\n return null;\n };\n }\n\n static String? match(String pattern, String message) {\n return (String? value) {\n if (value != null && !RegExp(pattern).hasMatch(value)) {\n return message;\n }\n return null;\n };\n }\n\n static String? confirmPassword(TextEditingController passwordController) {\n return (String? value) {\n if (value != passwordController.text) {\n return 'Passwords do not match';\n }\n return null;\n };\n }\n}\n```\n\n## Input Formatters\n\n```dart\nimport 'package:flutter/services.dart';\n\nclass PhoneInputFormatter extends TextInputFormatter {\n @override\n TextEditingValue formatEditUpdate(\n TextEditingValue oldValue,\n TextEditingValue newValue,\n ) {\n final digits = newValue.text.replaceAll(RegExp(r'\\D'), '');\n final buffer = StringBuffer();\n\n for (int i = 0; i \u003c digits.length && i \u003c 10; i++) {\n if (i == 3 || i == 6) buffer.write('-');\n buffer.write(digits[i]);\n }\n\n return TextEditingValue(\n text: buffer.toString(),\n selection: TextSelection.collapsed(offset: buffer.length),\n );\n }\n}\n\nclass CreditCardFormatter extends TextInputFormatter {\n @override\n TextEditingValue formatEditUpdate(\n TextEditingValue oldValue,\n TextEditingValue newValue,\n ) {\n final digits = newValue.text.replaceAll(RegExp(r'\\D'), '');\n final buffer = StringBuffer();\n\n for (int i = 0; i \u003c digits.length && i \u003c 16; i++) {\n if (i > 0 && i % 4 == 0) buffer.write(' ');\n buffer.write(digits[i]);\n }\n\n return TextEditingValue(\n text: buffer.toString(),\n selection: TextSelection.collapsed(offset: buffer.length),\n );\n }\n}\n\nclass CurrencyInputFormatter extends TextInputFormatter {\n final int decimalPlaces;\n\n CurrencyInputFormatter({this.decimalPlaces = 2});\n\n @override\n TextEditingValue formatEditUpdate(\n TextEditingValue oldValue,\n TextEditingValue newValue,\n ) {\n if (newValue.text.isEmpty) return newValue;\n\n final digits = newValue.text.replaceAll(RegExp(r'[^\\d]'), '');\n if (digits.isEmpty) return const TextEditingValue(text: '');\n\n final value = int.parse(digits) / 100;\n final formatted = value.toStringAsFixed(decimalPlaces);\n\n return TextEditingValue(\n text: formatted,\n selection: TextSelection.collapsed(offset: formatted.length),\n );\n }\n}\n\nclass UpperCaseFormatter extends TextInputFormatter {\n @override\n TextEditingValue formatEditUpdate(\n TextEditingValue oldValue,\n TextEditingValue newValue,\n ) {\n return newValue.copyWith(text: newValue.text.toUpperCase());\n }\n}\n```\n\n### Using Formatters\n\n```dart\nTextFormField(\n decoration: const InputDecoration(labelText: 'Phone'),\n keyboardType: TextInputType.phone,\n inputFormatters: [\n FilteringTextInputFormatter.digitsOnly,\n PhoneInputFormatter(),\n ],\n)\n\nTextFormField(\n decoration: const InputDecoration(labelText: 'Amount'),\n keyboardType: const TextInputType.numberWithOptions(decimal: true),\n inputFormatters: [\n FilteringTextInputFormatter.allow(RegExp(r'[\\d.]')),\n CurrencyInputFormatter(),\n ],\n)\n```\n\n## Custom FormFields\n\n### Dropdown FormField\n\n```dart\nclass DropdownFormField\u003cT> extends FormField\u003cT> {\n DropdownFormField({\n super.key,\n required List\u003cDropdownMenuItem\u003cT>> items,\n super.initialValue,\n super.validator,\n super.onSaved,\n String? labelText,\n String? hintText,\n ValueChanged\u003cT?>? onChanged,\n }) : super(\n builder: (state) {\n return InputDecorator(\n decoration: InputDecoration(\n labelText: labelText,\n errorText: state.errorText,\n ),\n child: DropdownButtonHideUnderline(\n child: DropdownButton\u003cT>(\n value: state.value,\n hint: hintText != null ? Text(hintText) : null,\n isExpanded: true,\n items: items,\n onChanged: (value) {\n state.didChange(value);\n onChanged?.call(value);\n },\n ),\n ),\n );\n },\n );\n}\n```\n\n### Checkbox FormField\n\n```dart\nclass CheckboxFormField extends FormField\u003cbool> {\n CheckboxFormField({\n super.key,\n required Widget label,\n super.initialValue = false,\n super.validator,\n super.onSaved,\n }) : super(\n builder: (state) {\n return Column(\n crossAxisAlignment: CrossAxisAlignment.start,\n children: [\n Row(\n children: [\n Checkbox(\n value: state.value ?? false,\n onChanged: state.didChange,\n ),\n Expanded(child: GestureDetector(\n onTap: () => state.didChange(!(state.value ?? false)),\n child: label,\n )),\n ],\n ),\n if (state.hasError)\n Padding(\n padding: const EdgeInsets.only(left: 12, top: 4),\n child: Text(\n state.errorText!,\n style: TextStyle(\n color: Theme.of(state.context).colorScheme.error,\n fontSize: 12,\n ),\n ),\n ),\n ],\n );\n },\n );\n}\n```\n\n### Date Picker FormField\n\n```dart\nclass DatePickerFormField extends FormField\u003cDateTime> {\n DatePickerFormField({\n super.key,\n super.initialValue,\n super.validator,\n super.onSaved,\n String? labelText,\n DateTime? firstDate,\n DateTime? lastDate,\n }) : super(\n builder: (state) {\n return GestureDetector(\n onTap: () async {\n final picked = await showDatePicker(\n context: state.context,\n initialDate: state.value ?? DateTime.now(),\n firstDate: firstDate ?? DateTime(1900),\n lastDate: lastDate ?? DateTime(2100),\n );\n if (picked != null) {\n state.didChange(picked);\n }\n },\n child: InputDecorator(\n decoration: InputDecoration(\n labelText: labelText,\n errorText: state.errorText,\n suffixIcon: const Icon(Icons.calendar_today),\n ),\n child: Text(\n state.value != null\n ? DateFormat.yMMMd().format(state.value!)\n : 'Select date',\n ),\n ),\n );\n },\n );\n}\n```\n\n## Form with Hooks\n\n```dart\nimport 'package:flutter_hooks/flutter_hooks.dart';\n\nclass HookLoginForm extends HookWidget {\n const HookLoginForm({super.key});\n\n @override\n Widget build(BuildContext context) {\n final formKey = useMemoized(GlobalKey\u003cFormState>.new);\n final emailController = useTextEditingController();\n final passwordController = useTextEditingController();\n final emailFocus = useFocusNode();\n final passwordFocus = useFocusNode();\n final isLoading = useState(false);\n\n Future\u003cvoid> submit() async {\n if (!formKey.currentState!.validate()) return;\n\n isLoading.value = true;\n try {\n await authService.login(\n email: emailController.text.trim(),\n password: passwordController.text,\n );\n } finally {\n isLoading.value = false;\n }\n }\n\n return Form(\n key: formKey,\n child: Column(\n children: [\n TextFormField(\n controller: emailController,\n focusNode: emailFocus,\n decoration: const InputDecoration(labelText: 'Email'),\n textInputAction: TextInputAction.next,\n onFieldSubmitted: (_) => passwordFocus.requestFocus(),\n validator: Validators.email,\n ),\n const SizedBox(height: 16),\n TextFormField(\n controller: passwordController,\n focusNode: passwordFocus,\n decoration: const InputDecoration(labelText: 'Password'),\n obscureText: true,\n onFieldSubmitted: (_) => submit(),\n validator: Validators.password,\n ),\n const SizedBox(height: 24),\n ElevatedButton(\n onPressed: isLoading.value ? null : submit,\n child: isLoading.value\n ? const CircularProgressIndicator()\n : const Text('Login'),\n ),\n ],\n ),\n );\n }\n}\n```\n\n## Server-Side Validation\n\n```dart\nclass ServerValidationForm extends StatefulWidget {\n const ServerValidationForm({super.key});\n\n @override\n State\u003cServerValidationForm> createState() => _ServerValidationFormState();\n}\n\nclass _ServerValidationFormState extends State\u003cServerValidationForm> {\n final _formKey = GlobalKey\u003cFormState>();\n final _emailController = TextEditingController();\n Map\u003cString, List\u003cString>> _serverErrors = {};\n\n String? _emailValidator(String? value) {\n final clientError = Validators.email(value);\n if (clientError != null) return clientError;\n\n final serverError = _serverErrors['email'];\n if (serverError != null && serverError.isNotEmpty) {\n return serverError.first;\n }\n return null;\n }\n\n Future\u003cvoid> _submit() async {\n setState(() => _serverErrors = {});\n\n if (!_formKey.currentState!.validate()) return;\n\n try {\n await api.register(email: _emailController.text);\n } on ValidationException catch (e) {\n setState(() => _serverErrors = e.errors);\n _formKey.currentState!.validate();\n }\n }\n\n @override\n Widget build(BuildContext context) {\n return Form(\n key: _formKey,\n child: Column(\n children: [\n TextFormField(\n controller: _emailController,\n decoration: const InputDecoration(labelText: 'Email'),\n validator: _emailValidator,\n onChanged: (_) {\n if (_serverErrors.containsKey('email')) {\n setState(() => _serverErrors.remove('email'));\n }\n },\n ),\n ElevatedButton(\n onPressed: _submit,\n child: const Text('Register'),\n ),\n ],\n ),\n );\n }\n}\n```\n\n## Auto-Save Form\n\n```dart\nclass AutoSaveForm extends StatefulWidget {\n const AutoSaveForm({super.key});\n\n @override\n State\u003cAutoSaveForm> createState() => _AutoSaveFormState();\n}\n\nclass _AutoSaveFormState extends State\u003cAutoSaveForm> {\n final _formKey = GlobalKey\u003cFormState>();\n Timer? _debounce;\n bool _hasChanges = false;\n\n void _onChanged() {\n setState(() => _hasChanges = true);\n\n _debounce?.cancel();\n _debounce = Timer(const Duration(seconds: 2), _autoSave);\n }\n\n Future\u003cvoid> _autoSave() async {\n if (!_hasChanges) return;\n if (!_formKey.currentState!.validate()) return;\n\n _formKey.currentState!.save();\n await saveToServer();\n setState(() => _hasChanges = false);\n }\n\n @override\n void dispose() {\n _debounce?.cancel();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Form(\n key: _formKey,\n onChanged: _onChanged,\n child: Column(\n children: [\n if (_hasChanges)\n const Text('Saving...', style: TextStyle(color: Colors.grey)),\n TextFormField(\n decoration: const InputDecoration(labelText: 'Title'),\n onSaved: (value) => saveField('title', value),\n ),\n TextFormField(\n decoration: const InputDecoration(labelText: 'Description'),\n maxLines: 3,\n onSaved: (value) => saveField('description', value),\n ),\n ],\n ),\n );\n }\n}\n```\n\n## Common Keyboard Types\n\n| Type | Usage |\n|------|-------|\n| `TextInputType.text` | General text |\n| `TextInputType.emailAddress` | Email with @ keyboard |\n| `TextInputType.phone` | Phone number pad |\n| `TextInputType.number` | Numeric keyboard |\n| `TextInputType.numberWithOptions(decimal: true)` | Numbers with decimal |\n| `TextInputType.multiline` | Multi-line text |\n| `TextInputType.url` | URL with shortcuts |\n\n## Form Checklist\n\n| Item | Implementation |\n|------|----------------|\n| GlobalKey | `GlobalKey\u003cFormState>()` for form |\n| Dispose controllers | Clean up in `dispose()` |\n| Validation | Client + server-side |\n| Input formatters | Phone, currency, etc. |\n| Keyboard types | Match input type |\n| Text actions | `textInputAction` for flow |\n| Loading state | Disable during submission |\n| Error display | Show below fields |\n\n---\n\n*Flutter and Material Design are trademarks of Google LLC.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":17424,"content_sha256":"11d76fe7be4656221b89778a1c860a3575d3f4b9272b0c54dd0f344493e7c60d"},{"filename":"references/gorouter-navigation.md","content":"# GoRouter Navigation\n\nGoRouter navigation guide covering route setup, guards, deep linking, and shell routes.\n\n## Basic Setup\n\n```dart\nimport 'package:go_router/go_router.dart';\n\nfinal goRouter = GoRouter(\n initialLocation: '/',\n debugLogDiagnostics: true,\n redirect: (context, state) {\n final isLoggedIn = /* check auth state */;\n final isAuthRoute = state.matchedLocation.startsWith('/auth');\n \n if (!isLoggedIn && !isAuthRoute) {\n return '/auth/login';\n }\n if (isLoggedIn && isAuthRoute) {\n return '/';\n }\n return null;\n },\n routes: [\n GoRoute(\n path: '/',\n name: 'home',\n builder: (context, state) => const HomeScreen(),\n routes: [\n GoRoute(\n path: 'details/:id',\n name: 'details',\n builder: (context, state) {\n final id = state.pathParameters['id']!;\n final extra = state.extra as Map\u003cString, dynamic>?;\n return DetailsScreen(id: id, title: extra?['title']);\n },\n ),\n ],\n ),\n GoRoute(\n path: '/auth/login',\n name: 'login',\n builder: (context, state) => const LoginScreen(),\n ),\n ],\n);\n```\n\n### App Integration\n\n```dart\nclass MyApp extends StatelessWidget {\n const MyApp({super.key});\n\n @override\n Widget build(BuildContext context) {\n return MaterialApp.router(\n routerConfig: goRouter,\n theme: AppTheme.light,\n darkTheme: AppTheme.dark,\n themeMode: ThemeMode.system,\n );\n }\n}\n```\n\n## Navigation Methods\n\n```dart\n// Navigate and replace entire stack\ncontext.go('/details/123');\n\n// Navigate and add to stack (can go back)\ncontext.push('/details/123');\n\n// Go back\ncontext.pop();\n\n// Go back with result\ncontext.pop(result);\n\n// Replace current route\ncontext.pushReplacement('/home');\n\n// Navigate with extra data\ncontext.push('/details/123', extra: {'title': 'Item Title'});\n\n// Navigate by name\ncontext.goNamed('details', pathParameters: {'id': '123'});\ncontext.pushNamed('details', pathParameters: {'id': '123'}, extra: data);\n```\n\n### Navigation Reference\n\n| Method | Behavior |\n|--------|----------|\n| `context.go()` | Navigate, replace entire stack |\n| `context.push()` | Navigate, add to stack |\n| `context.pop()` | Go back one level |\n| `context.pushReplacement()` | Replace current route |\n| `context.goNamed()` | Navigate by route name |\n| `context.canPop()` | Check if can go back |\n\n## Shell Routes (Persistent UI)\n\n```dart\nfinal goRouter = GoRouter(\n routes: [\n ShellRoute(\n builder: (context, state, child) {\n return ScaffoldWithNavBar(child: child);\n },\n routes: [\n GoRoute(\n path: '/home',\n builder: (_, __) => const HomeScreen(),\n ),\n GoRoute(\n path: '/search',\n builder: (_, __) => const SearchScreen(),\n ),\n GoRoute(\n path: '/profile',\n builder: (_, __) => const ProfileScreen(),\n ),\n ],\n ),\n ],\n);\n\nclass ScaffoldWithNavBar extends StatelessWidget {\n final Widget child;\n \n const ScaffoldWithNavBar({super.key, required this.child});\n\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n body: child,\n bottomNavigationBar: NavigationBar(\n selectedIndex: _calculateSelectedIndex(context),\n onDestinationSelected: (index) => _onItemTapped(index, context),\n destinations: const [\n NavigationDestination(icon: Icon(Icons.home), label: 'Home'),\n NavigationDestination(icon: Icon(Icons.search), label: 'Search'),\n NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),\n ],\n ),\n );\n }\n \n int _calculateSelectedIndex(BuildContext context) {\n final location = GoRouterState.of(context).matchedLocation;\n if (location.startsWith('/home')) return 0;\n if (location.startsWith('/search')) return 1;\n if (location.startsWith('/profile')) return 2;\n return 0;\n }\n \n void _onItemTapped(int index, BuildContext context) {\n switch (index) {\n case 0: context.go('/home');\n case 1: context.go('/search');\n case 2: context.go('/profile');\n }\n }\n}\n```\n\n## Query Parameters\n\n```dart\nGoRoute(\n path: '/search',\n builder: (context, state) {\n final query = state.uri.queryParameters['q'] ?? '';\n final page = int.tryParse(state.uri.queryParameters['page'] ?? '1') ?? 1;\n return SearchScreen(query: query, page: page);\n },\n),\n\n// Navigate with query params\ncontext.go('/search?q=flutter&page=2');\ncontext.goNamed('search', queryParameters: {'q': 'flutter', 'page': '2'});\n```\n\n## Riverpod Integration\n\n```dart\nfinal routerProvider = Provider\u003cGoRouter>((ref) {\n final authState = ref.watch(authProvider);\n \n return GoRouter(\n refreshListenable: authState,\n redirect: (context, state) {\n final isLoggedIn = authState.isAuthenticated;\n final isAuthRoute = state.matchedLocation.startsWith('/auth');\n \n if (!isLoggedIn && !isAuthRoute) return '/auth/login';\n if (isLoggedIn && isAuthRoute) return '/';\n return null;\n },\n routes: [...],\n );\n});\n\n// In app.dart\nclass MyApp extends ConsumerWidget {\n const MyApp({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final router = ref.watch(routerProvider);\n return MaterialApp.router(routerConfig: router);\n }\n}\n```\n\n## Error Handling\n\n```dart\nfinal goRouter = GoRouter(\n errorBuilder: (context, state) {\n return ErrorScreen(error: state.error);\n },\n routes: [...],\n);\n```\n\n## Deep Linking\n\nDeep links work automatically when routes are configured with path parameters:\n\n```dart\n// URL: myapp://details/123\n// or: https://myapp.com/details/123\nGoRoute(\n path: '/details/:id',\n builder: (context, state) => DetailsScreen(id: state.pathParameters['id']!),\n),\n```\n\n## Best Practices\n\n| Do | Don't |\n|----|-------|\n| Use named routes for maintainability | Hardcode paths everywhere |\n| Use `push()` for detail screens | Use `go()` for all navigation |\n| Pass simple data via `extra` | Pass complex objects via URL |\n| Use redirect for auth guards | Check auth in every screen |\n| Use ShellRoute for persistent UI | Rebuild nav bar in every screen |\n\n---\n\n*GoRouter is an open-source navigation package for Flutter.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6244,"content_sha256":"0c71e90920777d89df7c1f4d5b32eaa4af3c85eaaee6fcda136ab231a8cbd016"},{"filename":"references/localization.md","content":"# Localization\n\nInternationalization (i18n) patterns using flutter_localizations and intl package for Flutter applications.\n\n## Setup\n\n### Dependencies\n\n```yaml\n# pubspec.yaml\ndependencies:\n flutter:\n sdk: flutter\n flutter_localizations:\n sdk: flutter\n intl: ^0.19.0\n\nflutter:\n generate: true\n```\n\n### l10n Configuration\n\n```yaml\n# l10n.yaml\narb-dir: lib/l10n\ntemplate-arb-file: app_en.arb\noutput-localization-file: app_localizations.dart\noutput-class: AppLocalizations\nnullable-getter: false\n```\n\n## ARB Files\n\n### English (Template)\n\n```json\n// lib/l10n/app_en.arb\n{\n \"@@locale\": \"en\",\n \"appTitle\": \"My App\",\n \"@appTitle\": {\n \"description\": \"The application title\"\n },\n \"hello\": \"Hello\",\n \"welcome\": \"Welcome, {name}!\",\n \"@welcome\": {\n \"description\": \"Welcome message with user name\",\n \"placeholders\": {\n \"name\": {\n \"type\": \"String\",\n \"example\": \"John\"\n }\n }\n },\n \"itemCount\": \"{count, plural, =0{No items} =1{1 item} other{{count} items}}\",\n \"@itemCount\": {\n \"description\": \"Number of items\",\n \"placeholders\": {\n \"count\": {\n \"type\": \"int\"\n }\n }\n },\n \"lastUpdated\": \"Last updated: {date}\",\n \"@lastUpdated\": {\n \"description\": \"Last update timestamp\",\n \"placeholders\": {\n \"date\": {\n \"type\": \"DateTime\",\n \"format\": \"yMMMd\"\n }\n }\n },\n \"price\": \"Price: {amount}\",\n \"@price\": {\n \"description\": \"Product price\",\n \"placeholders\": {\n \"amount\": {\n \"type\": \"double\",\n \"format\": \"currency\",\n \"optionalParameters\": {\n \"symbol\": \"$\",\n \"decimalDigits\": 2\n }\n }\n }\n },\n \"gender\": \"{gender, select, male{He} female{She} other{They}} liked this\",\n \"@gender\": {\n \"description\": \"Gender-specific message\",\n \"placeholders\": {\n \"gender\": {\n \"type\": \"String\"\n }\n }\n }\n}\n```\n\n### Chinese\n\n```json\n// lib/l10n/app_zh.arb\n{\n \"@@locale\": \"zh\",\n \"appTitle\": \"我的应用\",\n \"hello\": \"你好\",\n \"welcome\": \"欢迎,{name}!\",\n \"itemCount\": \"{count, plural, =0{没有项目} other{{count} 个项目}}\",\n \"lastUpdated\": \"最后更新:{date}\",\n \"price\": \"价格:{amount}\",\n \"gender\": \"{gender, select, male{他} female{她} other{Ta}}喜欢了这个\"\n}\n```\n\n### Japanese\n\n```json\n// lib/l10n/app_ja.arb\n{\n \"@@locale\": \"ja\",\n \"appTitle\": \"マイアプリ\",\n \"hello\": \"こんにちは\",\n \"welcome\": \"ようこそ、{name}さん!\",\n \"itemCount\": \"{count, plural, =0{アイテムなし} other{{count}件}}\",\n \"lastUpdated\": \"最終更新:{date}\",\n \"price\": \"価格:{amount}\",\n \"gender\": \"{gender, select, male{彼} female{彼女} other{その人}}がいいねしました\"\n}\n```\n\n## App Configuration\n\n```dart\nimport 'package:flutter_localizations/flutter_localizations.dart';\nimport 'package:flutter_gen/gen_l10n/app_localizations.dart';\n\nclass MyApp extends StatelessWidget {\n const MyApp({super.key});\n\n @override\n Widget build(BuildContext context) {\n return MaterialApp(\n title: 'My App',\n localizationsDelegates: const [\n AppLocalizations.delegate,\n GlobalMaterialLocalizations.delegate,\n GlobalWidgetsLocalizations.delegate,\n GlobalCupertinoLocalizations.delegate,\n ],\n supportedLocales: const [\n Locale('en'),\n Locale('zh'),\n Locale('ja'),\n ],\n locale: const Locale('en'),\n home: const HomePage(),\n );\n }\n}\n```\n\n## Using Translations\n\n```dart\nimport 'package:flutter_gen/gen_l10n/app_localizations.dart';\n\nclass HomePage extends StatelessWidget {\n const HomePage({super.key});\n\n @override\n Widget build(BuildContext context) {\n final l10n = AppLocalizations.of(context);\n\n return Scaffold(\n appBar: AppBar(title: Text(l10n.appTitle)),\n body: Column(\n children: [\n Text(l10n.hello),\n Text(l10n.welcome('John')),\n Text(l10n.itemCount(5)),\n Text(l10n.lastUpdated(DateTime.now())),\n Text(l10n.price(29.99)),\n Text(l10n.gender('female')),\n ],\n ),\n );\n }\n}\n```\n\n### Extension for Convenience\n\n```dart\nextension LocalizationExtension on BuildContext {\n AppLocalizations get l10n => AppLocalizations.of(this);\n}\n\n// Usage\nText(context.l10n.hello)\n```\n\n## Dynamic Locale Switching\n\n### With Riverpod\n\n```dart\n@riverpod\nclass LocaleNotifier extends _$LocaleNotifier {\n @override\n Locale build() {\n final saved = ref.watch(sharedPreferencesProvider).getString('locale');\n if (saved != null) {\n return Locale(saved);\n }\n return const Locale('en');\n }\n\n void setLocale(Locale locale) {\n ref.read(sharedPreferencesProvider).setString('locale', locale.languageCode);\n state = locale;\n }\n}\n\nclass MyApp extends ConsumerWidget {\n const MyApp({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final locale = ref.watch(localeNotifierProvider);\n\n return MaterialApp(\n localizationsDelegates: AppLocalizations.localizationsDelegates,\n supportedLocales: AppLocalizations.supportedLocales,\n locale: locale,\n home: const HomePage(),\n );\n }\n}\n```\n\n### Language Selector\n\n```dart\nclass LanguageSelector extends ConsumerWidget {\n const LanguageSelector({super.key});\n\n static const languages = [\n (Locale('en'), 'English'),\n (Locale('zh'), '中文'),\n (Locale('ja'), '日本語'),\n ];\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final currentLocale = ref.watch(localeNotifierProvider);\n\n return PopupMenuButton\u003cLocale>(\n initialValue: currentLocale,\n onSelected: (locale) {\n ref.read(localeNotifierProvider.notifier).setLocale(locale);\n },\n itemBuilder: (context) => languages.map((lang) {\n return PopupMenuItem(\n value: lang.$1,\n child: Row(\n children: [\n if (currentLocale == lang.$1)\n const Icon(Icons.check, size: 18),\n const SizedBox(width: 8),\n Text(lang.$2),\n ],\n ),\n );\n }).toList(),\n child: const Icon(Icons.language),\n );\n }\n}\n```\n\n## Date and Number Formatting\n\n```dart\nimport 'package:intl/intl.dart';\n\nclass FormattingUtils {\n static String formatDate(DateTime date, String locale) {\n return DateFormat.yMMMd(locale).format(date);\n }\n\n static String formatDateTime(DateTime dateTime, String locale) {\n return DateFormat.yMMMd(locale).add_jm().format(dateTime);\n }\n\n static String formatRelativeTime(DateTime dateTime, String locale) {\n final now = DateTime.now();\n final diff = now.difference(dateTime);\n\n if (diff.inDays > 7) {\n return DateFormat.yMMMd(locale).format(dateTime);\n } else if (diff.inDays > 0) {\n return '${diff.inDays}d ago';\n } else if (diff.inHours > 0) {\n return '${diff.inHours}h ago';\n } else if (diff.inMinutes > 0) {\n return '${diff.inMinutes}m ago';\n } else {\n return 'Just now';\n }\n }\n\n static String formatCurrency(double amount, String locale, {String? symbol}) {\n return NumberFormat.currency(\n locale: locale,\n symbol: symbol,\n decimalDigits: 2,\n ).format(amount);\n }\n\n static String formatNumber(num number, String locale) {\n return NumberFormat.decimalPattern(locale).format(number);\n }\n\n static String formatPercent(double value, String locale) {\n return NumberFormat.percentPattern(locale).format(value);\n }\n\n static String formatCompact(num number, String locale) {\n return NumberFormat.compact(locale: locale).format(number);\n }\n}\n```\n\n### Usage with Locale\n\n```dart\nclass FormattedContent extends StatelessWidget {\n const FormattedContent({super.key});\n\n @override\n Widget build(BuildContext context) {\n final locale = Localizations.localeOf(context).toString();\n\n return Column(\n children: [\n Text(FormattingUtils.formatDate(DateTime.now(), locale)),\n Text(FormattingUtils.formatCurrency(1234.56, locale, symbol: '\\

Flutter Development Guide A practical guide for building cross-platform applications with Flutter 3 and Dart. Focuses on proven patterns, state management, and performance optimization. Quick Reference Widget Patterns | Purpose | Component | |---------|-----------| | State management (simple) | + | | State management (complex) | / | | Async data | / | | Real-time streams | | | Navigation | + | | Responsive layout | + breakpoints | | List display | | | Complex scrolling | + Slivers | | Hooks | + | | Forms | + + validation | Performance Patterns | Purpose | Solution | |---------|----------| | P…

)),\n Text(FormattingUtils.formatNumber(1234567, locale)),\n Text(FormattingUtils.formatPercent(0.75, locale)),\n Text(FormattingUtils.formatCompact(1500000, locale)),\n ],\n );\n }\n}\n```\n\n## RTL Support\n\n```dart\nclass RtlAwareWidget extends StatelessWidget {\n const RtlAwareWidget({super.key});\n\n @override\n Widget build(BuildContext context) {\n final isRtl = Directionality.of(context) == TextDirection.rtl;\n\n return Row(\n children: [\n Icon(isRtl ? Icons.arrow_back : Icons.arrow_forward),\n const Expanded(child: Text('Content')),\n Padding(\n padding: EdgeInsetsDirectional.only(start: 16),\n child: const Icon(Icons.settings),\n ),\n ],\n );\n }\n}\n```\n\n### Directional Widgets\n\n| Standard | Directional |\n|----------|-------------|\n| `EdgeInsets` | `EdgeInsetsDirectional` |\n| `Padding` | `Padding` with `EdgeInsetsDirectional` |\n| `Align` | `AlignmentDirectional` |\n| `Positioned` | `PositionedDirectional` |\n| `BorderRadius` | `BorderRadiusDirectional` |\n\n```dart\n// Use directional\nPadding(\n padding: const EdgeInsetsDirectional.only(start: 16, end: 8),\n child: child,\n)\n\nContainer(\n alignment: AlignmentDirectional.centerStart,\n child: child,\n)\n\nContainer(\n decoration: const BoxDecoration(\n borderRadius: BorderRadiusDirectional.only(\n topStart: Radius.circular(8),\n bottomStart: Radius.circular(8),\n ),\n ),\n)\n```\n\n## Organized Translations\n\n### Split by Feature\n\n```\nlib/\n l10n/\n app_en.arb # Common translations\n app_zh.arb\n features/\n auth_en.arb # Auth feature translations\n auth_zh.arb\n settings_en.arb # Settings feature translations\n settings_zh.arb\n```\n\n### Namespaced Keys\n\n```json\n// app_en.arb\n{\n \"auth_login\": \"Login\",\n \"auth_logout\": \"Logout\",\n \"auth_forgotPassword\": \"Forgot Password?\",\n\n \"settings_title\": \"Settings\",\n \"settings_language\": \"Language\",\n \"settings_theme\": \"Theme\",\n\n \"error_network\": \"Network error. Please try again.\",\n \"error_unknown\": \"An unknown error occurred.\"\n}\n```\n\n## Testing\n\n```dart\nvoid main() {\n testWidgets('shows localized text', (tester) async {\n await tester.pumpWidget(\n MaterialApp(\n localizationsDelegates: AppLocalizations.localizationsDelegates,\n supportedLocales: AppLocalizations.supportedLocales,\n locale: const Locale('en'),\n home: const HomePage(),\n ),\n );\n\n expect(find.text('Hello'), findsOneWidget);\n });\n\n testWidgets('switches locale', (tester) async {\n await tester.pumpWidget(\n ProviderScope(\n child: MaterialApp(\n localizationsDelegates: AppLocalizations.localizationsDelegates,\n supportedLocales: AppLocalizations.supportedLocales,\n locale: const Locale('zh'),\n home: const HomePage(),\n ),\n ),\n );\n\n expect(find.text('你好'), findsOneWidget);\n });\n}\n```\n\n## ARB Placeholders Reference\n\n| Type | Format Options |\n|------|----------------|\n| `String` | None |\n| `int` | `compact`, `compactCurrency`, `compactLong`, `compactSimpleCurrency` |\n| `double` | `compact`, `compactCurrency`, `currency`, `decimalPattern`, `decimalPercentPattern`, `percentPattern`, `scientificPattern`, `simpleCurrency` |\n| `DateTime` | Any `DateFormat` pattern (yMd, yMMMd, jm, etc.) |\n| `num` | Same as `int` and `double` |\n\n## Localization Checklist\n\n| Item | Implementation |\n|------|----------------|\n| Dependencies | `flutter_localizations`, `intl` |\n| l10n.yaml | Configure ARB paths and output |\n| ARB files | Create for each supported locale |\n| App config | Add delegates and supported locales |\n| Generate | Run `flutter gen-l10n` |\n| Use translations | `AppLocalizations.of(context)` |\n| Date/number formatting | Use `intl` formatters with locale |\n| RTL support | Use directional widgets |\n| Persist preference | Save user's locale choice |\n| Testing | Test with different locales |\n\n---\n\n*Flutter is a trademark of Google LLC. intl is an open-source package by the Dart team.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11992,"content_sha256":"d711c335b235db35c8e7faed1c2871e66ae65d08e16b40cc404fd231a6adca31"},{"filename":"references/networking.md","content":"# Networking\n\nDio configuration, interceptors, error handling, and caching strategies for Flutter network requests.\n\n## Dio Setup\n\n```dart\nimport 'package:dio/dio.dart';\n\nclass ApiClient {\n static final ApiClient _instance = ApiClient._internal();\n factory ApiClient() => _instance;\n\n late final Dio dio;\n\n ApiClient._internal() {\n dio = Dio(BaseOptions(\n baseUrl: 'https://api.example.com/v1',\n connectTimeout: const Duration(seconds: 10),\n receiveTimeout: const Duration(seconds: 30),\n sendTimeout: const Duration(seconds: 30),\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n },\n ));\n\n dio.interceptors.addAll([\n AuthInterceptor(),\n LoggingInterceptor(),\n RetryInterceptor(dio: dio),\n ]);\n }\n}\n```\n\n## Interceptors\n\n### Auth Interceptor\n\n```dart\nclass AuthInterceptor extends Interceptor {\n final TokenStorage _tokenStorage;\n\n AuthInterceptor({TokenStorage? tokenStorage})\n : _tokenStorage = tokenStorage ?? TokenStorage();\n\n @override\n void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {\n final token = await _tokenStorage.getAccessToken();\n if (token != null) {\n options.headers['Authorization'] = 'Bearer $token';\n }\n handler.next(options);\n }\n\n @override\n void onError(DioException err, ErrorInterceptorHandler handler) async {\n if (err.response?.statusCode == 401) {\n try {\n final newToken = await _refreshToken();\n if (newToken != null) {\n err.requestOptions.headers['Authorization'] = 'Bearer $newToken';\n final response = await Dio().fetch(err.requestOptions);\n return handler.resolve(response);\n }\n } catch (e) {\n await _tokenStorage.clearTokens();\n }\n }\n handler.next(err);\n }\n\n Future\u003cString?> _refreshToken() async {\n final refreshToken = await _tokenStorage.getRefreshToken();\n if (refreshToken == null) return null;\n\n final response = await Dio().post(\n 'https://api.example.com/v1/auth/refresh',\n data: {'refresh_token': refreshToken},\n );\n\n if (response.statusCode == 200) {\n final newToken = response.data['access_token'];\n await _tokenStorage.saveAccessToken(newToken);\n return newToken;\n }\n return null;\n }\n}\n```\n\n### Logging Interceptor\n\n```dart\nclass LoggingInterceptor extends Interceptor {\n @override\n void onRequest(RequestOptions options, RequestInterceptorHandler handler) {\n debugPrint('→ ${options.method} ${options.uri}');\n if (options.data != null) {\n debugPrint(' Body: ${options.data}');\n }\n handler.next(options);\n }\n\n @override\n void onResponse(Response response, ResponseInterceptorHandler handler) {\n debugPrint('← ${response.statusCode} ${response.requestOptions.uri}');\n handler.next(response);\n }\n\n @override\n void onError(DioException err, ErrorInterceptorHandler handler) {\n debugPrint('✗ ${err.response?.statusCode} ${err.requestOptions.uri}');\n debugPrint(' Error: ${err.message}');\n handler.next(err);\n }\n}\n```\n\n### Retry Interceptor\n\n```dart\nclass RetryInterceptor extends Interceptor {\n final Dio dio;\n final int maxRetries;\n final Duration retryDelay;\n\n RetryInterceptor({\n required this.dio,\n this.maxRetries = 3,\n this.retryDelay = const Duration(seconds: 1),\n });\n\n @override\n void onError(DioException err, ErrorInterceptorHandler handler) async {\n final retryCount = err.requestOptions.extra['retryCount'] ?? 0;\n\n if (_shouldRetry(err) && retryCount \u003c maxRetries) {\n await Future.delayed(retryDelay * (retryCount + 1));\n\n err.requestOptions.extra['retryCount'] = retryCount + 1;\n\n try {\n final response = await dio.fetch(err.requestOptions);\n return handler.resolve(response);\n } catch (e) {\n return handler.next(err);\n }\n }\n\n handler.next(err);\n }\n\n bool _shouldRetry(DioException err) {\n return err.type == DioExceptionType.connectionTimeout ||\n err.type == DioExceptionType.sendTimeout ||\n err.type == DioExceptionType.receiveTimeout ||\n (err.response?.statusCode ?? 0) >= 500;\n }\n}\n```\n\n## Error Handling\n\n### Custom Exception\n\n```dart\nsealed class ApiException implements Exception {\n final String message;\n final int? statusCode;\n final dynamic data;\n\n const ApiException({\n required this.message,\n this.statusCode,\n this.data,\n });\n}\n\nclass NetworkException extends ApiException {\n const NetworkException({super.message = 'Network connection failed'});\n}\n\nclass ServerException extends ApiException {\n const ServerException({\n required super.message,\n super.statusCode,\n super.data,\n });\n}\n\nclass UnauthorizedException extends ApiException {\n const UnauthorizedException({super.message = 'Authentication required'});\n}\n\nclass ValidationException extends ApiException {\n final Map\u003cString, List\u003cString>> errors;\n\n const ValidationException({\n required this.errors,\n super.message = 'Validation failed',\n });\n}\n```\n\n### Error Handler\n\n```dart\nclass ApiErrorHandler {\n static ApiException handle(DioException error) {\n switch (error.type) {\n case DioExceptionType.connectionTimeout:\n case DioExceptionType.sendTimeout:\n case DioExceptionType.receiveTimeout:\n return const NetworkException(message: 'Connection timeout');\n\n case DioExceptionType.connectionError:\n return const NetworkException(message: 'No internet connection');\n\n case DioExceptionType.badResponse:\n return _handleResponse(error.response);\n\n case DioExceptionType.cancel:\n return const ApiException(message: 'Request cancelled');\n\n default:\n return ApiException(message: error.message ?? 'Unknown error');\n }\n }\n\n static ApiException _handleResponse(Response? response) {\n final statusCode = response?.statusCode ?? 0;\n final data = response?.data;\n\n switch (statusCode) {\n case 400:\n if (data is Map && data.containsKey('errors')) {\n return ValidationException(\n errors: Map\u003cString, List\u003cString>>.from(\n (data['errors'] as Map).map(\n (k, v) => MapEntry(k.toString(), List\u003cString>.from(v)),\n ),\n ),\n );\n }\n return ServerException(\n message: data?['message'] ?? 'Bad request',\n statusCode: statusCode,\n );\n\n case 401:\n return const UnauthorizedException();\n\n case 403:\n return const ServerException(\n message: 'Access denied',\n statusCode: 403,\n );\n\n case 404:\n return const ServerException(\n message: 'Resource not found',\n statusCode: 404,\n );\n\n case 422:\n return ValidationException(\n errors: _parseValidationErrors(data),\n );\n\n case >= 500:\n return ServerException(\n message: 'Server error',\n statusCode: statusCode,\n );\n\n default:\n return ServerException(\n message: data?['message'] ?? 'Unknown error',\n statusCode: statusCode,\n );\n }\n }\n\n static Map\u003cString, List\u003cString>> _parseValidationErrors(dynamic data) {\n if (data is! Map) return {};\n final errors = data['errors'];\n if (errors is! Map) return {};\n return errors.map((k, v) => MapEntry(\n k.toString(),\n v is List ? v.map((e) => e.toString()).toList() : [v.toString()],\n ));\n }\n}\n```\n\n## Repository Pattern\n\n```dart\nabstract class BaseRepository {\n final Dio dio;\n\n BaseRepository(this.dio);\n\n Future\u003cT> safeCall\u003cT>(Future\u003cT> Function() call) async {\n try {\n return await call();\n } on DioException catch (e) {\n throw ApiErrorHandler.handle(e);\n }\n }\n}\n\nclass UserRepository extends BaseRepository {\n UserRepository(super.dio);\n\n Future\u003cUser> getUser(String id) => safeCall(() async {\n final response = await dio.get('/users/$id');\n return User.fromJson(response.data);\n });\n\n Future\u003cList\u003cUser>> getUsers({int page = 1, int limit = 20}) => safeCall(() async {\n final response = await dio.get('/users', queryParameters: {\n 'page': page,\n 'limit': limit,\n });\n return (response.data['data'] as List)\n .map((e) => User.fromJson(e))\n .toList();\n });\n\n Future\u003cUser> updateUser(String id, Map\u003cString, dynamic> data) => safeCall(() async {\n final response = await dio.patch('/users/$id', data: data);\n return User.fromJson(response.data);\n });\n}\n```\n\n## Caching\n\n### Memory Cache\n\n```dart\nclass CacheInterceptor extends Interceptor {\n final Map\u003cString, CacheEntry> _cache = {};\n final Duration maxAge;\n\n CacheInterceptor({this.maxAge = const Duration(minutes: 5)});\n\n @override\n void onRequest(RequestOptions options, RequestInterceptorHandler handler) {\n if (options.method != 'GET') {\n handler.next(options);\n return;\n }\n\n final key = _cacheKey(options);\n final cached = _cache[key];\n\n if (cached != null && !cached.isExpired) {\n return handler.resolve(Response(\n requestOptions: options,\n data: cached.data,\n statusCode: 200,\n ));\n }\n\n handler.next(options);\n }\n\n @override\n void onResponse(Response response, ResponseInterceptorHandler handler) {\n if (response.requestOptions.method == 'GET') {\n final key = _cacheKey(response.requestOptions);\n _cache[key] = CacheEntry(\n data: response.data,\n expiry: DateTime.now().add(maxAge),\n );\n }\n handler.next(response);\n }\n\n String _cacheKey(RequestOptions options) {\n return '${options.uri}';\n }\n\n void invalidate(String pattern) {\n _cache.removeWhere((key, _) => key.contains(pattern));\n }\n\n void clear() => _cache.clear();\n}\n\nclass CacheEntry {\n final dynamic data;\n final DateTime expiry;\n\n CacheEntry({required this.data, required this.expiry});\n\n bool get isExpired => DateTime.now().isAfter(expiry);\n}\n```\n\n### Disk Cache with Hive\n\n```dart\nimport 'package:hive_flutter/hive_flutter.dart';\n\nclass DiskCacheInterceptor extends Interceptor {\n static const String _boxName = 'api_cache';\n final Duration maxAge;\n\n DiskCacheInterceptor({this.maxAge = const Duration(hours: 1)});\n\n @override\n void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {\n if (options.method != 'GET') {\n handler.next(options);\n return;\n }\n\n final box = await Hive.openBox(_boxName);\n final key = _cacheKey(options);\n final cached = box.get(key);\n\n if (cached != null) {\n final entry = CachedResponse.fromJson(cached);\n if (!entry.isExpired) {\n return handler.resolve(Response(\n requestOptions: options,\n data: entry.data,\n statusCode: 200,\n ));\n }\n }\n\n handler.next(options);\n }\n\n @override\n void onResponse(Response response, ResponseInterceptorHandler handler) async {\n if (response.requestOptions.method == 'GET') {\n final box = await Hive.openBox(_boxName);\n final key = _cacheKey(response.requestOptions);\n await box.put(key, CachedResponse(\n data: response.data,\n expiry: DateTime.now().add(maxAge),\n ).toJson());\n }\n handler.next(response);\n }\n\n String _cacheKey(RequestOptions options) => options.uri.toString();\n}\n\nclass CachedResponse {\n final dynamic data;\n final DateTime expiry;\n\n CachedResponse({required this.data, required this.expiry});\n\n bool get isExpired => DateTime.now().isAfter(expiry);\n\n factory CachedResponse.fromJson(Map\u003cString, dynamic> json) {\n return CachedResponse(\n data: json['data'],\n expiry: DateTime.parse(json['expiry']),\n );\n }\n\n Map\u003cString, dynamic> toJson() => {\n 'data': data,\n 'expiry': expiry.toIso8601String(),\n };\n}\n```\n\n## Riverpod Integration\n\n```dart\n@riverpod\nDio dio(Ref ref) {\n return ApiClient().dio;\n}\n\n@riverpod\nUserRepository userRepository(Ref ref) {\n return UserRepository(ref.watch(dioProvider));\n}\n\n@riverpod\nFuture\u003cUser> user(Ref ref, String id) async {\n final repository = ref.watch(userRepositoryProvider);\n return repository.getUser(id);\n}\n\n@riverpod\nclass Users extends _$Users {\n @override\n Future\u003cList\u003cUser>> build() => _fetch();\n\n Future\u003cList\u003cUser>> _fetch() async {\n final repository = ref.watch(userRepositoryProvider);\n return repository.getUsers();\n }\n\n Future\u003cvoid> refresh() async {\n state = const AsyncLoading();\n state = await AsyncValue.guard(_fetch);\n }\n}\n```\n\n## Request Cancellation\n\n```dart\nclass SearchRepository extends BaseRepository {\n CancelToken? _searchToken;\n\n SearchRepository(super.dio);\n\n Future\u003cList\u003cSearchResult>> search(String query) async {\n _searchToken?.cancel();\n _searchToken = CancelToken();\n\n return safeCall(() async {\n final response = await dio.get(\n '/search',\n queryParameters: {'q': query},\n cancelToken: _searchToken,\n );\n return (response.data as List)\n .map((e) => SearchResult.fromJson(e))\n .toList();\n });\n }\n}\n```\n\n## Common Patterns\n\n| Pattern | Usage |\n|---------|-------|\n| Singleton client | Single Dio instance across app |\n| Interceptor chain | Auth → Retry → Cache → Logging |\n| Repository layer | Abstract API from business logic |\n| Error mapping | Convert DioException to app exceptions |\n| Cancel tokens | Debounce/cancel previous requests |\n| Cache invalidation | Clear cache on mutations |\n\n## Networking Checklist\n\n| Item | Implementation |\n|------|----------------|\n| Base configuration | Timeouts, headers, base URL |\n| Auth handling | Token injection, refresh on 401 |\n| Error handling | Typed exceptions, user messages |\n| Retry logic | Exponential backoff for transient errors |\n| Request logging | Debug interceptor |\n| Caching | Memory/disk cache for GET requests |\n| Cancellation | Cancel tokens for search/debounce |\n\n---\n\n*Dio is an open-source package by the Flutter China community.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13946,"content_sha256":"d6bded9a7b561d859883cf70eb0e64858bde38526be22856487f09ece1de848a"},{"filename":"references/performance.md","content":"# Performance Optimization\n\nFlutter performance guide covering profiling, const optimization, and DevTools analysis.\n\n## Profiling Commands\n\n```bash\n# Run in profile mode (required for accurate measurements)\nflutter run --profile\n\n# Analyze code issues\nflutter analyze\n\n# Launch DevTools\nflutter pub global activate devtools\nflutter pub global run devtools\n\n# Build release for testing\nflutter build apk --release\nflutter build ios --release\n```\n\n## Const Widget Optimization\n\nThe most important optimization for preventing unnecessary rebuilds:\n\n```dart\n// BAD - Creates new objects every build\nWidget build(BuildContext context) {\n return Container(\n padding: EdgeInsets.all(16), // New object each time\n child: Text('Hello'), // New widget each time\n );\n}\n\n// GOOD - Const prevents rebuilds\nWidget build(BuildContext context) {\n return Container(\n padding: const EdgeInsets.all(16),\n child: const Text('Hello'),\n );\n}\n```\n\n### Extracting Const Widgets\n\n```dart\n// BAD - Inline static content\nclass MyScreen extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Icon(Icons.star, size: 48),\n Text('Welcome'),\n Text('Description text here'),\n ],\n );\n }\n}\n\n// GOOD - Extract to const classes\nclass MyScreen extends StatelessWidget {\n const MyScreen({super.key});\n\n @override\n Widget build(BuildContext context) {\n return const Column(\n children: [\n _Header(),\n _Description(),\n ],\n );\n }\n}\n\nclass _Header extends StatelessWidget {\n const _Header();\n\n @override\n Widget build(BuildContext context) {\n return const Column(\n children: [\n Icon(Icons.star, size: 48),\n Text('Welcome'),\n ],\n );\n }\n}\n```\n\n## Selective Provider Watching\n\n```dart\n// BAD - Rebuilds on any user change\nclass UserAvatar extends ConsumerWidget {\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final user = ref.watch(userProvider);\n return CircleAvatar(\n backgroundImage: NetworkImage(user.avatarUrl),\n );\n }\n}\n\n// GOOD - Only rebuilds when avatarUrl changes\nclass UserAvatar extends ConsumerWidget {\n const UserAvatar({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final avatarUrl = ref.watch(userProvider.select((u) => u.avatarUrl));\n return CircleAvatar(\n backgroundImage: NetworkImage(avatarUrl),\n );\n }\n}\n```\n\n## RepaintBoundary\n\nIsolate expensive widgets to prevent unnecessary repaints:\n\n```dart\n// Isolate complex animated widgets\nRepaintBoundary(\n child: ComplexAnimatedWidget(),\n)\n\n// Isolate frequently updating widgets\nRepaintBoundary(\n child: StreamBuilder\u003cint>(\n stream: counterStream,\n builder: (context, snapshot) => Text('${snapshot.data}'),\n ),\n)\n```\n\n## List Optimization\n\n```dart\n// BAD - Builds all items upfront\nListView(\n children: items.map((item) => ItemWidget(item: item)).toList(),\n)\n\n// GOOD - Lazy loading with builder\nListView.builder(\n itemCount: items.length,\n itemBuilder: (context, index) {\n return ItemWidget(\n key: ValueKey(items[index].id),\n item: items[index],\n );\n },\n)\n\n// For heterogeneous content\nListView.separated(\n itemCount: items.length,\n separatorBuilder: (_, __) => const Divider(),\n itemBuilder: (context, index) => ItemWidget(item: items[index]),\n)\n```\n\n## Image Optimization\n\n```dart\n// Use cached_network_image for network images\nCachedNetworkImage(\n imageUrl: url,\n placeholder: (_, __) => const ShimmerPlaceholder(),\n errorWidget: (_, __, ___) => const Icon(Icons.error),\n memCacheWidth: 200,\n memCacheHeight: 200,\n)\n\n// Resize images in memory\nImage.network(\n url,\n cacheWidth: 200, // Decode at smaller size\n cacheHeight: 200, // Saves memory\n)\n\n// Precache images\nprecacheImage(NetworkImage(url), context);\n```\n\n## Heavy Computation\n\n```dart\n// BAD - Blocks UI thread\nvoid processData() {\n final result = heavyComputation(data); // UI freezes\n updateUI(result);\n}\n\n// GOOD - Run in isolate\nFuture\u003cvoid> processData() async {\n final result = await compute(heavyComputation, data);\n updateUI(result);\n}\n\n// For multiple operations\nFuture\u003cvoid> processMultiple() async {\n final results = await Future.wait([\n compute(process1, data1),\n compute(process2, data2),\n compute(process3, data3),\n ]);\n}\n```\n\n## Animation Performance\n\n```dart\n// Use AnimatedBuilder for custom animations\nAnimatedBuilder(\n animation: controller,\n builder: (context, child) {\n return Transform.rotate(\n angle: controller.value * 2 * pi,\n child: child, // Child not rebuilt\n );\n },\n child: const ExpensiveWidget(),\n)\n\n// Prefer implicit animations for simple cases\nAnimatedContainer(\n duration: const Duration(milliseconds: 300),\n width: expanded ? 200 : 100,\n child: const Content(),\n)\n```\n\n## DevTools Analysis\n\n### Key Metrics\n\n| Metric | Target | Action if Exceeded |\n|--------|--------|-------------------|\n| Frame time | \u003c 16ms (60fps) | Profile build/paint |\n| Build time | \u003c 8ms | Add const, extract widgets |\n| Paint time | \u003c 8ms | Add RepaintBoundary |\n| Memory | Stable | Check for leaks |\n\n### Common Issues\n\n| Issue | Symptom | Solution |\n|-------|---------|----------|\n| Expensive builds | High build time | Extract const widgets |\n| Excessive repaints | High paint time | Add RepaintBoundary |\n| Memory leaks | Growing memory | Dispose controllers |\n| Jank | Dropped frames | Use compute() |\n\n## Performance Checklist\n\n| Check | Solution |\n|-------|----------|\n| Unnecessary rebuilds | Add `const`, use `select()` |\n| Large lists | Use `ListView.builder` |\n| Image loading | Use `cached_network_image` |\n| Heavy computation | Use `compute()` |\n| Jank in animations | Use `RepaintBoundary` |\n| Memory leaks | Dispose controllers, cancel subscriptions |\n| Network calls | Cache responses, debounce requests |\n| Startup time | Defer initialization, lazy loading |\n\n## Dispose Pattern\n\n```dart\nclass MyWidget extends StatefulWidget {\n const MyWidget({super.key});\n\n @override\n State\u003cMyWidget> createState() => _MyWidgetState();\n}\n\nclass _MyWidgetState extends State\u003cMyWidget> {\n late final TextEditingController _controller;\n late final StreamSubscription _subscription;\n\n @override\n void initState() {\n super.initState();\n _controller = TextEditingController();\n _subscription = stream.listen(handleData);\n }\n\n @override\n void dispose() {\n _controller.dispose();\n _subscription.cancel();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) => Container();\n}\n```\n\n---\n\n*Flutter and DevTools are trademarks of Google LLC.*\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6634,"content_sha256":"2f5c557c6c7218dffafb3d56b0e569020c03ba6f187df96f3c66f1b1ae1e11b1"},{"filename":"references/platform-specific.md","content":"# Platform Integration\n\nFlutter platform-specific implementations for iOS, Android, Web, and Desktop.\n\n## Platform Detection\n\n```dart\nimport 'dart:io' show Platform;\nimport 'package:flutter/foundation.dart' show kIsWeb;\n\nbool get isIOS => !kIsWeb && Platform.isIOS;\nbool get isAndroid => !kIsWeb && Platform.isAndroid;\nbool get isWeb => kIsWeb;\nbool get isDesktop => !kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);\nbool get isMobile => !kIsWeb && (Platform.isIOS || Platform.isAndroid);\n```\n\n## Adaptive Widgets\n\n### Platform-Aware Components\n\n```dart\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\n\nclass AdaptiveButton extends StatelessWidget {\n final String label;\n final VoidCallback onPressed;\n\n const AdaptiveButton({\n super.key,\n required this.label,\n required this.onPressed,\n });\n\n @override\n Widget build(BuildContext context) {\n if (Platform.isIOS) {\n return CupertinoButton.filled(\n onPressed: onPressed,\n child: Text(label),\n );\n }\n return ElevatedButton(\n onPressed: onPressed,\n child: Text(label),\n );\n }\n}\n```\n\n### Adaptive Dialog\n\n```dart\nFuture\u003cbool?> showAdaptiveConfirmDialog(\n BuildContext context, {\n required String title,\n required String content,\n}) async {\n if (Platform.isIOS) {\n return showCupertinoDialog\u003cbool>(\n context: context,\n builder: (context) => CupertinoAlertDialog(\n title: Text(title),\n content: Text(content),\n actions: [\n CupertinoDialogAction(\n isDestructiveAction: true,\n onPressed: () => Navigator.pop(context, true),\n child: const Text('Delete'),\n ),\n CupertinoDialogAction(\n isDefaultAction: true,\n onPressed: () => Navigator.pop(context, false),\n child: const Text('Cancel'),\n ),\n ],\n ),\n );\n }\n\n return showDialog\u003cbool>(\n context: context,\n builder: (context) => AlertDialog(\n title: Text(title),\n content: Text(content),\n actions: [\n TextButton(\n onPressed: () => Navigator.pop(context, false),\n child: const Text('Cancel'),\n ),\n TextButton(\n onPressed: () => Navigator.pop(context, true),\n child: const Text('Delete'),\n ),\n ],\n ),\n );\n}\n```\n\n### Adaptive Scaffold\n\n```dart\nclass AdaptiveScaffold extends StatelessWidget {\n final String title;\n final Widget body;\n final List\u003cWidget>? actions;\n\n const AdaptiveScaffold({\n super.key,\n required this.title,\n required this.body,\n this.actions,\n });\n\n @override\n Widget build(BuildContext context) {\n if (Platform.isIOS) {\n return CupertinoPageScaffold(\n navigationBar: CupertinoNavigationBar(\n middle: Text(title),\n trailing: actions != null\n ? Row(mainAxisSize: MainAxisSize.min, children: actions!)\n : null,\n ),\n child: SafeArea(child: body),\n );\n }\n\n return Scaffold(\n appBar: AppBar(title: Text(title), actions: actions),\n body: body,\n );\n }\n}\n```\n\n## Platform Channels\n\n### Method Channel (Dart Side)\n\n```dart\nimport 'package:flutter/services.dart';\n\nclass NativeBridge {\n static const _channel = MethodChannel('com.example.app/native');\n\n static Future\u003cString> getPlatformVersion() async {\n final version = await _channel.invokeMethod\u003cString>('getPlatformVersion');\n return version ?? 'Unknown';\n }\n\n static Future\u003cvoid> triggerHaptic() async {\n await _channel.invokeMethod('triggerHaptic');\n }\n\n static Future\u003cMap\u003cString, dynamic>> getDeviceInfo() async {\n final result = await _channel.invokeMethod\u003cMap>('getDeviceInfo');\n return Map\u003cString, dynamic>.from(result ?? {});\n }\n}\n```\n\n### iOS Implementation (Swift)\n\n```swift\n// ios/Runner/AppDelegate.swift\nimport Flutter\nimport UIKit\n\n@main\n@objc class AppDelegate: FlutterAppDelegate {\n override func application(\n _ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n ) -> Bool {\n let controller = window?.rootViewController as! FlutterViewController\n let channel = FlutterMethodChannel(\n name: \"com.example.app/native\",\n binaryMessenger: controller.binaryMessenger\n )\n \n channel.setMethodCallHandler { (call, result) in\n switch call.method {\n case \"getPlatformVersion\":\n result(\"iOS \" + UIDevice.current.systemVersion)\n case \"triggerHaptic\":\n let generator = UIImpactFeedbackGenerator(style: .medium)\n generator.impactOccurred()\n result(nil)\n case \"getDeviceInfo\":\n result([\n \"model\": UIDevice.current.model,\n \"name\": UIDevice.current.name,\n \"systemVersion\": UIDevice.current.systemVersion\n ])\n default:\n result(FlutterMethodNotImplemented)\n }\n }\n \n GeneratedPluginRegistrant.register(with: self)\n return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n }\n}\n```\n\n### Android Implementation (Kotlin)\n\n```kotlin\n// android/app/src/main/kotlin/.../MainActivity.kt\npackage com.example.app\n\nimport android.os.Build\nimport android.os.VibrationEffect\nimport android.os.Vibrator\nimport android.content.Context\nimport io.flutter.embedding.android.FlutterActivity\nimport io.flutter.embedding.engine.FlutterEngine\nimport io.flutter.plugin.common.MethodChannel\n\nclass MainActivity: FlutterActivity() {\n private val CHANNEL = \"com.example.app/native\"\n\n override fun configureFlutterEngine(flutterEngine: FlutterEngine) {\n super.configureFlutterEngine(flutterEngine)\n \n MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)\n .setMethodCallHandler { call, result ->\n when (call.method) {\n \"getPlatformVersion\" -> {\n result.success(\"Android ${Build.VERSION.RELEASE}\")\n }\n \"triggerHaptic\" -> {\n val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator\n if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n vibrator.vibrate(\n VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE)\n )\n } else {\n @Suppress(\"DEPRECATION\")\n vibrator.vibrate(50)\n }\n result.success(null)\n }\n \"getDeviceInfo\" -> {\n result.success(mapOf(\n \"model\" to Build.MODEL,\n \"manufacturer\" to Build.MANUFACTURER,\n \"version\" to Build.VERSION.RELEASE\n ))\n }\n else -> result.notImplemented()\n }\n }\n }\n}\n```\n\n## iOS-Specific Configuration\n\n### Info.plist Permissions\n\n```xml\n\u003c!-- ios/Runner/Info.plist -->\n\u003ckey>NSCameraUsageDescription\u003c/key>\n\u003cstring>This app needs camera access to take photos\u003c/string>\n\n\u003ckey>NSPhotoLibraryUsageDescription\u003c/key>\n\u003cstring>This app needs photo library access to save images\u003c/string>\n\n\u003ckey>NSLocationWhenInUseUsageDescription\u003c/key>\n\u003cstring>This app needs location access to show nearby places\u003c/string>\n\n\u003ckey>NSMicrophoneUsageDescription\u003c/key>\n\u003cstring>This app needs microphone access for voice recording\u003c/string>\n```\n\n### iOS App Icons and Launch Screen\n\n```\nios/Runner/Assets.xcassets/\n├── AppIcon.appiconset/\n│ ├── Contents.json\n│ └── Icon-App-*.png\n└── LaunchImage.imageset/\n ├── Contents.json\n └── LaunchImage*.png\n```\n\n## Android-Specific Configuration\n\n### AndroidManifest.xml Permissions\n\n```xml\n\u003c!-- android/app/src/main/AndroidManifest.xml -->\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003cuses-permission android:name=\"android.permission.INTERNET\"/>\n \u003cuses-permission android:name=\"android.permission.CAMERA\"/>\n \u003cuses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>\n \u003cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n \u003cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n \n \u003capplication\n android:label=\"My App\"\n android:icon=\"@mipmap/ic_launcher\">\n \u003c!-- ... -->\n \u003c/application>\n\u003c/manifest>\n```\n\n### Build Gradle Configuration\n\n```groovy\n// android/app/build.gradle\nandroid {\n compileSdkVersion 34\n \n defaultConfig {\n minSdkVersion 21\n targetSdkVersion 34\n multiDexEnabled true\n }\n \n buildTypes {\n release {\n signingConfig signingConfigs.release\n minifyEnabled true\n proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n }\n }\n}\n```\n\n## Web-Specific\n\n### Conditional Imports\n\n```dart\n// lib/services/storage_service.dart\nexport 'storage_service_stub.dart'\n if (dart.library.io) 'storage_service_native.dart'\n if (dart.library.html) 'storage_service_web.dart';\n```\n\n```dart\n// lib/services/storage_service_web.dart\nimport 'dart:html' as html;\n\nclass StorageService {\n void save(String key, String value) {\n html.window.localStorage[key] = value;\n }\n \n String? load(String key) {\n return html.window.localStorage[key];\n }\n}\n```\n\n### Web Index Configuration\n\n```html\n\u003c!-- web/index.html -->\n\u003c!DOCTYPE html>\n\u003chtml>\n\u003chead>\n \u003cmeta charset=\"UTF-8\">\n \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n \u003ctitle>My App\u003c/title>\n \u003clink rel=\"manifest\" href=\"manifest.json\">\n \u003clink rel=\"icon\" type=\"image/png\" href=\"favicon.png\"/>\n\u003c/head>\n\u003cbody>\n \u003cscript src=\"flutter_bootstrap.js\" async>\u003c/script>\n\u003c/body>\n\u003c/html>\n```\n\n## Platform-Specific Styling\n\n```dart\nThemeData get theme {\n final baseTheme = ThemeData(\n colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),\n useMaterial3: true,\n );\n\n if (Platform.isIOS) {\n return baseTheme.copyWith(\n // iOS-style page transitions\n pageTransitionsTheme: const PageTransitionsTheme(\n builders: {\n TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),\n },\n ),\n );\n }\n\n return baseTheme;\n}\n```\n\n## Platform Reference\n\n| Feature | iOS | Android | Web |\n|---------|-----|---------|-----|\n| Navigation | Cupertino style | Material style | URL-based |\n| Haptics | UIFeedbackGenerator | Vibrator | Not available |\n| Storage | NSUserDefaults | SharedPreferences | localStorage |\n| Deep links | Universal Links | App Links | URL routing |\n| Notifications | APNs | FCM | Web Push |\n\n---\n\n*Flutter, iOS, Android, and their respective logos are trademarks of Google LLC and Apple Inc.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11049,"content_sha256":"f0d8c283400ab00586e5bd8e68de3af2f35dde31841a0f9cd0ea1dba47b414ce"},{"filename":"references/project-structure.md","content":"# Project Structure\n\nFlutter project architecture guide covering feature-based structure, dependencies, and entry point setup.\n\n## Feature-Based Structure\n\n```\nlib/\n├── main.dart # Entry point\n├── app.dart # App widget, MaterialApp.router\n├── core/\n│ ├── constants/\n│ │ ├── app_colors.dart\n│ │ ├── app_strings.dart\n│ │ └── app_sizes.dart\n│ ├── theme/\n│ │ ├── app_theme.dart\n│ │ └── text_styles.dart\n│ ├── utils/\n│ │ ├── extensions.dart\n│ │ └── validators.dart\n│ └── errors/\n│ └── failures.dart\n├── features/\n│ ├── auth/\n│ │ ├── data/\n│ │ │ ├── repositories/\n│ │ │ │ └── auth_repository_impl.dart\n│ │ │ └── datasources/\n│ │ │ ├── auth_remote_datasource.dart\n│ │ │ └── auth_local_datasource.dart\n│ │ ├── domain/\n│ │ │ ├── entities/\n│ │ │ │ └── user.dart\n│ │ │ ├── repositories/\n│ │ │ │ └── auth_repository.dart\n│ │ │ └── usecases/\n│ │ │ ├── login.dart\n│ │ │ └── logout.dart\n│ │ ├── presentation/\n│ │ │ ├── screens/\n│ │ │ │ ├── login_screen.dart\n│ │ │ │ └── register_screen.dart\n│ │ │ └── widgets/\n│ │ │ └── auth_form.dart\n│ │ └── providers/\n│ │ └── auth_provider.dart\n│ └── home/\n│ ├── data/\n│ ├── domain/\n│ ├── presentation/\n│ └── providers/\n├── shared/\n│ ├── widgets/\n│ │ ├── buttons/\n│ │ │ └── primary_button.dart\n│ │ ├── inputs/\n│ │ │ └── text_input.dart\n│ │ └── cards/\n│ │ └── info_card.dart\n│ ├── services/\n│ │ ├── api_service.dart\n│ │ └── storage_service.dart\n│ └── models/\n│ └── api_response.dart\n└── routes/\n └── app_router.dart\n```\n\n## Feature Layer Responsibilities\n\n| Layer | Responsibility |\n|-------|----------------|\n| **data/** | API calls, local storage, DTOs, repository implementations |\n| **domain/** | Business logic, entities, abstract repositories, use cases |\n| **presentation/** | UI screens, widgets, view logic |\n| **providers/** | Riverpod providers or Bloc definitions |\n\n## pubspec.yaml Essentials\n\n```yaml\nname: my_app\ndescription: A Flutter application.\nversion: 1.0.0+1\npublish_to: 'none'\n\nenvironment:\n sdk: '>=3.3.0 \u003c4.0.0'\n\ndependencies:\n flutter:\n sdk: flutter\n \n # State Management (choose one)\n flutter_riverpod: ^2.5.0\n riverpod_annotation: ^2.3.0\n # OR\n flutter_bloc: ^8.1.0\n \n # Navigation\n go_router: ^14.0.0\n \n # Networking\n dio: ^5.4.0\n \n # Code Generation\n freezed_annotation: ^2.4.0\n json_annotation: ^4.9.0\n \n # Storage\n shared_preferences: ^2.2.0\n hive_flutter: ^1.1.0\n \n # Utilities\n flutter_hooks: ^0.20.0\n cached_network_image: ^3.3.0\n intl: ^0.19.0\n\ndev_dependencies:\n flutter_test:\n sdk: flutter\n \n # Code Generation\n build_runner: ^2.4.0\n riverpod_generator: ^2.4.0\n freezed: ^2.5.0\n json_serializable: ^6.8.0\n \n # Linting\n flutter_lints: ^4.0.0\n \n # Testing\n bloc_test: ^9.1.0\n mocktail: ^1.0.0\n\nflutter:\n uses-material-design: true\n```\n\n## Main Entry Point\n\n```dart\n// main.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\n\nvoid main() async {\n WidgetsFlutterBinding.ensureInitialized();\n \n // Initialize services\n await Hive.initFlutter();\n \n runApp(\n const ProviderScope(\n child: MyApp(),\n ),\n );\n}\n```\n\n```dart\n// app.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass MyApp extends ConsumerWidget {\n const MyApp({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final router = ref.watch(routerProvider);\n\n return MaterialApp.router(\n title: 'My App',\n routerConfig: router,\n theme: AppTheme.light,\n darkTheme: AppTheme.dark,\n themeMode: ThemeMode.system,\n debugShowCheckedModeBanner: false,\n );\n }\n}\n```\n\n## Router Provider\n\n```dart\n// routes/app_router.dart\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:go_router/go_router.dart';\n\nfinal routerProvider = Provider\u003cGoRouter>((ref) {\n return GoRouter(\n initialLocation: '/',\n debugLogDiagnostics: true,\n redirect: (context, state) {\n // Auth guard logic\n return null;\n },\n routes: [\n GoRoute(\n path: '/',\n name: 'home',\n builder: (context, state) => const HomeScreen(),\n ),\n // Add more routes\n ],\n );\n});\n```\n\n## Environment Configuration\n\n```dart\n// core/constants/environment.dart\nenum Environment { dev, staging, prod }\n\nclass EnvConfig {\n static Environment current = Environment.dev;\n \n static String get baseUrl {\n switch (current) {\n case Environment.dev:\n return 'https://dev-api.example.com';\n case Environment.staging:\n return 'https://staging-api.example.com';\n case Environment.prod:\n return 'https://api.example.com';\n }\n }\n}\n```\n\n## Dependency Injection with Riverpod\n\n```dart\n// shared/services/api_service.dart\nfinal apiServiceProvider = Provider\u003cApiService>((ref) {\n final dio = Dio(BaseOptions(\n baseUrl: EnvConfig.baseUrl,\n connectTimeout: const Duration(seconds: 10),\n receiveTimeout: const Duration(seconds: 10),\n ));\n \n // Add interceptors\n dio.interceptors.add(AuthInterceptor(ref));\n dio.interceptors.add(LogInterceptor(responseBody: true));\n \n return ApiService(dio);\n});\n\n// features/auth/providers/auth_provider.dart\nfinal authRepositoryProvider = Provider\u003cAuthRepository>((ref) {\n final api = ref.watch(apiServiceProvider);\n final storage = ref.watch(storageServiceProvider);\n return AuthRepositoryImpl(api: api, storage: storage);\n});\n```\n\n## Best Practices\n\n| Practice | Description |\n|----------|-------------|\n| Feature isolation | Each feature is self-contained |\n| Dependency inversion | Domain depends on abstractions |\n| Single responsibility | One class, one purpose |\n| Naming conventions | Clear, descriptive names |\n| Barrel exports | One index.dart per folder |\n\n---\n\n*Flutter is a trademark of Google LLC.*\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6723,"content_sha256":"3d45de13d573e740db1e98877dc0ae29d02af5d877f853205d024a1c32f23588"},{"filename":"references/riverpod-state.md","content":"# Riverpod State Management\n\nRiverpod 2.0 state management guide covering provider types, notifier patterns, and widget integration.\n\n## Provider Types\n\n```dart\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\n// Simple computed value\nfinal greetingProvider = Provider\u003cString>((ref) {\n final name = ref.watch(userNameProvider);\n return 'Hello, $name';\n});\n\n// Simple mutable state\nfinal counterProvider = StateProvider\u003cint>((ref) => 0);\n\n// Async state (API calls)\nfinal usersProvider = FutureProvider\u003cList\u003cUser>>((ref) async {\n final api = ref.read(apiProvider);\n return api.getUsers();\n});\n\n// Stream state (real-time)\nfinal messagesProvider = StreamProvider\u003cList\u003cMessage>>((ref) {\n return ref.read(chatServiceProvider).messagesStream;\n});\n```\n\n### Provider Type Reference\n\n| Provider | Use Case |\n|----------|----------|\n| `Provider` | Computed/derived values, dependency injection |\n| `StateProvider` | Simple mutable state (counter, toggle) |\n| `FutureProvider` | Async operations (one-time fetch) |\n| `StreamProvider` | Real-time data streams |\n| `NotifierProvider` | Complex state with methods |\n| `AsyncNotifierProvider` | Async state with methods |\n\n## Notifier Pattern (Riverpod 2.0)\n\n### Synchronous Notifier\n\n```dart\n@riverpod\nclass TodoList extends _$TodoList {\n @override\n List\u003cTodo> build() => [];\n\n void add(Todo todo) {\n state = [...state, todo];\n }\n\n void toggle(String id) {\n state = [\n for (final todo in state)\n if (todo.id == id) \n todo.copyWith(completed: !todo.completed) \n else \n todo,\n ];\n }\n\n void remove(String id) {\n state = state.where((t) => t.id != id).toList();\n }\n}\n```\n\n### Async Notifier\n\n```dart\n@riverpod\nclass UserProfile extends _$UserProfile {\n @override\n Future\u003cUser> build() async {\n return ref.read(apiProvider).getCurrentUser();\n }\n\n Future\u003cvoid> updateName(String name) async {\n state = const AsyncValue.loading();\n state = await AsyncValue.guard(() async {\n final updated = await ref.read(apiProvider).updateUser(name: name);\n return updated;\n });\n }\n\n Future\u003cvoid> refresh() async {\n ref.invalidateSelf();\n await future;\n }\n}\n```\n\n## Usage in Widgets\n\n### ConsumerWidget (Recommended)\n\n```dart\nclass TodoScreen extends ConsumerWidget {\n const TodoScreen({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final todos = ref.watch(todoListProvider);\n\n return ListView.builder(\n itemCount: todos.length,\n itemBuilder: (context, index) {\n final todo = todos[index];\n return ListTile(\n key: ValueKey(todo.id),\n title: Text(todo.title),\n leading: Checkbox(\n value: todo.completed,\n onChanged: (_) => ref.read(todoListProvider.notifier).toggle(todo.id),\n ),\n );\n },\n );\n }\n}\n```\n\n### Selective Rebuilds with select\n\n```dart\nclass UserAvatar extends ConsumerWidget {\n const UserAvatar({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n // Only rebuilds when avatarUrl changes\n final avatarUrl = ref.watch(userProvider.select((u) => u?.avatarUrl));\n\n return CircleAvatar(\n backgroundImage: avatarUrl != null ? NetworkImage(avatarUrl) : null,\n );\n }\n}\n```\n\n### Async State Handling\n\n```dart\nclass UserProfileScreen extends ConsumerWidget {\n const UserProfileScreen({super.key});\n\n @override\n Widget build(BuildContext context, WidgetRef ref) {\n final userAsync = ref.watch(userProfileProvider);\n\n return userAsync.when(\n data: (user) => UserProfileContent(user: user),\n loading: () => const Center(child: CircularProgressIndicator()),\n error: (err, stack) => ErrorView(\n message: err.toString(),\n onRetry: () => ref.invalidate(userProfileProvider),\n ),\n );\n }\n}\n```\n\n### Consumer for Scoped Rebuilds\n\n```dart\nclass MyScreen extends StatelessWidget {\n const MyScreen({super.key});\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n const Text('Static content'),\n Consumer(\n builder: (context, ref, child) {\n final count = ref.watch(counterProvider);\n return Text('Count: $count');\n },\n ),\n ],\n );\n }\n}\n```\n\n## Provider Modifiers\n\n```dart\n// Auto-dispose when no longer used\n@riverpod\nclass AutoDisposeExample extends _$AutoDisposeExample {\n @override\n String build() => 'value';\n}\n\n// Family - parameterized providers\n@riverpod\nFuture\u003cUser> userById(UserByIdRef ref, String id) async {\n return ref.read(apiProvider).getUser(id);\n}\n\n// Usage\nfinal user = ref.watch(userByIdProvider('123'));\n```\n\n## Best Practices\n\n| Do | Don't |\n|----|-------|\n| Use `ref.watch()` in build | Use `ref.watch()` in callbacks |\n| Use `ref.read()` in callbacks | Use `ref.read()` in build |\n| Use `select()` for granular rebuilds | Watch entire state unnecessarily |\n| Create new state instances | Mutate state directly |\n| Use `AsyncValue.guard()` for errors | Catch errors manually |\n\n## Quick Reference\n\n| Method | When to Use |\n|--------|-------------|\n| `ref.watch()` | In build method, rebuilds on change |\n| `ref.read()` | In callbacks, one-time read |\n| `ref.listen()` | Side effects on change |\n| `ref.invalidate()` | Force provider refresh |\n| `ref.refresh()` | Invalidate and get new value |\n\n---\n\n*Riverpod is an open-source state management library by Remi Rousselet.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5450,"content_sha256":"4633cba2912145032641d5921765d34335f9763f28a8c9629559ffb4cfcba227"},{"filename":"references/testing.md","content":"# Testing Strategies\n\nFlutter testing guide covering widget tests, unit tests, integration tests, and mocking patterns.\n\n## Test Types\n\n| Type | Purpose | Speed | Scope |\n|------|---------|-------|-------|\n| Unit tests | Business logic, utilities | Fast | Single function/class |\n| Widget tests | UI components | Medium | Single widget |\n| Integration tests | Full user flows | Slow | Multiple screens |\n\n## Widget Tests\n\n### Basic Widget Test\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n testWidgets('Counter increments when button tapped', (tester) async {\n await tester.pumpWidget(const MaterialApp(home: CounterScreen()));\n\n // Verify initial state\n expect(find.text('0'), findsOneWidget);\n expect(find.text('1'), findsNothing);\n\n // Tap the increment button\n await tester.tap(find.byIcon(Icons.add));\n await tester.pump();\n\n // Verify state changed\n expect(find.text('0'), findsNothing);\n expect(find.text('1'), findsOneWidget);\n });\n}\n```\n\n### Testing with Riverpod\n\n```dart\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n testWidgets('displays user name from provider', (tester) async {\n await tester.pumpWidget(\n ProviderScope(\n overrides: [\n userProvider.overrideWithValue(\n AsyncValue.data(User(name: 'Test User')),\n ),\n ],\n child: const MaterialApp(home: UserScreen()),\n ),\n );\n\n expect(find.text('Test User'), findsOneWidget);\n });\n\n testWidgets('shows loading indicator', (tester) async {\n await tester.pumpWidget(\n ProviderScope(\n overrides: [\n userProvider.overrideWithValue(const AsyncValue.loading()),\n ],\n child: const MaterialApp(home: UserScreen()),\n ),\n );\n\n expect(find.byType(CircularProgressIndicator), findsOneWidget);\n });\n\n testWidgets('shows error message', (tester) async {\n await tester.pumpWidget(\n ProviderScope(\n overrides: [\n userProvider.overrideWithValue(\n AsyncValue.error('Network error', StackTrace.current),\n ),\n ],\n child: const MaterialApp(home: UserScreen()),\n ),\n );\n\n expect(find.text('Network error'), findsOneWidget);\n });\n}\n```\n\n### Testing with Bloc\n\n```dart\nimport 'package:bloc_test/bloc_test.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:mocktail/mocktail.dart';\n\nclass MockCounterBloc extends MockBloc\u003cCounterEvent, CounterState>\n implements CounterBloc {}\n\nvoid main() {\n late MockCounterBloc mockBloc;\n\n setUp(() {\n mockBloc = MockCounterBloc();\n });\n\n testWidgets('displays current count', (tester) async {\n when(() => mockBloc.state).thenReturn(const CounterState(value: 42));\n\n await tester.pumpWidget(\n MaterialApp(\n home: BlocProvider\u003cCounterBloc>.value(\n value: mockBloc,\n child: const CounterScreen(),\n ),\n ),\n );\n\n expect(find.text('42'), findsOneWidget);\n });\n\n testWidgets('calls increment on button tap', (tester) async {\n when(() => mockBloc.state).thenReturn(const CounterState(value: 0));\n\n await tester.pumpWidget(\n MaterialApp(\n home: BlocProvider\u003cCounterBloc>.value(\n value: mockBloc,\n child: const CounterScreen(),\n ),\n ),\n );\n\n await tester.tap(find.byIcon(Icons.add));\n\n verify(() => mockBloc.add(CounterIncremented())).called(1);\n });\n}\n```\n\n## Bloc Tests\n\n```dart\nimport 'package:bloc_test/bloc_test.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:mocktail/mocktail.dart';\n\nclass MockUserRepository extends Mock implements UserRepository {}\n\nvoid main() {\n late MockUserRepository mockRepository;\n\n setUp(() {\n mockRepository = MockUserRepository();\n });\n\n group('UserBloc', () {\n blocTest\u003cUserBloc, UserState>(\n 'emits loading then success when user loaded',\n setUp: () {\n when(() => mockRepository.getUser())\n .thenAnswer((_) async => User(name: 'Test'));\n },\n build: () => UserBloc(repository: mockRepository),\n act: (bloc) => bloc.add(UserRequested()),\n expect: () => [\n const UserState(status: UserStatus.loading),\n UserState(status: UserStatus.success, user: User(name: 'Test')),\n ],\n );\n\n blocTest\u003cUserBloc, UserState>(\n 'emits loading then failure when error occurs',\n setUp: () {\n when(() => mockRepository.getUser())\n .thenThrow(Exception('Network error'));\n },\n build: () => UserBloc(repository: mockRepository),\n act: (bloc) => bloc.add(UserRequested()),\n expect: () => [\n const UserState(status: UserStatus.loading),\n isA\u003cUserState>()\n .having((s) => s.status, 'status', UserStatus.failure),\n ],\n );\n });\n}\n```\n\n## Unit Tests\n\n```dart\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n group('Validator', () {\n test('returns error for empty email', () {\n expect(Validator.email(''), 'Email is required');\n });\n\n test('returns error for invalid email', () {\n expect(Validator.email('invalid'), 'Invalid email format');\n });\n\n test('returns null for valid email', () {\n expect(Validator.email('[email protected]'), isNull);\n });\n });\n\n group('Calculator', () {\n late Calculator calculator;\n\n setUp(() {\n calculator = Calculator();\n });\n\n test('adds two numbers', () {\n expect(calculator.add(2, 3), 5);\n });\n\n test('throws on division by zero', () {\n expect(() => calculator.divide(10, 0), throwsArgumentError);\n });\n });\n}\n```\n\n## Mocking with Mocktail\n\n```dart\nimport 'package:mocktail/mocktail.dart';\n\n// Create mock classes\nclass MockApiService extends Mock implements ApiService {}\nclass MockStorageService extends Mock implements StorageService {}\n\n// Register fallback values for complex types\nsetUpAll(() {\n registerFallbackValue(User(name: 'fallback'));\n});\n\nvoid main() {\n late MockApiService mockApi;\n\n setUp(() {\n mockApi = MockApiService();\n });\n\n test('fetches user from API', () async {\n // Arrange\n when(() => mockApi.getUser(any()))\n .thenAnswer((_) async => User(name: 'Test'));\n\n // Act\n final repository = UserRepository(api: mockApi);\n final user = await repository.getUser('123');\n\n // Assert\n expect(user.name, 'Test');\n verify(() => mockApi.getUser('123')).called(1);\n });\n}\n```\n\n## Integration Tests\n\n```dart\n// integration_test/app_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:integration_test/integration_test.dart';\nimport 'package:my_app/main.dart' as app;\n\nvoid main() {\n IntegrationTestWidgetsFlutterBinding.ensureInitialized();\n\n testWidgets('complete login flow', (tester) async {\n app.main();\n await tester.pumpAndSettle();\n\n // Navigate to login\n await tester.tap(find.text('Login'));\n await tester.pumpAndSettle();\n\n // Enter credentials\n await tester.enterText(\n find.byKey(const Key('email_field')),\n '[email protected]',\n );\n await tester.enterText(\n find.byKey(const Key('password_field')),\n 'password123',\n );\n\n // Submit form\n await tester.tap(find.text('Sign In'));\n await tester.pumpAndSettle();\n\n // Verify navigation to home\n expect(find.text('Welcome'), findsOneWidget);\n });\n}\n```\n\nRun integration tests:\n\n```bash\nflutter test integration_test/app_test.dart\n```\n\n## Test Helpers\n\n```dart\n// test/helpers/pump_app.dart\nextension PumpApp on WidgetTester {\n Future\u003cvoid> pumpApp(Widget widget, {List\u003cOverride>? overrides}) {\n return pumpWidget(\n ProviderScope(\n overrides: overrides ?? [],\n child: MaterialApp(\n home: widget,\n ),\n ),\n );\n }\n}\n\n// Usage\nawait tester.pumpApp(const MyWidget());\n```\n\n## Golden Tests\n\n```dart\ntestWidgets('matches golden', (tester) async {\n await tester.pumpWidget(const MaterialApp(home: MyWidget()));\n\n await expectLater(\n find.byType(MyWidget),\n matchesGoldenFile('goldens/my_widget.png'),\n );\n});\n```\n\nUpdate goldens:\n\n```bash\nflutter test --update-goldens\n```\n\n## Testing Checklist\n\n| Test Type | What to Test |\n|-----------|--------------|\n| Widget tests | UI rendering, user interactions, state changes |\n| Bloc tests | Event → state transitions, async operations |\n| Unit tests | Validators, formatters, utilities, models |\n| Integration tests | Critical user flows, navigation |\n\n---\n\n*Flutter and flutter_test are trademarks of Google LLC.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8651,"content_sha256":"a782ec57cc7785b5de3a13bff403af843a12c1e3e3b1b5d0b3263f3753b9efa2"},{"filename":"references/widget-patterns.md","content":"# Widget Patterns\n\nFlutter widget best practices covering const optimization, responsive layouts, hooks, and sliver patterns.\n\n## Optimized Widget Pattern\n\nAlways use `const` constructors for static widgets to prevent unnecessary rebuilds:\n\n```dart\nclass OptimizedCard extends StatelessWidget {\n final String title;\n final VoidCallback onTap;\n\n const OptimizedCard({\n super.key,\n required this.title,\n required this.onTap,\n });\n\n @override\n Widget build(BuildContext context) {\n return Card(\n child: InkWell(\n onTap: onTap,\n child: Padding(\n padding: const EdgeInsets.all(16),\n child: Text(title, style: Theme.of(context).textTheme.titleMedium),\n ),\n ),\n );\n }\n}\n```\n\n### Extracting Const Widgets\n\n```dart\nclass MyScreen extends StatelessWidget {\n const MyScreen({super.key});\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: const [\n _Header(),\n _Body(),\n _Footer(),\n ],\n );\n }\n}\n\nclass _Header extends StatelessWidget {\n const _Header();\n\n @override\n Widget build(BuildContext context) {\n return const Text('Header');\n }\n}\n```\n\n## Responsive Layout\n\n```dart\nclass ResponsiveLayout extends StatelessWidget {\n final Widget mobile;\n final Widget? tablet;\n final Widget desktop;\n\n const ResponsiveLayout({\n super.key,\n required this.mobile,\n this.tablet,\n required this.desktop,\n });\n\n static const double mobileBreakpoint = 650;\n static const double desktopBreakpoint = 1100;\n\n @override\n Widget build(BuildContext context) {\n return LayoutBuilder(\n builder: (context, constraints) {\n if (constraints.maxWidth >= desktopBreakpoint) return desktop;\n if (constraints.maxWidth >= mobileBreakpoint) return tablet ?? mobile;\n return mobile;\n },\n );\n }\n}\n```\n\n### Breakpoint Reference\n\n| Type | Width | Usage |\n|------|-------|-------|\n| Mobile | \u003c 650pt | Single column, bottom nav |\n| Tablet | 650-1100pt | Two columns, side nav optional |\n| Desktop | > 1100pt | Multi-column, persistent nav |\n\n## Custom Hooks (flutter_hooks)\n\n```dart\nimport 'package:flutter_hooks/flutter_hooks.dart';\n\nclass CounterWidget extends HookWidget {\n const CounterWidget({super.key});\n\n @override\n Widget build(BuildContext context) {\n final counter = useState(0);\n final controller = useTextEditingController();\n final isMounted = useIsMounted();\n\n useEffect(() {\n debugPrint('Widget mounted');\n return () {\n debugPrint('Widget disposed');\n };\n }, const []);\n\n return Column(\n children: [\n Text('Count: ${counter.value}'),\n ElevatedButton(\n onPressed: () => counter.value++,\n child: const Text('Increment'),\n ),\n TextField(controller: controller),\n ],\n );\n }\n}\n```\n\n### Common Hooks\n\n| Hook | Purpose |\n|------|---------|\n| `useState` | Local state management |\n| `useEffect` | Side effects with cleanup |\n| `useMemoized` | Expensive computation caching |\n| `useTextEditingController` | Text field controller |\n| `useAnimationController` | Animation controller |\n| `useFocusNode` | Focus management |\n| `useIsMounted` | Check if widget is mounted |\n\n## Sliver Patterns\n\n```dart\nCustomScrollView(\n slivers: [\n SliverAppBar(\n expandedHeight: 200,\n pinned: true,\n flexibleSpace: FlexibleSpaceBar(\n title: const Text('Title'),\n background: Image.network(imageUrl, fit: BoxFit.cover),\n ),\n ),\n SliverPadding(\n padding: const EdgeInsets.all(16),\n sliver: SliverList(\n delegate: SliverChildBuilderDelegate(\n (context, index) => ListTile(\n key: ValueKey(items[index].id),\n title: Text(items[index].title),\n ),\n childCount: items.length,\n ),\n ),\n ),\n const SliverToBoxAdapter(\n child: Padding(\n padding: EdgeInsets.all(16),\n child: Text('Footer'),\n ),\n ),\n ],\n)\n```\n\n### Sliver Types\n\n| Sliver | Usage |\n|--------|-------|\n| `SliverAppBar` | Collapsing app bar |\n| `SliverList` | Lazy list |\n| `SliverGrid` | Lazy grid |\n| `SliverToBoxAdapter` | Single non-sliver widget |\n| `SliverPadding` | Add padding to sliver |\n| `SliverFillRemaining` | Fill remaining space |\n\n## Key Usage Patterns\n\n```dart\nListView.builder(\n itemCount: items.length,\n itemBuilder: (context, index) {\n final item = items[index];\n return Dismissible(\n key: ValueKey(item.id),\n child: ListTile(\n key: ValueKey('tile_${item.id}'),\n title: Text(item.title),\n ),\n );\n },\n)\n```\n\n| Key Type | When to Use |\n|----------|-------------|\n| `ValueKey` | Unique ID available |\n| `ObjectKey` | Object identity matters |\n| `UniqueKey` | Force rebuild |\n| `GlobalKey` | Access state across tree |\n\n## Optimization Checklist\n\n| Pattern | Implementation |\n|---------|----------------|\n| const widgets | Add `const` to static widgets |\n| Keys | Use `ValueKey` for list items |\n| Select | `ref.watch(provider.select(...))` |\n| RepaintBoundary | Isolate expensive repaints |\n| ListView.builder | Lazy loading for lists |\n| const constructors | Always use when possible |\n\n---\n\n*Flutter and Material Design are trademarks of Google LLC.*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5262,"content_sha256":"03531b22a5f7f3789e29abef3851f437cb2b3cceeaad51a071c5b798bae84f01"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Flutter Development Guide","type":"text"}]},{"type":"paragraph","content":[{"text":"A practical guide for building cross-platform applications with Flutter 3 and Dart. Focuses on proven patterns, state management, and performance optimization.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Widget Patterns","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Component","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"State management (simple)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"StateProvider","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"ConsumerWidget","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"State management (complex)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"NotifierProvider","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"Bloc","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Async data","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"FutureProvider","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"AsyncNotifierProvider","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Real-time streams","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"StreamProvider","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Navigation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GoRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"context.go/push","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Responsive layout","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"LayoutBuilder","type":"text","marks":[{"type":"code_inline"}]},{"text":" + breakpoints","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"List display","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ListView.builder","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Complex scrolling","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CustomScrollView","type":"text","marks":[{"type":"code_inline"}]},{"text":" + Slivers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hooks","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HookWidget","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"useState/useEffect","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Forms","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Form","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"TextFormField","type":"text","marks":[{"type":"code_inline"}]},{"text":" + validation","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Performance Patterns","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prevent rebuilds","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"const","type":"text","marks":[{"type":"code_inline"}]},{"text":" constructors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Selective updates","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ref.watch(provider.select(...))","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Isolate repaints","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"RepaintBoundary","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Lazy lists","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ListView.builder","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Heavy computation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"compute()","type":"text","marks":[{"type":"code_inline"}]},{"text":" isolate","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Image caching","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cached_network_image","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Principles","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Widget Optimization","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"const","type":"text","marks":[{"type":"code_inline"}]},{"text":" constructors wherever possible","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Extract static widgets to separate const classes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"Key","type":"text","marks":[{"type":"code_inline"}]},{"text":" for list items (ValueKey, ObjectKey)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"ConsumerWidget","type":"text","marks":[{"type":"code_inline"}]},{"text":" over ","type":"text"},{"text":"StatefulWidget","type":"text","marks":[{"type":"code_inline"}]},{"text":" for state","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"State Management","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Riverpod for dependency injection and simple state","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bloc/Cubit for event-driven workflows and complex logic","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never mutate state directly (create new instances)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"select()","type":"text","marks":[{"type":"code_inline"}]},{"text":" to minimize rebuilds","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Layout","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"8pt spacing increments (8, 16, 24, 32, 48)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Responsive breakpoints: mobile (\u003c650), tablet (650-1100), desktop (>1100)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Support all screen sizes with flexible layouts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Follow Material 3 / Cupertino design guidelines","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Performance","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Profile with DevTools before optimizing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Target \u003c16ms frame time for 60fps","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"RepaintBoundary","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complex animations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Offload heavy work with ","type":"text"},{"text":"compute()","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Checklist","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Widget Best Practices","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"const","type":"text","marks":[{"type":"code_inline"}]},{"text":" constructors on all static widgets","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Proper ","type":"text"},{"text":"Key","type":"text","marks":[{"type":"code_inline"}]},{"text":" on list items","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"ConsumerWidget","type":"text","marks":[{"type":"code_inline"}]},{"text":" for state-dependent widgets","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No widget building inside ","type":"text"},{"text":"build()","type":"text","marks":[{"type":"code_inline"}]},{"text":" method","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Extract reusable widgets to separate files","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"State Management","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Immutable state objects","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"select()","type":"text","marks":[{"type":"code_inline"}]},{"text":" for granular rebuilds","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Proper provider scoping","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Dispose controllers and subscriptions","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Handle loading/error states","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Navigation","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"GoRouter with typed routes","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Auth guards via redirect","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Deep linking support","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"State preservation across routes","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Performance","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Profile mode testing (","type":"text"},{"text":"flutter run --profile","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"\u003c16ms frame rendering time","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No unnecessary rebuilds (DevTools check)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Images cached and resized","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Heavy computation in isolates","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Testing","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Widget tests for UI components","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Unit tests for business logic","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Integration tests for user flows","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Bloc tests with ","type":"text"},{"text":"blocTest()","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Topic","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Widget patterns, const optimization, responsive layout","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Widget Patterns","type":"text","marks":[{"type":"link","attrs":{"href":"references/widget-patterns.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Riverpod providers, notifiers, async state","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Riverpod State Management","type":"text","marks":[{"type":"link","attrs":{"href":"references/riverpod-state.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bloc, Cubit, event-driven state","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bloc State Management","type":"text","marks":[{"type":"link","attrs":{"href":"references/bloc-state.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GoRouter setup, routes, deep linking","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GoRouter Navigation","type":"text","marks":[{"type":"link","attrs":{"href":"references/gorouter-navigation.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Feature-based structure, dependencies","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Project Structure","type":"text","marks":[{"type":"link","attrs":{"href":"references/project-structure.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Profiling, const optimization, DevTools","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Performance Optimization","type":"text","marks":[{"type":"link","attrs":{"href":"references/performance.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Widget tests, integration tests, mocking","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Testing Strategies","type":"text","marks":[{"type":"link","attrs":{"href":"references/testing.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"iOS/Android/Web specific implementations","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Platform Integration","type":"text","marks":[{"type":"link","attrs":{"href":"references/platform-specific.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Implicit/explicit animations, Hero, transitions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Animations","type":"text","marks":[{"type":"link","attrs":{"href":"references/animations.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dio, interceptors, error handling, caching","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Networking","type":"text","marks":[{"type":"link","attrs":{"href":"references/networking.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Form validation, FormField, input formatters","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Forms","type":"text","marks":[{"type":"link","attrs":{"href":"references/forms.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"i18n, flutter_localizations, intl","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Localization","type":"text","marks":[{"type":"link","attrs":{"href":"references/localization.md","title":null}}]}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Flutter, Dart, Material Design, and Cupertino are trademarks of Google LLC and Apple Inc. respectively. Riverpod, Bloc, and GoRouter are open-source packages by their respective maintainers.","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"flutter-dev","author":"@skillopedia","source":{"stars":12276,"repo_name":"skills","origin_url":"https://github.com/minimax-ai/skills/blob/HEAD/skills/flutter-dev/SKILL.md","repo_owner":"minimax-ai","body_sha256":"a0fba44ab13f5ea3f5229d0f65b542259b96dc15dbccad79afb25e85d6423389","cluster_key":"8cbc736435ccaf5168f89008edec69a51896af41113cb9e2878cd10481980368","clean_bundle":{"format":"clean-skill-bundle-v1","source":"minimax-ai/skills/skills/flutter-dev/SKILL.md","attachments":[{"id":"1bcf0c23-7e9a-53c1-8e81-e748f9781f0f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1bcf0c23-7e9a-53c1-8e81-e748f9781f0f/attachment.md","path":"references/animations.md","size":12863,"sha256":"83600209588577f9c2005edca10d0cba4255cbe9370f3f297f3158824b809406","contentType":"text/markdown; charset=utf-8"},{"id":"b82583c7-956b-5a41-9f8f-bda044cf3fd9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b82583c7-956b-5a41-9f8f-bda044cf3fd9/attachment.md","path":"references/bloc-state.md","size":6765,"sha256":"9ea17d693a6a0e8a1e80c55b716b98a12a89972f8ce2c25428925607075cfcca","contentType":"text/markdown; charset=utf-8"},{"id":"0dee7cfc-06dc-594e-8ae8-6d34cb7894f4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0dee7cfc-06dc-594e-8ae8-6d34cb7894f4/attachment.md","path":"references/forms.md","size":17424,"sha256":"11d76fe7be4656221b89778a1c860a3575d3f4b9272b0c54dd0f344493e7c60d","contentType":"text/markdown; charset=utf-8"},{"id":"be92c512-c704-5c1b-964c-5964628b6c9e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/be92c512-c704-5c1b-964c-5964628b6c9e/attachment.md","path":"references/gorouter-navigation.md","size":6244,"sha256":"0c71e90920777d89df7c1f4d5b32eaa4af3c85eaaee6fcda136ab231a8cbd016","contentType":"text/markdown; charset=utf-8"},{"id":"50869960-afcd-56d4-8616-c942658d1582","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/50869960-afcd-56d4-8616-c942658d1582/attachment.md","path":"references/localization.md","size":11992,"sha256":"d711c335b235db35c8e7faed1c2871e66ae65d08e16b40cc404fd231a6adca31","contentType":"text/markdown; charset=utf-8"},{"id":"0a350743-34de-5b9f-9b81-321b5d35a9b4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0a350743-34de-5b9f-9b81-321b5d35a9b4/attachment.md","path":"references/networking.md","size":13946,"sha256":"d6bded9a7b561d859883cf70eb0e64858bde38526be22856487f09ece1de848a","contentType":"text/markdown; charset=utf-8"},{"id":"16bebbb7-ab63-5d1d-be41-63581505d0b7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/16bebbb7-ab63-5d1d-be41-63581505d0b7/attachment.md","path":"references/performance.md","size":6634,"sha256":"2f5c557c6c7218dffafb3d56b0e569020c03ba6f187df96f3c66f1b1ae1e11b1","contentType":"text/markdown; charset=utf-8"},{"id":"ebc47e26-11eb-5f42-b112-0b6c078dbb58","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ebc47e26-11eb-5f42-b112-0b6c078dbb58/attachment.md","path":"references/platform-specific.md","size":11049,"sha256":"f0d8c283400ab00586e5bd8e68de3af2f35dde31841a0f9cd0ea1dba47b414ce","contentType":"text/markdown; charset=utf-8"},{"id":"4811c22d-c05c-5758-bee5-c1f9a72d8ab0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4811c22d-c05c-5758-bee5-c1f9a72d8ab0/attachment.md","path":"references/project-structure.md","size":6723,"sha256":"3d45de13d573e740db1e98877dc0ae29d02af5d877f853205d024a1c32f23588","contentType":"text/markdown; charset=utf-8"},{"id":"0fff42bd-e9ef-56ef-96a6-4ce23cb11569","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0fff42bd-e9ef-56ef-96a6-4ce23cb11569/attachment.md","path":"references/riverpod-state.md","size":5450,"sha256":"4633cba2912145032641d5921765d34335f9763f28a8c9629559ffb4cfcba227","contentType":"text/markdown; charset=utf-8"},{"id":"2ccc1bea-b82a-5eb5-9195-3fa8bc440508","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2ccc1bea-b82a-5eb5-9195-3fa8bc440508/attachment.md","path":"references/testing.md","size":8651,"sha256":"a782ec57cc7785b5de3a13bff403af843a12c1e3e3b1b5d0b3263f3753b9efa2","contentType":"text/markdown; charset=utf-8"},{"id":"0e2a2d24-1758-5632-b1be-e26f2eb4b571","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0e2a2d24-1758-5632-b1be-e26f2eb4b571/attachment.md","path":"references/widget-patterns.md","size":5262,"sha256":"03531b22a5f7f3789e29abef3851f437cb2b3cceeaad51a071c5b798bae84f01","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"0cc730b36f292ef22cf8a5c5c7c75704d382b9226b786e4283763f881c9bdd53","attachment_count":12,"text_attachments":12,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":2,"skill_md_path":"skills/flutter-dev/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":1},"license":"MIT","version":"v1","category":"testing-qa","metadata":{"sources":["flutter-expert by Jeff Smolinski (https://github.com/Jeffallan/claude-skills) — Flutter expert skill framework","Flutter Documentation","Riverpod Documentation","Bloc Library Documentation"],"version":"1.0.0","category":"mobile"},"import_tag":"clean-skills-v1","description":"Flutter cross-platform development guide covering widget patterns, Riverpod/Bloc state management, GoRouter navigation, performance optimization, and platform-specific implementations. Includes const optimization, responsive layouts, testing strategies, and DevTools profiling.\nUse when: building Flutter apps, implementing state management (Riverpod/Bloc), setting up GoRouter navigation, creating custom widgets, optimizing performance, writing widget tests, cross-platform development.\n"}},"renderedAt":1782979274944}

Flutter Development Guide A practical guide for building cross-platform applications with Flutter 3 and Dart. Focuses on proven patterns, state management, and performance optimization. Quick Reference Widget Patterns | Purpose | Component | |---------|-----------| | State management (simple) | + | | State management (complex) | / | | Async data | / | | Real-time streams | | | Navigation | + | | Responsive layout | + breakpoints | | List display | | | Complex scrolling | + Slivers | | Hooks | + | | Forms | + + validation | Performance Patterns | Purpose | Solution | |---------|----------| | P…