Back to skills
SkillHub ClubBuild MobileMobileTesting

moai-lang-flutter

Provides detailed Flutter 3.24+ and Dart 3.5+ development guidance with Riverpod state management, go_router navigation, and performance optimization patterns. Includes practical code examples for modern Dart features, widget building, and testing frameworks.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
56
Hot score
92
Updated
March 20, 2026
Overall rating
A8.0
Composite score
6.1
Best-practice grade
N/A

Install command

npx @skill-hub/cli install modu-ai-cc-plugins-moai-lang-flutter
flutterdartriverpodmobile-developmentcross-platform

Repository

modu-ai/cc-plugins

Skill path: .claude/skills/moai-lang-flutter

Provides detailed Flutter 3.24+ and Dart 3.5+ development guidance with Riverpod state management, go_router navigation, and performance optimization patterns. Includes practical code examples for modern Dart features, widget building, and testing frameworks.

Open repository

Best for

Primary workflow: Build Mobile.

Technical facets: Mobile, Testing.

Target audience: Flutter developers working on cross-platform mobile, desktop, or web applications who want to implement modern Dart patterns and Riverpod state management.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: modu-ai.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install moai-lang-flutter into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/modu-ai/cc-plugins before adding moai-lang-flutter to shared team environments
  • Use moai-lang-flutter for mobile workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: "moai-lang-flutter"
description: "Flutter 3.24+ / Dart 3.5+ development specialist covering Riverpod, go_router, and cross-platform patterns. Use when building cross-platform mobile apps, desktop apps, or web applications with Flutter."
version: 1.0.0
category: "language"
modularized: false
tags: ['flutter', 'dart', 'riverpod', 'cross-platform', 'mobile', 'desktop']
context7-libraries: ['/flutter/flutter', '/rrousselgit/riverpod', '/flutter/packages']
related-skills: ['moai-lang-swift', 'moai-lang-kotlin']
updated: 2025-12-07
status: "active"
---

## Quick Reference (30 seconds)

Flutter/Dart Development Expert - Dart 3.5+, Flutter 3.24+ with modern patterns.

Auto-Triggers: Flutter projects (`.dart` files, `pubspec.yaml`), cross-platform apps, widget development

Core Capabilities:
- Dart 3.5: Patterns, records, sealed classes, extension types
- Flutter 3.24: Widget tree, Material 3, adaptive layouts
- Riverpod: State management with code generation
- go_router: Declarative navigation and deep linking
- Platform Channels: Native iOS/Android integration
- Testing: flutter_test, widget_test, integration_test

## Implementation Guide (5 minutes)

### Dart 3.5 Language Features

Pattern Matching with Sealed Classes:
```dart
sealed class Result<T> {
  const Result();
}
class Success<T> extends Result<T> {
  final T data;
  const Success(this.data);
}
class Failure<T> extends Result<T> {
  final String error;
  const Failure(this.error);
}

// Exhaustive switch expression
String handleResult(Result<User> result) => switch (result) {
  Success(:final data) => 'User: ${data.name}',
  Failure(:final error) => 'Error: $error',
};

// Guard clauses in patterns
String describeUser(User user) => switch (user) {
  User(age: var a) when a < 18 => 'Minor',
  User(age: var a) when a >= 65 => 'Senior',
  User(name: var n, age: var a) => '$n, age $a',
};
```

Records and Destructuring:
```dart
typedef UserRecord = ({String name, int age, String email});

// Multiple return values
(String name, int age) parseUser(Map<String, dynamic> json) {
  return (json['name'] as String, json['age'] as int);
}

// Destructuring
void processUser(Map<String, dynamic> json) {
  final (name, age) = parseUser(json);
  print('$name is $age years old');
}

// Record patterns in collections
void processUsers(List<UserRecord> users) {
  for (final (:name, :age, :email) in users) {
    print('$name ($age): $email');
  }
}
```

Extension Types:
```dart
extension type UserId(String value) {
  factory UserId.generate() => UserId(Uuid().v4());
  bool get isValid => value.isNotEmpty;
}

extension type Email(String value) {
  bool get isValid => value.contains('@') && value.contains('.');
  String get domain => value.split('@').last;
}
```

### Riverpod State Management

Provider Definitions:
```dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'providers.g.dart';

@riverpod
UserRepository userRepository(Ref ref) {
  return UserRepository(ref.read(dioProvider));
}

@riverpod
Future<User> user(Ref ref, String userId) async {
  return ref.watch(userRepositoryProvider).getUser(userId);
}

@riverpod
class UserNotifier extends _$UserNotifier {
  @override
  FutureOr<User?> build() => null;

  Future<void> loadUser(String id) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(userRepositoryProvider).getUser(id),
    );
  }

  Future<void> updateUser(User user) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(userRepositoryProvider).updateUser(user),
    );
  }
}

@riverpod
Stream<List<Message>> messages(Ref ref, String chatId) {
  return ref.watch(chatRepositoryProvider).watchMessages(chatId);
}
```

Widget Integration:
```dart
class UserScreen extends ConsumerWidget {
  final String userId;
  const UserScreen({required this.userId, super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider(userId));

    return Scaffold(
      appBar: AppBar(title: const Text('User Profile')),
      body: userAsync.when(
        data: (user) => UserProfile(user: user),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (error, stack) => Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Error: $error'),
              ElevatedButton(
                onPressed: () => ref.invalidate(userProvider(userId)),
                child: const Text('Retry'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```

StatefulWidget with Riverpod:
```dart
class EditUserScreen extends ConsumerStatefulWidget {
  const EditUserScreen({super.key});
  @override
  ConsumerState<EditUserScreen> createState() => _EditUserScreenState();
}

class _EditUserScreenState extends ConsumerState<EditUserScreen> {
  final _formKey = GlobalKey<FormState>();
  late TextEditingController _nameController;

  @override
  void initState() {
    super.initState();
    _nameController = TextEditingController();
  }

  @override
  void dispose() {
    _nameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    ref.listen(userNotifierProvider, (prev, next) {
      next.whenOrNull(
        error: (e, _) => ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Error: $e')),
        ),
      );
    });

    final isLoading = ref.watch(userNotifierProvider).isLoading;

    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            controller: _nameController,
            validator: (v) => v?.isEmpty ?? true ? 'Required' : null,
          ),
          ElevatedButton(
            onPressed: isLoading ? null : () async {
              if (_formKey.currentState!.validate()) {
                await ref.read(userNotifierProvider.notifier)
                    .updateUser(User(name: _nameController.text));
              }
            },
            child: isLoading
                ? const CircularProgressIndicator()
                : const Text('Save'),
          ),
        ],
      ),
    );
  }
}
```

### go_router Navigation

Router Configuration:
```dart
final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'user/:id',
          name: 'user-detail',
          builder: (context, state) => UserDetailScreen(
            userId: state.pathParameters['id']!,
          ),
        ),
      ],
    ),
    ShellRoute(
      builder: (context, state, child) => MainShell(child: child),
      routes: [
        GoRoute(
          path: '/feed',
          pageBuilder: (_, __) => const NoTransitionPage(child: FeedScreen()),
        ),
        GoRoute(
          path: '/search',
          pageBuilder: (_, __) => const NoTransitionPage(child: SearchScreen()),
        ),
        GoRoute(
          path: '/profile',
          pageBuilder: (_, __) => const NoTransitionPage(child: ProfileScreen()),
        ),
      ],
    ),
  ],
  redirect: (context, state) {
    final isLoggedIn = authNotifier.isLoggedIn;
    final isLoggingIn = state.matchedLocation == '/login';
    if (!isLoggedIn && !isLoggingIn) return '/login';
    if (isLoggedIn && isLoggingIn) return '/';
    return null;
  },
  errorBuilder: (context, state) => ErrorScreen(error: state.error),
);

// Navigation methods
void navigateToUser(BuildContext context, String userId) {
  context.go('/user/$userId');
}

void goBack(BuildContext context) {
  if (context.canPop()) context.pop();
  else context.go('/');
}
```

### Platform Channels

Dart Implementation:
```dart
class NativeBridge {
  static const _channel = MethodChannel('com.example.app/native');
  static const _eventChannel = EventChannel('com.example.app/events');

  Future<String> getPlatformVersion() async {
    try {
      final version = await _channel.invokeMethod<String>('getPlatformVersion');
      return version ?? 'Unknown';
    } on PlatformException catch (e) {
      throw NativeBridgeException('Failed: ${e.message}');
    }
  }

  Future<void> shareContent({required String text, String? title}) async {
    await _channel.invokeMethod('share', {
      'text': text,
      if (title != null) 'title': title,
    });
  }

  Stream<BatteryState> watchBatteryState() {
    return _eventChannel.receiveBroadcastStream().map((event) {
      final data = event as Map<dynamic, dynamic>;
      return BatteryState(
        level: data['level'] as int,
        isCharging: data['isCharging'] as bool,
      );
    });
  }

  void setupMethodCallHandler() {
    _channel.setMethodCallHandler((call) async {
      switch (call.method) {
        case 'onNativeEvent':
          // Handle native event
          return true;
        default:
          throw MissingPluginException('Not implemented: ${call.method}');
      }
    });
  }
}

class BatteryState {
  final int level;
  final bool isCharging;
  const BatteryState({required this.level, required this.isCharging});
}
```

### Widget Patterns

Adaptive Layouts:
```dart
class AdaptiveScaffold extends StatelessWidget {
  final Widget child;
  final List<NavigationDestination> destinations;
  final int selectedIndex;
  final ValueChanged<int> onDestinationSelected;

  const AdaptiveScaffold({
    required this.child,
    required this.destinations,
    required this.selectedIndex,
    required this.onDestinationSelected,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;

    if (width < 600) {
      return Scaffold(
        body: child,
        bottomNavigationBar: NavigationBar(
          selectedIndex: selectedIndex,
          onDestinationSelected: onDestinationSelected,
          destinations: destinations,
        ),
      );
    }

    if (width < 840) {
      return Scaffold(
        body: Row(children: [
          NavigationRail(
            selectedIndex: selectedIndex,
            onDestinationSelected: onDestinationSelected,
            destinations: destinations.map((d) => NavigationRailDestination(
              icon: d.icon, selectedIcon: d.selectedIcon, label: Text(d.label),
            )).toList(),
          ),
          const VerticalDivider(thickness: 1, width: 1),
          Expanded(child: child),
        ]),
      );
    }

    return Scaffold(
      body: Row(children: [
        NavigationDrawer(
          selectedIndex: selectedIndex,
          onDestinationSelected: onDestinationSelected,
          children: destinations.map((d) => NavigationDrawerDestination(
            icon: d.icon, selectedIcon: d.selectedIcon ?? d.icon, label: Text(d.label),
          )).toList(),
        ),
        const VerticalDivider(thickness: 1, width: 1),
        Expanded(child: child),
      ]),
    );
  }
}
```

### Testing

Widget Test Example:
```dart
void main() {
  testWidgets('UserScreen displays data', (tester) async {
    final container = ProviderContainer(overrides: [
      userRepositoryProvider.overrideWithValue(MockUserRepository()),
    ]);

    await tester.pumpWidget(
      UncontrolledProviderScope(
        container: container,
        child: const MaterialApp(home: UserScreen(userId: '1')),
      ),
    );

    expect(find.byType(CircularProgressIndicator), findsOneWidget);
    await tester.pumpAndSettle();
    expect(find.text('Test User'), findsOneWidget);
  });
}
```

For comprehensive testing patterns, see [examples.md](examples.md).

## Advanced Patterns

For comprehensive coverage including:
- Clean Architecture with Riverpod
- Isolates for compute-heavy operations
- Custom render objects and painting
- FFI and platform-specific plugins
- Performance optimization and profiling

See: [reference.md](reference.md) and [examples.md](examples.md)

## Context7 Library Mappings

Flutter/Dart Core:
- `/flutter/flutter` - Flutter framework
- `/dart-lang/sdk` - Dart SDK

State Management:
- `/rrousselGit/riverpod` - Riverpod state management
- `/felangel/bloc` - BLoC pattern

Navigation and Storage:
- `/flutter/packages` - go_router and official packages
- `/cfug/dio` - HTTP client
- `/isar/isar` - NoSQL database

## Works Well With

- `moai-lang-swift` - iOS native integration for platform channels
- `moai-lang-kotlin` - Android native integration for platform channels
- `moai-domain-backend` - API integration and backend communication
- `moai-quality-security` - Mobile security best practices
- `moai-essentials-debug` - Flutter debugging and DevTools


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### examples.md

```markdown
# Flutter/Dart Examples

Production-ready code examples for Flutter cross-platform development.

## Complete Feature: User Authentication

### Full Authentication Flow with Riverpod

```dart
// lib/features/auth/domain/entities/user.dart
class User {
  final String id;
  final String email;
  final String name;
  final String? avatarUrl;

  const User({
    required this.id,
    required this.email,
    required this.name,
    this.avatarUrl,
  });
}

class AuthTokens {
  final String accessToken;
  final String refreshToken;
  final DateTime expiresAt;

  const AuthTokens({
    required this.accessToken,
    required this.refreshToken,
    required this.expiresAt,
  });

  bool get isExpired => DateTime.now().isAfter(expiresAt);
}

// lib/features/auth/domain/errors/auth_error.dart
sealed class AuthError implements Exception {
  const AuthError();

  String get message => switch (this) {
    InvalidCredentialsError() => 'Invalid email or password',
    NetworkError(:final cause) => 'Network error: $cause',
    TokenExpiredError() => 'Session expired. Please login again',
    UnauthorizedError() => 'Unauthorized access',
    UnknownError(:final message) => message,
  };
}

class InvalidCredentialsError extends AuthError {
  const InvalidCredentialsError();
}

class NetworkError extends AuthError {
  final Object cause;
  const NetworkError(this.cause);
}

class TokenExpiredError extends AuthError {
  const TokenExpiredError();
}

class UnauthorizedError extends AuthError {
  const UnauthorizedError();
}

class UnknownError extends AuthError {
  @override
  final String message;
  const UnknownError(this.message);
}

// lib/features/auth/domain/repositories/auth_repository.dart
abstract class AuthRepository {
  Future<User> login(String email, String password);
  Future<void> logout();
  Future<User?> restoreSession();
  Stream<User?> watchAuthState();
}

// lib/features/auth/data/models/user_model.dart
class UserModel extends User {
  const UserModel({
    required super.id,
    required super.email,
    required super.name,
    super.avatarUrl,
  });

  factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
    id: json['id'] as String,
    email: json['email'] as String,
    name: json['name'] as String,
    avatarUrl: json['avatar_url'] as String?,
  );

  Map<String, dynamic> toJson() => {
    'id': id,
    'email': email,
    'name': name,
    if (avatarUrl != null) 'avatar_url': avatarUrl,
  };
}

class AuthTokensModel extends AuthTokens {
  const AuthTokensModel({
    required super.accessToken,
    required super.refreshToken,
    required super.expiresAt,
  });

  factory AuthTokensModel.fromJson(Map<String, dynamic> json) => AuthTokensModel(
    accessToken: json['access_token'] as String,
    refreshToken: json['refresh_token'] as String,
    expiresAt: DateTime.parse(json['expires_at'] as String),
  );

  Map<String, dynamic> toJson() => {
    'access_token': accessToken,
    'refresh_token': refreshToken,
    'expires_at': expiresAt.toIso8601String(),
  };
}

// lib/features/auth/data/datasources/auth_api.dart
class AuthApi {
  final Dio _dio;

  AuthApi(this._dio);

  Future<AuthTokensModel> login(String email, String password) async {
    final response = await _dio.post('/auth/login', data: {
      'email': email,
      'password': password,
    });
    return AuthTokensModel.fromJson(response.data);
  }

  Future<AuthTokensModel> refreshToken(String refreshToken) async {
    final response = await _dio.post('/auth/refresh', data: {
      'refresh_token': refreshToken,
    });
    return AuthTokensModel.fromJson(response.data);
  }

  Future<UserModel> getUser(String accessToken) async {
    final response = await _dio.get(
      '/auth/me',
      options: Options(headers: {'Authorization': 'Bearer $accessToken'}),
    );
    return UserModel.fromJson(response.data);
  }

  Future<void> logout(String accessToken) async {
    await _dio.post(
      '/auth/logout',
      options: Options(headers: {'Authorization': 'Bearer $accessToken'}),
    );
  }
}

// lib/features/auth/data/datasources/secure_storage.dart
class SecureStorage {
  static const _tokensKey = 'auth_tokens';
  final FlutterSecureStorage _storage;

  SecureStorage(this._storage);

  Future<AuthTokensModel?> getTokens() async {
    final json = await _storage.read(key: _tokensKey);
    if (json == null) return null;
    return AuthTokensModel.fromJson(jsonDecode(json));
  }

  Future<void> saveTokens(AuthTokensModel tokens) async {
    await _storage.write(
      key: _tokensKey,
      value: jsonEncode(tokens.toJson()),
    );
  }

  Future<void> clearTokens() async {
    await _storage.delete(key: _tokensKey);
  }
}

// lib/features/auth/data/repositories/auth_repository_impl.dart
class AuthRepositoryImpl implements AuthRepository {
  final AuthApi _api;
  final SecureStorage _storage;
  final _authStateController = StreamController<User?>.broadcast();

  AuthRepositoryImpl(this._api, this._storage);

  @override
  Stream<User?> watchAuthState() => _authStateController.stream;

  @override
  Future<User> login(String email, String password) async {
    try {
      final tokens = await _api.login(email, password);
      await _storage.saveTokens(tokens);

      final user = await _api.getUser(tokens.accessToken);
      _authStateController.add(user);

      return user;
    } on DioException catch (e) {
      throw _mapDioError(e);
    }
  }

  @override
  Future<void> logout() async {
    try {
      final tokens = await _storage.getTokens();
      if (tokens != null) {
        await _api.logout(tokens.accessToken).catchError((_) {});
      }
    } finally {
      await _storage.clearTokens();
      _authStateController.add(null);
    }
  }

  @override
  Future<User?> restoreSession() async {
    final tokens = await _storage.getTokens();
    if (tokens == null) return null;

    try {
      AuthTokensModel activeTokens = tokens;

      if (tokens.isExpired) {
        activeTokens = await _api.refreshToken(tokens.refreshToken);
        await _storage.saveTokens(activeTokens);
      }

      final user = await _api.getUser(activeTokens.accessToken);
      _authStateController.add(user);
      return user;
    } on DioException catch (e) {
      if (e.response?.statusCode == 401) {
        await _storage.clearTokens();
        _authStateController.add(null);
        return null;
      }
      throw _mapDioError(e);
    }
  }

  AuthError _mapDioError(DioException e) {
    if (e.type == DioExceptionType.connectionError ||
        e.type == DioExceptionType.connectionTimeout) {
      return NetworkError(e);
    }

    final statusCode = e.response?.statusCode;
    return switch (statusCode) {
      401 => const InvalidCredentialsError(),
      403 => const UnauthorizedError(),
      _ => UnknownError(e.message ?? 'Unknown error'),
    };
  }
}

// lib/features/auth/presentation/providers/auth_provider.dart
part 'auth_provider.g.dart';

@riverpod
AuthRepository authRepository(Ref ref) {
  return AuthRepositoryImpl(
    ref.read(authApiProvider),
    ref.read(secureStorageProvider),
  );
}

@riverpod
Stream<User?> authState(Ref ref) {
  return ref.watch(authRepositoryProvider).watchAuthState();
}

@riverpod
class AuthController extends _$AuthController {
  @override
  FutureOr<User?> build() async {
    return ref.read(authRepositoryProvider).restoreSession();
  }

  Future<void> login(String email, String password) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(authRepositoryProvider).login(email, password),
    );
  }

  Future<void> logout() async {
    await ref.read(authRepositoryProvider).logout();
    state = const AsyncData(null);
  }
}

// lib/features/auth/presentation/screens/login_screen.dart
class LoginScreen extends ConsumerStatefulWidget {
  const LoginScreen({super.key});

  @override
  ConsumerState<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends ConsumerState<LoginScreen> {
  final _formKey = GlobalKey<FormState>();
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _obscurePassword = true;

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  Future<void> _handleLogin() async {
    if (!_formKey.currentState!.validate()) return;

    await ref.read(authControllerProvider.notifier).login(
      _emailController.text.trim(),
      _passwordController.text,
    );
  }

  @override
  Widget build(BuildContext context) {
    final authState = ref.watch(authControllerProvider);
    final theme = Theme.of(context);

    // Listen for auth state changes
    ref.listen(authControllerProvider, (prev, next) {
      next.whenOrNull(
        data: (user) {
          if (user != null) {
            context.go('/home');
          }
        },
        error: (error, _) {
          final message = error is AuthError
              ? error.message
              : 'An unexpected error occurred';

          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text(message),
              backgroundColor: theme.colorScheme.error,
            ),
          );
        },
      );
    });

    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(24),
          child: Form(
            key: _formKey,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                const SizedBox(height: 48),

                // Header
                Text(
                  'Welcome Back',
                  style: theme.textTheme.headlineLarge,
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 8),
                Text(
                  'Sign in to continue',
                  style: theme.textTheme.bodyLarge?.copyWith(
                    color: theme.colorScheme.onSurfaceVariant,
                  ),
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 48),

                // Email field
                TextFormField(
                  controller: _emailController,
                  keyboardType: TextInputType.emailAddress,
                  textInputAction: TextInputAction.next,
                  decoration: const InputDecoration(
                    labelText: 'Email',
                    prefixIcon: Icon(Icons.email_outlined),
                    border: OutlineInputBorder(),
                  ),
                  validator: (value) {
                    if (value == null || value.isEmpty) {
                      return 'Please enter your email';
                    }
                    if (!value.contains('@') || !value.contains('.')) {
                      return 'Please enter a valid email';
                    }
                    return null;
                  },
                ),
                const SizedBox(height: 16),

                // Password field
                TextFormField(
                  controller: _passwordController,
                  obscureText: _obscurePassword,
                  textInputAction: TextInputAction.done,
                  onFieldSubmitted: (_) => _handleLogin(),
                  decoration: InputDecoration(
                    labelText: 'Password',
                    prefixIcon: const Icon(Icons.lock_outlined),
                    suffixIcon: IconButton(
                      icon: Icon(
                        _obscurePassword
                            ? Icons.visibility_outlined
                            : Icons.visibility_off_outlined,
                      ),
                      onPressed: () {
                        setState(() => _obscurePassword = !_obscurePassword);
                      },
                    ),
                    border: const OutlineInputBorder(),
                  ),
                  validator: (value) {
                    if (value == null || value.isEmpty) {
                      return 'Please enter your password';
                    }
                    if (value.length < 6) {
                      return 'Password must be at least 6 characters';
                    }
                    return null;
                  },
                ),
                const SizedBox(height: 8),

                // Forgot password
                Align(
                  alignment: Alignment.centerRight,
                  child: TextButton(
                    onPressed: () => context.push('/forgot-password'),
                    child: const Text('Forgot Password?'),
                  ),
                ),
                const SizedBox(height: 24),

                // Login button
                FilledButton(
                  onPressed: authState.isLoading ? null : _handleLogin,
                  style: FilledButton.styleFrom(
                    minimumSize: const Size.fromHeight(56),
                  ),
                  child: authState.isLoading
                      ? const SizedBox(
                          height: 24,
                          width: 24,
                          child: CircularProgressIndicator(strokeWidth: 2),
                        )
                      : const Text('Sign In'),
                ),
                const SizedBox(height: 24),

                // Register link
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(
                      "Don't have an account?",
                      style: theme.textTheme.bodyMedium,
                    ),
                    TextButton(
                      onPressed: () => context.push('/register'),
                      child: const Text('Sign Up'),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
```

## Network Layer

### Dio Client with Interceptors

```dart
// lib/core/network/api_client.dart
class ApiClient {
  late final Dio _dio;
  final SecureStorage _storage;

  ApiClient({
    required String baseUrl,
    required SecureStorage storage,
  }) : _storage = storage {
    _dio = Dio(BaseOptions(
      baseUrl: baseUrl,
      connectTimeout: const Duration(seconds: 30),
      receiveTimeout: const Duration(seconds: 30),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ));

    _dio.interceptors.addAll([
      AuthInterceptor(_storage, _dio),
      LogInterceptor(
        requestBody: true,
        responseBody: true,
        logPrint: (log) => debugPrint(log.toString()),
      ),
      RetryInterceptor(_dio),
    ]);
  }

  Future<T> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    required T Function(dynamic) fromJson,
  }) async {
    final response = await _dio.get(path, queryParameters: queryParameters);
    return fromJson(response.data);
  }

  Future<T> post<T>(
    String path, {
    dynamic data,
    required T Function(dynamic) fromJson,
  }) async {
    final response = await _dio.post(path, data: data);
    return fromJson(response.data);
  }

  Future<T> put<T>(
    String path, {
    dynamic data,
    required T Function(dynamic) fromJson,
  }) async {
    final response = await _dio.put(path, data: data);
    return fromJson(response.data);
  }

  Future<void> delete(String path) async {
    await _dio.delete(path);
  }
}

// lib/core/network/interceptors/auth_interceptor.dart
class AuthInterceptor extends Interceptor {
  final SecureStorage _storage;
  final Dio _dio;
  bool _isRefreshing = false;
  final _pendingRequests = <({RequestOptions options, ErrorInterceptorHandler handler})>[];

  AuthInterceptor(this._storage, this._dio);

  @override
  Future<void> onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) async {
    final tokens = await _storage.getTokens();
    if (tokens != null) {
      options.headers['Authorization'] = 'Bearer ${tokens.accessToken}';
    }
    handler.next(options);
  }

  @override
  Future<void> onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) async {
    if (err.response?.statusCode != 401) {
      return handler.next(err);
    }

    // Queue the request if already refreshing
    if (_isRefreshing) {
      _pendingRequests.add((options: err.requestOptions, handler: handler));
      return;
    }

    _isRefreshing = true;

    try {
      final tokens = await _storage.getTokens();
      if (tokens == null) {
        return handler.next(err);
      }

      // Refresh token
      final response = await _dio.post(
        '/auth/refresh',
        data: {'refresh_token': tokens.refreshToken},
        options: Options(headers: {'Authorization': null}),
      );

      final newTokens = AuthTokensModel.fromJson(response.data);
      await _storage.saveTokens(newTokens);

      // Retry original request
      final retryResponse = await _retryRequest(err.requestOptions, newTokens);
      handler.resolve(retryResponse);

      // Retry pending requests
      for (final pending in _pendingRequests) {
        final response = await _retryRequest(pending.options, newTokens);
        pending.handler.resolve(response);
      }
    } on DioException catch (e) {
      // Refresh failed - clear tokens and reject all pending
      await _storage.clearTokens();
      handler.next(e);

      for (final pending in _pendingRequests) {
        pending.handler.next(e);
      }
    } finally {
      _isRefreshing = false;
      _pendingRequests.clear();
    }
  }

  Future<Response> _retryRequest(
    RequestOptions options,
    AuthTokensModel tokens,
  ) async {
    options.headers['Authorization'] = 'Bearer ${tokens.accessToken}';
    return _dio.fetch(options);
  }
}

// lib/core/network/interceptors/retry_interceptor.dart
class RetryInterceptor extends Interceptor {
  final Dio _dio;
  final int maxRetries;
  final Duration retryDelay;

  RetryInterceptor(
    this._dio, {
    this.maxRetries = 3,
    this.retryDelay = const Duration(seconds: 1),
  });

  @override
  Future<void> onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) async {
    final shouldRetry = _shouldRetry(err);
    final retryCount = err.requestOptions.extra['retryCount'] ?? 0;

    if (!shouldRetry || retryCount >= maxRetries) {
      return handler.next(err);
    }

    await Future.delayed(retryDelay * (retryCount + 1));

    try {
      err.requestOptions.extra['retryCount'] = retryCount + 1;
      final response = await _dio.fetch(err.requestOptions);
      handler.resolve(response);
    } on DioException catch (e) {
      handler.next(e);
    }
  }

  bool _shouldRetry(DioException err) {
    return err.type == DioExceptionType.connectionTimeout ||
        err.type == DioExceptionType.sendTimeout ||
        err.type == DioExceptionType.receiveTimeout ||
        (err.response?.statusCode != null &&
            err.response!.statusCode! >= 500);
  }
}
```

## Platform Channels

### Complete Native Bridge

```dart
// lib/core/platform/native_bridge.dart
class NativeBridge {
  static const _methodChannel = MethodChannel('com.example.app/native');
  static const _eventChannel = EventChannel('com.example.app/events');

  // Singleton pattern
  static final NativeBridge _instance = NativeBridge._();
  static NativeBridge get instance => _instance;
  NativeBridge._() {
    _setupMethodCallHandler();
  }

  // Method calls
  Future<String> getPlatformVersion() async {
    try {
      final version = await _methodChannel.invokeMethod<String>('getPlatformVersion');
      return version ?? 'Unknown';
    } on PlatformException catch (e) {
      throw NativeBridgeException('Failed to get platform version: ${e.message}');
    }
  }

  Future<DeviceInfo> getDeviceInfo() async {
    try {
      final result = await _methodChannel.invokeMapMethod<String, dynamic>('getDeviceInfo');
      return DeviceInfo.fromMap(result ?? {});
    } on PlatformException catch (e) {
      throw NativeBridgeException('Failed to get device info: ${e.message}');
    }
  }

  Future<void> shareContent({
    required String text,
    String? title,
    String? url,
  }) async {
    try {
      await _methodChannel.invokeMethod('share', {
        'text': text,
        if (title != null) 'title': title,
        if (url != null) 'url': url,
      });
    } on PlatformException catch (e) {
      throw NativeBridgeException('Failed to share: ${e.message}');
    }
  }

  Future<bool> requestPermission(PermissionType type) async {
    try {
      final result = await _methodChannel.invokeMethod<bool>(
        'requestPermission',
        {'type': type.name},
      );
      return result ?? false;
    } on PlatformException catch (e) {
      throw NativeBridgeException('Failed to request permission: ${e.message}');
    }
  }

  // Event streams
  Stream<BatteryState> watchBatteryState() {
    return _eventChannel.receiveBroadcastStream('battery').map((event) {
      final data = event as Map<dynamic, dynamic>;
      return BatteryState(
        level: data['level'] as int,
        isCharging: data['isCharging'] as bool,
      );
    });
  }

  Stream<ConnectivityState> watchConnectivity() {
    return _eventChannel.receiveBroadcastStream('connectivity').map((event) {
      final data = event as Map<dynamic, dynamic>;
      return ConnectivityState(
        isConnected: data['isConnected'] as bool,
        type: ConnectivityType.values.byName(data['type'] as String),
      );
    });
  }

  // Bidirectional communication
  void _setupMethodCallHandler() {
    _methodChannel.setMethodCallHandler((call) async {
      switch (call.method) {
        case 'onDeepLink':
          final url = call.arguments as String;
          _handleDeepLink(url);
          return true;
        case 'onPushNotification':
          final data = call.arguments as Map<dynamic, dynamic>;
          _handlePushNotification(data.cast<String, dynamic>());
          return true;
        default:
          throw MissingPluginException('Method not implemented: ${call.method}');
      }
    });
  }

  void _handleDeepLink(String url) {
    // Handle deep link
    debugPrint('Received deep link: $url');
  }

  void _handlePushNotification(Map<String, dynamic> data) {
    // Handle push notification
    debugPrint('Received notification: $data');
  }
}

// Models
class DeviceInfo {
  final String platform;
  final String version;
  final String model;
  final String manufacturer;

  const DeviceInfo({
    required this.platform,
    required this.version,
    required this.model,
    required this.manufacturer,
  });

  factory DeviceInfo.fromMap(Map<String, dynamic> map) => DeviceInfo(
    platform: map['platform'] as String? ?? 'unknown',
    version: map['version'] as String? ?? 'unknown',
    model: map['model'] as String? ?? 'unknown',
    manufacturer: map['manufacturer'] as String? ?? 'unknown',
  );
}

class BatteryState {
  final int level;
  final bool isCharging;
  const BatteryState({required this.level, required this.isCharging});
}

class ConnectivityState {
  final bool isConnected;
  final ConnectivityType type;
  const ConnectivityState({required this.isConnected, required this.type});
}

enum ConnectivityType { none, wifi, mobile, ethernet }
enum PermissionType { camera, microphone, location, storage, notifications }

class NativeBridgeException implements Exception {
  final String message;
  const NativeBridgeException(this.message);

  @override
  String toString() => 'NativeBridgeException: $message';
}
```

## Testing Examples

### Widget Tests with Riverpod

```dart
// test/features/auth/presentation/screens/login_screen_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mocktail/mocktail.dart';

class MockAuthRepository extends Mock implements AuthRepository {}

void main() {
  late ProviderContainer container;
  late MockAuthRepository mockAuthRepository;

  setUp(() {
    mockAuthRepository = MockAuthRepository();

    // Default mock behavior
    when(() => mockAuthRepository.restoreSession())
        .thenAnswer((_) async => null);
    when(() => mockAuthRepository.watchAuthState())
        .thenAnswer((_) => const Stream.empty());

    container = ProviderContainer(overrides: [
      authRepositoryProvider.overrideWithValue(mockAuthRepository),
    ]);
  });

  tearDown(() => container.dispose());

  Widget buildTestWidget() {
    return UncontrolledProviderScope(
      container: container,
      child: MaterialApp(
        home: const LoginScreen(),
      ),
    );
  }

  group('LoginScreen', () {
    testWidgets('renders email and password fields', (tester) async {
      await tester.pumpWidget(buildTestWidget());

      expect(find.byType(TextFormField), findsNWidgets(2));
      expect(find.text('Email'), findsOneWidget);
      expect(find.text('Password'), findsOneWidget);
    });

    testWidgets('shows validation errors for empty fields', (tester) async {
      await tester.pumpWidget(buildTestWidget());

      await tester.tap(find.byType(FilledButton));
      await tester.pump();

      expect(find.text('Please enter your email'), findsOneWidget);
      expect(find.text('Please enter your password'), findsOneWidget);
    });

    testWidgets('shows validation error for invalid email', (tester) async {
      await tester.pumpWidget(buildTestWidget());

      await tester.enterText(
        find.widgetWithText(TextFormField, 'Email'),
        'invalid-email',
      );
      await tester.enterText(
        find.widgetWithText(TextFormField, 'Password'),
        'password123',
      );

      await tester.tap(find.byType(FilledButton));
      await tester.pump();

      expect(find.text('Please enter a valid email'), findsOneWidget);
    });

    testWidgets('calls login when form is valid', (tester) async {
      when(() => mockAuthRepository.login(any(), any()))
          .thenAnswer((_) async => const User(
                id: '1',
                email: '[email protected]',
                name: 'Test User',
              ));

      await tester.pumpWidget(buildTestWidget());

      await tester.enterText(
        find.widgetWithText(TextFormField, 'Email'),
        '[email protected]',
      );
      await tester.enterText(
        find.widgetWithText(TextFormField, 'Password'),
        'password123',
      );

      await tester.tap(find.byType(FilledButton));
      await tester.pump();

      verify(() => mockAuthRepository.login('[email protected]', 'password123'))
          .called(1);
    });

    testWidgets('shows loading indicator during login', (tester) async {
      when(() => mockAuthRepository.login(any(), any()))
          .thenAnswer((_) async {
        await Future.delayed(const Duration(seconds: 2));
        return const User(
          id: '1',
          email: '[email protected]',
          name: 'Test User',
        );
      });

      await tester.pumpWidget(buildTestWidget());

      await tester.enterText(
        find.widgetWithText(TextFormField, 'Email'),
        '[email protected]',
      );
      await tester.enterText(
        find.widgetWithText(TextFormField, 'Password'),
        'password123',
      );

      await tester.tap(find.byType(FilledButton));
      await tester.pump();

      expect(find.byType(CircularProgressIndicator), findsOneWidget);
    });

    testWidgets('shows error snackbar on login failure', (tester) async {
      when(() => mockAuthRepository.login(any(), any()))
          .thenThrow(const InvalidCredentialsError());

      await tester.pumpWidget(buildTestWidget());

      await tester.enterText(
        find.widgetWithText(TextFormField, 'Email'),
        '[email protected]',
      );
      await tester.enterText(
        find.widgetWithText(TextFormField, 'Password'),
        'wrongpassword',
      );

      await tester.tap(find.byType(FilledButton));
      await tester.pumpAndSettle();

      expect(find.byType(SnackBar), findsOneWidget);
      expect(find.text('Invalid email or password'), findsOneWidget);
    });
  });
}
```

### Integration Tests

```dart
// integration_test/auth_flow_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('Authentication Flow', () {
    testWidgets('complete login and logout flow', (tester) async {
      await tester.pumpWidget(const MyApp());
      await tester.pumpAndSettle();

      // Verify we're on login screen
      expect(find.text('Welcome Back'), findsOneWidget);

      // Enter credentials
      await tester.enterText(
        find.byKey(const Key('email_field')),
        '[email protected]',
      );
      await tester.enterText(
        find.byKey(const Key('password_field')),
        'password123',
      );

      // Tap login
      await tester.tap(find.byKey(const Key('login_button')));
      await tester.pumpAndSettle();

      // Verify navigation to home
      expect(find.byType(HomeScreen), findsOneWidget);
      expect(find.text('Test User'), findsOneWidget);

      // Navigate to profile
      await tester.tap(find.byIcon(Icons.person));
      await tester.pumpAndSettle();

      // Tap logout
      await tester.tap(find.text('Logout'));
      await tester.pumpAndSettle();

      // Verify back on login screen
      expect(find.text('Welcome Back'), findsOneWidget);
    });

    testWidgets('session restoration on app restart', (tester) async {
      // First launch - login
      await tester.pumpWidget(const MyApp());
      await tester.pumpAndSettle();

      await tester.enterText(
        find.byKey(const Key('email_field')),
        '[email protected]',
      );
      await tester.enterText(
        find.byKey(const Key('password_field')),
        'password123',
      );
      await tester.tap(find.byKey(const Key('login_button')));
      await tester.pumpAndSettle();

      expect(find.byType(HomeScreen), findsOneWidget);

      // Simulate app restart
      await tester.pumpWidget(const MyApp());
      await tester.pumpAndSettle();

      // Should restore session and show home
      expect(find.byType(HomeScreen), findsOneWidget);
    });
  });
}
```

---

Version: 1.0.0
Last Updated: 2025-12-07

```

### reference.md

```markdown
# Flutter/Dart Reference Guide

## Platform Version Matrix

### Dart 3.5 (November 2025)
- Release: November 2025
- Key Features:
  - Pattern matching with exhaustiveness checking
  - Records and destructuring
  - Sealed classes and interfaces
  - Extension types with zero runtime cost
  - Macros (experimental)
  - Enhanced class modifiers (sealed, final, base, interface)

### Flutter 3.24 (November 2025)
- Release: November 2025
- Minimum OS: iOS 12.0+, Android API 21+
- Key Features:
  - Material 3 design system
  - Impeller rendering engine (default on iOS)
  - Adaptive layouts and responsive design
  - Enhanced platform views
  - Improved hot reload and DevTools

## Context7 Library Mappings

### Core Framework
```
/flutter/flutter              - Flutter framework and engine
/dart-lang/sdk                - Dart language SDK
/flutter/packages             - Official Flutter packages (go_router, etc.)
```

### State Management
```
/rrousselGit/riverpod         - Riverpod state management
/felangel/bloc                - BLoC pattern library
/jonataslaw/getx              - GetX framework
/alibaba/flutter_redux        - Redux for Flutter
```

### Navigation
```
/flutter/packages             - go_router official package
/theyakka/fluro               - Fluro router
```

### Networking
```
/cfug/dio                     - Dio HTTP client
/nickmeinhold/chopper         - Chopper HTTP client generator
```

### Storage
```
/isar/isar                    - Isar NoSQL database
/simonbengtsson/drift         - Drift SQL database
/nickmeinhold/hive            - Hive key-value storage
/nickmeinhold/shared_preferences - SharedPreferences wrapper
```

### UI Components
```
/Sub6Resources/flutter_html   - HTML rendering
/nickmeinhold/cached_network_image - Image caching
/nickmeinhold/flutter_svg     - SVG rendering
```

### Testing
```
/flutter/flutter              - flutter_test (built-in)
/dart-lang/mockito            - Mockito for Dart
/nickmeinhold/mocktail        - Mocktail testing
```

## Architecture Patterns

### Clean Architecture with Riverpod

Layer Organization:
```dart
// lib/
// ├── core/
// │   ├── error/
// │   │   ├── exceptions.dart
// │   │   └── failures.dart
// │   ├── network/
// │   │   └── api_client.dart
// │   └── utils/
// │       └── extensions.dart
// ├── features/
// │   └── user/
// │       ├── data/
// │       │   ├── datasources/
// │       │   │   ├── user_local_datasource.dart
// │       │   │   └── user_remote_datasource.dart
// │       │   ├── models/
// │       │   │   └── user_model.dart
// │       │   └── repositories/
// │       │       └── user_repository_impl.dart
// │       ├── domain/
// │       │   ├── entities/
// │       │   │   └── user.dart
// │       │   ├── repositories/
// │       │   │   └── user_repository.dart
// │       │   └── usecases/
// │       │       ├── get_user.dart
// │       │       └── update_user.dart
// │       └── presentation/
// │           ├── providers/
// │           │   └── user_provider.dart
// │           ├── screens/
// │           │   └── user_screen.dart
// │           └── widgets/
// │               └── user_card.dart
// └── main.dart

// Domain Layer - Entity
class User {
  final String id;
  final String name;
  final String email;
  final DateTime? createdAt;

  const User({
    required this.id,
    required this.name,
    required this.email,
    this.createdAt,
  });
}

// Domain Layer - Repository Interface
abstract class UserRepository {
  Future<User> getUser(String id);
  Future<User> updateUser(User user);
  Stream<User> watchUser(String id);
}

// Domain Layer - Use Case
class GetUserUseCase {
  final UserRepository _repository;

  const GetUserUseCase(this._repository);

  Future<User> call(String id) => _repository.getUser(id);
}

// Data Layer - Model
class UserModel extends User {
  const UserModel({
    required super.id,
    required super.name,
    required super.email,
    super.createdAt,
  });

  factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
    id: json['id'] as String,
    name: json['name'] as String,
    email: json['email'] as String,
    createdAt: json['created_at'] != null
        ? DateTime.parse(json['created_at'] as String)
        : null,
  );

  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'email': email,
    if (createdAt != null) 'created_at': createdAt!.toIso8601String(),
  };
}

// Data Layer - Repository Implementation
class UserRepositoryImpl implements UserRepository {
  final UserRemoteDataSource _remote;
  final UserLocalDataSource _local;

  UserRepositoryImpl(this._remote, this._local);

  @override
  Future<User> getUser(String id) async {
    try {
      final user = await _remote.fetchUser(id);
      await _local.cacheUser(user);
      return user;
    } on NetworkException {
      final cached = await _local.getCachedUser(id);
      if (cached != null) return cached;
      rethrow;
    }
  }

  @override
  Future<User> updateUser(User user) async {
    final updated = await _remote.updateUser(user as UserModel);
    await _local.cacheUser(updated);
    return updated;
  }

  @override
  Stream<User> watchUser(String id) => _local.watchUser(id);
}

// Presentation Layer - Provider
@riverpod
UserRepository userRepository(Ref ref) {
  return UserRepositoryImpl(
    ref.read(userRemoteDataSourceProvider),
    ref.read(userLocalDataSourceProvider),
  );
}

@riverpod
class UserController extends _$UserController {
  @override
  FutureOr<User?> build(String userId) async {
    return ref.read(userRepositoryProvider).getUser(userId);
  }

  Future<void> refresh() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(userRepositoryProvider).getUser(arg),
    );
  }

  Future<void> update(User user) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(userRepositoryProvider).updateUser(user),
    );
  }
}
```

## Concurrency Patterns

### Isolates for Heavy Computation

Simple Compute:
```dart
// Using compute for one-off operations
Future<List<ProcessedItem>> processItems(List<RawItem> items) async {
  return compute(_processItemsIsolate, items);
}

List<ProcessedItem> _processItemsIsolate(List<RawItem> items) {
  return items.map((item) => ProcessedItem.from(item)).toList();
}
```

Long-Running Isolate:
```dart
class ImageProcessor {
  late final Isolate _isolate;
  late final ReceivePort _receivePort;
  late final SendPort _sendPort;
  bool _isInitialized = false;

  Future<void> spawn() async {
    if (_isInitialized) return;

    _receivePort = ReceivePort();
    _isolate = await Isolate.spawn(
      _isolateEntry,
      _receivePort.sendPort,
    );
    _sendPort = await _receivePort.first as SendPort;
    _isInitialized = true;
  }

  Future<Uint8List> processImage(Uint8List imageData) async {
    if (!_isInitialized) {
      throw StateError('ImageProcessor not initialized. Call spawn() first.');
    }

    final responsePort = ReceivePort();
    _sendPort.send(_ProcessImageRequest(imageData, responsePort.sendPort));
    final result = await responsePort.first;

    if (result is _ProcessImageError) {
      throw result.error;
    }
    return result as Uint8List;
  }

  void dispose() {
    _isolate.kill(priority: Isolate.immediate);
    _receivePort.close();
  }

  static void _isolateEntry(SendPort sendPort) {
    final receivePort = ReceivePort();
    sendPort.send(receivePort.sendPort);

    receivePort.listen((message) {
      if (message is _ProcessImageRequest) {
        try {
          final processed = _heavyImageProcessing(message.imageData);
          message.replyPort.send(processed);
        } catch (e) {
          message.replyPort.send(_ProcessImageError(e));
        }
      }
    });
  }

  static Uint8List _heavyImageProcessing(Uint8List data) {
    // Heavy computation here
    return data;
  }
}

class _ProcessImageRequest {
  final Uint8List imageData;
  final SendPort replyPort;
  _ProcessImageRequest(this.imageData, this.replyPort);
}

class _ProcessImageError {
  final Object error;
  _ProcessImageError(this.error);
}
```

## Navigation Patterns

### Advanced go_router Configuration

Nested Navigation:
```dart
final router = GoRouter(
  initialLocation: '/',
  routes: [
    // Root route
    GoRoute(
      path: '/',
      redirect: (context, state) => '/home',
    ),

    // Main app with bottom navigation
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        return MainScaffold(navigationShell: navigationShell);
      },
      branches: [
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/home',
              name: 'home',
              builder: (context, state) => const HomeScreen(),
              routes: [
                GoRoute(
                  path: 'detail/:id',
                  name: 'home-detail',
                  builder: (context, state) => DetailScreen(
                    id: state.pathParameters['id']!,
                  ),
                ),
              ],
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/search',
              name: 'search',
              builder: (context, state) => const SearchScreen(),
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/profile',
              name: 'profile',
              builder: (context, state) => const ProfileScreen(),
            ),
          ],
        ),
      ],
    ),

    // Auth routes (outside main scaffold)
    GoRoute(
      path: '/login',
      name: 'login',
      builder: (context, state) => const LoginScreen(),
    ),
    GoRoute(
      path: '/register',
      name: 'register',
      builder: (context, state) => const RegisterScreen(),
    ),
  ],

  // Global redirect for auth
  redirect: (context, state) {
    final isLoggedIn = ref.read(authStateProvider).valueOrNull != null;
    final isAuthRoute = state.matchedLocation.startsWith('/login') ||
        state.matchedLocation.startsWith('/register');

    if (!isLoggedIn && !isAuthRoute) {
      return '/login?redirect=${state.uri}';
    }

    if (isLoggedIn && isAuthRoute) {
      final redirect = state.uri.queryParameters['redirect'];
      return redirect ?? '/home';
    }

    return null;
  },
);
```

Deep Linking Configuration:
```dart
// android/app/src/main/AndroidManifest.xml
// Add intent filters for deep linking

// iOS: ios/Runner/Info.plist
// Add URL schemes and associated domains

// Router configuration for deep links
GoRouter(
  routes: [
    GoRoute(
      path: '/product/:id',
      builder: (context, state) {
        final productId = state.pathParameters['id']!;
        final query = state.uri.queryParameters;
        return ProductScreen(
          id: productId,
          variant: query['variant'],
        );
      },
    ),
  ],
);
```

## Performance Optimization

### Widget Optimization

Const Constructors:
```dart
// Good - uses const
class MyWidget extends StatelessWidget {
  const MyWidget({super.key}); // const constructor

  @override
  Widget build(BuildContext context) {
    return const Column(
      children: [
        Text('Static text'), // const widget
        Icon(Icons.star),    // const widget
      ],
    );
  }
}

// Usage
const MyWidget(); // const instantiation
```

RepaintBoundary:
```dart
class ComplexAnimatedWidget extends StatelessWidget {
  const ComplexAnimatedWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Static header - doesn't need repaint
        const Header(),

        // Animated content - isolated repaint
        RepaintBoundary(
          child: AnimatedContent(),
        ),

        // Static footer - doesn't need repaint
        const Footer(),
      ],
    );
  }
}
```

ListView Optimization:
```dart
class OptimizedList extends StatelessWidget {
  final List<Item> items;
  const OptimizedList({required this.items, super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      // Add extent for better performance
      itemExtent: 72.0,
      // Or use prototypeItem for variable heights
      // prototypeItem: const ItemCard(item: prototypeItem),
      itemBuilder: (context, index) {
        final item = items[index];
        return ItemCard(
          key: ValueKey(item.id), // Stable key
          item: item,
        );
      },
    );
  }
}
```

### Memory Management

Image Caching:
```dart
class ImageCacheManager {
  static final ImageCacheManager _instance = ImageCacheManager._();
  static ImageCacheManager get instance => _instance;

  ImageCacheManager._();

  void configureCache() {
    // Configure image cache size
    PaintingBinding.instance.imageCache.maximumSize = 100;
    PaintingBinding.instance.imageCache.maximumSizeBytes = 50 << 20; // 50 MB
  }

  void clearCache() {
    PaintingBinding.instance.imageCache.clear();
    PaintingBinding.instance.imageCache.clearLiveImages();
  }
}
```

Provider Disposal:
```dart
@riverpod
class ResourceController extends _$ResourceController {
  StreamSubscription? _subscription;
  Timer? _timer;

  @override
  FutureOr<Resource?> build() {
    // Setup
    _subscription = someStream.listen(_onData);
    _timer = Timer.periodic(
      const Duration(seconds: 30),
      (_) => _refresh(),
    );

    // Cleanup on disposal
    ref.onDispose(() {
      _subscription?.cancel();
      _timer?.cancel();
    });

    return null;
  }

  void _onData(dynamic data) {
    // Handle data
  }

  void _refresh() {
    // Refresh logic
  }
}
```

## Testing Frameworks

### flutter_test Configuration
```dart
// test/widget_test.dart
void main() {
  group('UserScreen', () {
    late ProviderContainer container;

    setUp(() {
      container = ProviderContainer(overrides: [
        userRepositoryProvider.overrideWithValue(MockUserRepository()),
      ]);
    });

    tearDown(() => container.dispose());

    testWidgets('renders user name', (tester) async {
      await tester.pumpWidget(
        UncontrolledProviderScope(
          container: container,
          child: const MaterialApp(home: UserScreen(userId: '1')),
        ),
      );

      await tester.pumpAndSettle();
      expect(find.text('Test User'), findsOneWidget);
    });
  });
}
```

### Integration Testing
```dart
// integration_test/app_test.dart
import 'package:integration_test/integration_test.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('end-to-end test', () {
    testWidgets('login and view profile', (tester) async {
      await tester.pumpWidget(const MyApp());

      // Enter credentials
      await tester.enterText(
        find.byKey(const Key('email_field')),
        '[email protected]',
      );
      await tester.enterText(
        find.byKey(const Key('password_field')),
        'password123',
      );

      // Tap login button
      await tester.tap(find.byKey(const Key('login_button')));
      await tester.pumpAndSettle();

      // Verify navigation to home
      expect(find.byType(HomeScreen), findsOneWidget);

      // Navigate to profile
      await tester.tap(find.byIcon(Icons.person));
      await tester.pumpAndSettle();

      // Verify profile screen
      expect(find.text('Test User'), findsOneWidget);
    });
  });
}
```

### Golden Tests
```dart
// test/golden_test.dart
void main() {
  testWidgets('UserCard golden test', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData.light(),
        home: Scaffold(
          body: UserCard(
            user: const User(
              id: '1',
              name: 'Test User',
              email: '[email protected]',
            ),
          ),
        ),
      ),
    );

    await expectLater(
      find.byType(UserCard),
      matchesGoldenFile('goldens/user_card.png'),
    );
  });
}

// Run with: flutter test --update-goldens
```

---

Version: 1.0.0
Last Updated: 2025-12-07

```

moai-lang-flutter | SkillHub