Claude Code Plugins

Community-maintained marketplace

Feedback

moai-lang-dart

@kivo360/quickhooks
0
0

Dart best practices with Flutter mobile development, async programming, and server-side Dart for 2025

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name moai-lang-dart
version 2.0.0
created Thu Nov 06 2025 00:00:00 GMT+0000 (Coordinated Universal Time)
updated Thu Nov 06 2025 00:00:00 GMT+0000 (Coordinated Universal Time)
status active
description Dart best practices with Flutter mobile development, async programming, and server-side Dart for 2025
keywords dart, programming, flutter, mobile, async, server-side, development
allowed-tools Read, Write, Edit, Bash, WebFetch, WebSearch

Dart Development Mastery

Modern Dart Development with 2025 Best Practices

Comprehensive Dart development guidance covering Flutter mobile applications, async programming patterns, server-side development, and cross-platform solutions using the latest tools and frameworks.

What It Does

Flutter Mobile Development

  • Mobile App Development: Flutter 3.x with Material Design 3 and Cupertino widgets
  • State Management: Provider, Riverpod, BLoC patterns for scalable applications
  • Navigation: Go Router for declarative routing and deep linking
  • Performance: Widget optimization, lazy loading, memory management

Server-Side Development

  • Web APIs: Shelf, Dart Frog, or Aqueduct for backend services
  • Database Integration: PostgreSQL, MongoDB with async drivers
  • Real-time Communication: WebSockets, gRPC with Dart's async capabilities
  • Testing: Unit tests, widget tests, integration tests with Dart test framework

Cross-Platform Development

  • Flutter for Multiple Platforms: iOS, Android, Web, Desktop, and Embedded
  • Shared Codebases: Dart packages for business logic across platforms
  • Platform-Specific APIs: Method channels for native integration

When to Use

Perfect Scenarios

  • Building cross-platform mobile applications with Flutter
  • Developing scalable Flutter apps with modern state management
  • Creating server-side APIs with Dart
  • Implementing real-time applications with WebSockets
  • Building web applications with Flutter Web
  • Developing desktop applications with Flutter Desktop
  • Creating embedded systems applications

Common Triggers

  • "Create Flutter app"
  • "Build Dart web API"
  • "Implement Flutter state management"
  • "Optimize Flutter performance"
  • "Test Flutter application"
  • "Dart best practices"

Tool Version Matrix (2025-11-06)

Core Dart/Flutter

  • Dart: 3.5.x (current) / 3.4.x (LTS)
  • Flutter: 3.24.x (current) / 3.22.x (LTS)
  • Dart SDK: 3.5.0
  • Package Manager: pub (built-in)

Flutter Frameworks

  • Material Design: 3.x (Material 3)
  • Cupertino Widgets: iOS 17+ support
  • Go Router: 13.x - Declarative routing
  • Riverpod: 2.5.x - Reactive state management
  • BLoC: 8.1.x - State management library

Testing Tools

  • Dart Test: Built-in testing framework
  • Flutter Test: Widget and integration testing
  • Mockito: 5.4.x - Mocking framework
  • Golden Tests: Widget screenshot testing
  • Integration Test: End-to-end testing

Development Tools

  • Flutter CLI: 3.24.x
  • Dart DevTools: Web-based debugging tools
  • Android Studio: Dolphin 2024.x
  • VS Code: Flutter extension

Backend Tools

  • Shelf: 1.4.x - Web server framework
  • Dart Frog: 1.0.x - Server-side framework
  • Aqueduct: 7.x - Full-stack framework
  • MongoDB Dart Driver: 4.12.x

Ecosystem Overview

Package Management

# Create new Flutter project
flutter create my_app
flutter create --org com.example --platforms=web,desktop my_app

# Create new Dart project
dart create my_dart_app

# Add dependencies
flutter pub add provider riverpod go_router
flutter pub add dio retrofit json_annotation
dart pub add shelf shelf_router

# Get dependencies
flutter pub get
dart pub get

# Run and build
flutter run
flutter run --release
flutter build apk
flutter build web
dart run

Project Structure (2025 Best Practice)

my_flutter_app/
├── lib/
│   ├── main.dart                 # App entry point
│   ├── app.dart                  # App configuration
│   ├── core/                     # Core utilities
│   │   ├── constants/           # App constants
│   │   ├── errors/              # Custom error classes
│   │   ├── extensions/          # Dart extensions
│   │   ├── network/             # Network configuration
│   │   ├── themes/              # App themes
│   │   └── utils/               # Utility functions
│   ├── features/                # Feature modules
│   │   ├── authentication/      # Auth feature
│   │   │   ├── data/            # Data layer (repositories, data sources)
│   │   │   ├── domain/          # Domain layer (entities, use cases)
│   │   │   └── presentation/    # UI layer (pages, widgets, providers)
│   │   ├── user_profile/        # User profile feature
│   │   └── settings/            # Settings feature
│   ├── shared/                  # Shared components
│   │   ├── widgets/             # Reusable widgets
│   │   ├── models/              # Shared data models
│   │   ├── services/            # Shared services
│   │   └── providers/           # Shared providers
│   └── routes/                  # App routes
├── test/                        # Test files
│   ├── unit/                    # Unit tests
│   ├── widget/                  # Widget tests
│   └── integration/             # Integration tests
├── assets/                      # Static assets
├── pubspec.yaml                 # Dependencies
└── analysis_options.yaml        # Linting rules

Modern Development Patterns

Dart 3.x Language Features

// Enhanced patterns with records and pattern matching
sealed class NetworkResult<T> {
  const NetworkResult();
}

class Success<T> extends NetworkResult<T> {
  final T data;
  const Success(this.data);
}

class Error<T> extends NetworkResult<T> {
  final String message;
  final Exception? exception;
  const Error(this.message, [this.exception]);
}

class Loading<T> extends NetworkResult<T> {
  const Loading();
}

// Pattern matching with switch expressions
T handleNetworkResult<T>(NetworkResult<T> result) {
  return switch (result) {
    Success(data: final data) => data,
    Error(message: final message) => throw Exception(message),
    Loading() => throw StateError('Still loading'),
  };
}

// Records for lightweight data structures
typedef UserInfo = (String name, int age, String email);

class UserService {
  UserInfo getUserInfo(int id) {
    return ('John Doe', 30, 'john@example.com');
  }
  
  void printUserInfo(UserInfo user) {
    final (name, age, email) = user;
    print('$name, $age years old, email: $email');
  }
}

// Enhanced type inference and const constructors
class AppConfig {
  static const apiBaseUrl = String.fromEnvironment('API_BASE_URL', defaultValue: 'https://api.example.com');
  static const appVersion = String.fromEnvironment('APP_VERSION', defaultValue: '1.0.0');
  static const isDebug = bool.fromEnvironment('DEBUG', defaultValue: false);
}

// Enhanced enums with methods and properties
enum ThemeMode {
  light._('Light Theme', '☀️'),
  dark._('Dark Theme', '🌙'),
  system._('System Theme', '💻');
  
  const ThemeMode._(this.displayName, this.icon);
  
  final String displayName;
  final String icon;
  
  Brightness get brightness => switch (this) {
    ThemeMode.light => Brightness.light,
    ThemeMode.dark => Brightness.dark,
    ThemeMode.system => PlatformDispatcher.instance.platformBrightness,
  };
}

// Extension methods for enhanced APIs
extension StringExtension on String {
  bool get isValidEmail {
    return RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(this);
  }
  
  String get capitalize {
    return '${this[0].toUpperCase()}${substring(1)}';
  }
  
  String truncate(int length, {String suffix = '...'}) {
    if (this.length <= length) return this;
    return '${substring(0, length)}$suffix';
  }
}

Modern Flutter State Management with Riverpod

// Provider setup with Riverpod 2.x
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Model classes with immutability
@immutable
class User {
  final String id;
  final String name;
  final String email;
  final String avatarUrl;
  
  const User({
    required this.id,
    required this.name,
    required this.email,
    required this.avatarUrl,
  });
  
  User copyWith({
    String? id,
    String? name,
    String? email,
    String? avatarUrl,
  }) {
    return User(
      id: id ?? this.id,
      name: name ?? this.name,
      email: email ?? this.email,
      avatarUrl: avatarUrl ?? this.avatarUrl,
    );
  }
  
  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is User && 
           other.id == id && 
           other.name == name && 
           other.email == email;
  }
  
  @override
  int get hashCode => id.hashCode ^ name.hashCode ^ email.hashCode;
}

// Repository interface
abstract class UserRepository {
  Future<User> getUser(String userId);
  Future<List<User>> getUsers({int page = 1, int limit = 20});
  Future<User> updateUser(User user);
  Future<void> deleteUser(String userId);
}

// Implementation with HTTP client
class HttpUserRepository implements UserRepository {
  final Dio _dio;
  
  HttpUserRepository(this._dio);
  
  @override
  Future<User> getUser(String userId) async {
    try {
      final response = await _dio.get('/users/$userId');
      return User.fromJson(response.data);
    } on DioException catch (e) {
      throw UserRepositoryException('Failed to get user: $e');
    }
  }
  
  @override
  Future<List<User>> getUsers({int page = 1, int limit = 20}) async {
    try {
      final response = await _dio.get('/users', queryParameters: {
        'page': page,
        'limit': limit,
      });
      
      return (response.data as List)
          .map((json) => User.fromJson(json))
          .toList();
    } on DioException catch (e) {
      throw UserRepositoryException('Failed to get users: $e');
    }
  }
  
  @override
  Future<User> updateUser(User user) async {
    try {
      final response = await _dio.put('/users/${user.id}', data: user.toJson());
      return User.fromJson(response.data);
    } on DioException catch (e) {
      throw UserRepositoryException('Failed to update user: $e');
    }
  }
  
  @override
  Future<void> deleteUser(String userId) async {
    try {
      await _dio.delete('/users/$userId');
    } on DioException catch (e) {
      throw UserRepositoryException('Failed to delete user: $e');
    }
  }
}

// Riverpod providers
final dioProvider = Provider<Dio>((ref) {
  final dio = Dio(BaseOptions(baseUrl: AppConfig.apiBaseUrl));
  dio.interceptors.add(LogInterceptor());
  return dio;
});

final userRepositoryProvider = Provider<UserRepository>((ref) {
  return HttpUserRepository(ref.read(dioProvider));
});

// Async providers for data fetching
final userProvider = FutureProvider.family<User, String>((ref, userId) async {
  final repository = ref.watch(userRepositoryProvider);
  return repository.getUser(userId);
});

final usersProvider = AsyncNotifierProvider<UsersNotifier, List<User>>(UsersNotifier.new);

// Notifier for managing state
class UsersNotifier extends AsyncNotifier<List<User>> {
  int _page = 1;
  final int _limit = 20;
  bool _hasMore = true;
  
  @override
  Future<List<User>> build() async {
    return _loadUsers();
  }
  
  Future<List<User>> _loadUsers() async {
    if (state.isLoading || !_hasMore) return state.value ?? [];
    
    state = const AsyncLoading();
    
    try {
      final repository = ref.read(userRepositoryProvider);
      final newUsers = await repository.getUsers(page: _page, limit: _limit);
      
      if (newUsers.length < _limit) {
        _hasMore = false;
      }
      
      final currentUsers = state.value ?? [];
      final updatedUsers = _page == 1 ? newUsers : [...currentUsers, ...newUsers];
      
      state = AsyncData(updatedUsers);
      _page++;
      
      return updatedUsers;
    } catch (error, stackTrace) {
      state = AsyncError(error, stackTrace);
      rethrow;
    }
  }
  
  Future<void> loadMoreUsers() async {
    await _loadUsers();
  }
  
  Future<void> refresh() async {
    _page = 1;
    _hasMore = true;
    await _loadUsers();
  }
}

// State management with Notifier for single user
final userNotifierProvider = AsyncNotifierProvider.family<UserNotifier, User, String>(UserNotifier.new);

class UserNotifier extends FamilyAsyncNotifier<User, String> {
  @override
  Future<User> build(String arg) async {
    final repository = ref.read(userRepositoryProvider);
    return repository.getUser(arg);
  }
  
  Future<void> updateUser(User user) async {
    state = const AsyncLoading();
    
    try {
      final repository = ref.read(userRepositoryProvider);
      final updatedUser = await repository.updateUser(user);
      state = AsyncData(updatedUser);
    } catch (error, stackTrace) {
      state = AsyncError(error, stackTrace);
    }
  }
}

// App-wide state providers
final appThemeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.system);
final appLocaleProvider = StateProvider<Locale>((ref) => const Locale('en', 'US'));

Modern Flutter UI with Material 3

// Modern app with Material 3 and go_router
class MyApp extends ConsumerWidget {
  const MyApp({super.key});
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final appRouter = ref.watch(goRouterProvider);
    final themeMode = ref.watch(appThemeProvider);
    final appLocale = ref.watch(appLocaleProvider);
    
    return MaterialApp.router(
      title: 'My Flutter App',
      debugShowCheckedModeBanner: false,
      themeMode: themeMode,
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      locale: appLocale,
      supportedLocales: const [
        Locale('en', 'US'),
        Locale('es', 'ES'),
        Locale('fr', 'FR'),
      ],
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      routerConfig: appRouter,
    );
  }
}

// Modern theme configuration
class AppTheme {
  static ThemeData get lightTheme {
    return ThemeData(
      useMaterial3: true,
      colorScheme: ColorScheme.fromSeed(
        seedColor: const Color(0xFF6750A4),
        brightness: Brightness.light,
      ),
      appBarTheme: const AppBarTheme(
        centerTitle: true,
        elevation: 0,
        scrolledUnderElevation: 1,
      ),
      cardTheme: CardTheme(
        elevation: 2,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(16),
        ),
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ElevatedButton.styleFrom(
          elevation: 1,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        ),
      ),
      inputDecorationTheme: InputDecorationTheme(
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
        ),
        contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      ),
    );
  }
  
  static ThemeData get darkTheme {
    return ThemeData(
      useMaterial3: true,
      colorScheme: ColorScheme.fromSeed(
        seedColor: const Color(0xFF6750A4),
        brightness: Brightness.dark,
      ),
      appBarTheme: const AppBarTheme(
        centerTitle: true,
        elevation: 0,
        scrolledUnderElevation: 1,
      ),
      cardTheme: CardTheme(
        elevation: 2,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(16),
        ),
      ),
    );
  }
}

// Modern user list widget with state management
class UserListScreen extends ConsumerWidget {
  const UserListScreen({super.key});
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final usersAsync = ref.watch(usersProvider);
    
    return Scaffold(
      appBar: AppBar(
        title: const Text('Users'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () {
              ref.read(usersProvider.notifier).refresh();
            },
          ),
        ],
      ),
      body: RefreshIndicator(
        onRefresh: () async {
          await ref.read(usersProvider.notifier).refresh();
        },
        child: usersAsync.when(
          data: (users) {
            if (users.isEmpty) {
              return const EmptyStateWidget(
                message: 'No users found',
                icon: Icons.people_outline,
              );
            }
            
            return NotificationListener<ScrollNotification>(
              onNotification: (scrollInfo) {
                if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
                  ref.read(usersProvider.notifier).loadMoreUsers();
                }
                return false;
              },
              child: ListView.builder(
                padding: const EdgeInsets.all(16),
                itemCount: users.length + 1, // +1 for loading indicator
                itemBuilder: (context, index) {
                  if (index == users.length) {
                    return const LoadingIndicator();
                  }
                  
                  return UserCard(user: users[index]);
                },
              ),
            );
          },
          loading: () => const Center(child: CircularProgressIndicator()),
          error: (error, stack) => ErrorWidget(
            error: error,
            onRetry: () {
              ref.read(usersProvider.notifier).refresh();
            },
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          context.go('/add-user');
        },
        icon: const Icon(Icons.add),
        label: const Text('Add User'),
      ),
    );
  }
}

// Modern user card widget
class UserCard extends ConsumerWidget {
  final User user;
  
  const UserCard({
    super.key,
    required this.user,
  });
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      child: InkWell(
        onTap: () {
          context.go('/users/${user.id}');
        },
        borderRadius: BorderRadius.circular(16),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              CircleAvatar(
                radius: 28,
                backgroundImage: NetworkImage(user.avatarUrl),
                backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
                child: user.avatarUrl.isEmpty
                    ? Text(
                        user.name.isNotEmpty ? user.name[0].toUpperCase() : '?',
                        style: Theme.of(context).textTheme.titleLarge,
                      )
                    : null,
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      user.name,
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                    const SizedBox(height: 4),
                    Text(
                      user.email,
                      style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                        color: Theme.of(context).colorScheme.onSurfaceVariant,
                      ),
                    ),
                  ],
                ),
              ),
              IconButton(
                icon: const Icon(Icons.more_vert),
                onPressed: () {
                  _showUserMenu(context, ref, user);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
  
  void _showUserMenu(BuildContext context, WidgetRef ref, User user) {
    showModalBottomSheet(
      context: context,
      builder: (context) {
        return SafeArea(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ListTile(
                leading: const Icon(Icons.edit),
                title: const Text('Edit User'),
                onTap: () {
                  Navigator.pop(context);
                  context.go('/users/${user.id}/edit');
                },
              ),
              ListTile(
                leading: const Icon(Icons.delete, color: Colors.red),
                title: const Text('Delete User', style: TextStyle(color: Colors.red)),
                onTap: () async {
                  Navigator.pop(context);
                  
                  final confirmed = await _showDeleteConfirmation(context);
                  if (confirmed) {
                    try {
                      await ref.read(userRepositoryProvider).deleteUser(user.id);
                      if (context.mounted) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          const SnackBar(content: Text('User deleted successfully')),
                        );
                      }
                    } catch (error) {
                      if (context.mounted) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(content: Text('Failed to delete user: $error')),
                        );
                      }
                    }
                  }
                },
              ),
            ],
          ),
        );
      },
    );
  }
  
  Future<bool> _showDeleteConfirmation(BuildContext context) async {
    return await showDialog<bool>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('Delete User'),
          content: Text('Are you sure you want to delete ${user.name}?'),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context, false),
              child: const Text('Cancel'),
            ),
            TextButton(
              onPressed: () => Navigator.pop(context, true),
              style: TextButton.styleFrom(foregroundColor: Colors.red),
              child: const Text('Delete'),
            ),
          ],
        );
      },
    ) ?? false;
  }
}

Go Router for Navigation

// Go router configuration
final goRouterProvider = Provider<GoRouter>((ref) {
  return GoRouter(
    initialLocation: '/',
    debugLogDiagnostics: AppConfig.isDebug,
    routes: [
      // Shell route for navigation
      ShellRoute(
        builder: (context, state, child) {
          return MainScaffold(child: child);
        },
        routes: [
          // Home route
          GoRoute(
            path: '/',
            builder: (context, state) => const HomeScreen(),
          ),
          
          // User routes
          GoRoute(
            path: '/users',
            builder: (context, state) => const UserListScreen(),
            routes: [
              GoRoute(
                path: '/:userId',
                builder: (context, state) {
                  final userId = state.pathParameters['userId']!;
                  return UserDetailScreen(userId: userId);
                },
                routes: [
                  GoRoute(
                    path: '/edit',
                    builder: (context, state) {
                      final userId = state.pathParameters['userId']!;
                      return EditUserScreen(userId: userId);
                    },
                  ),
                ],
              ),
            ],
          ),
          
          // Settings route
          GoRoute(
            path: '/settings',
            builder: (context, state) => const SettingsScreen(),
            routes: [
              GoRoute(
                path: '/profile',
                builder: (context, state) => const ProfileSettingsScreen(),
              ),
              GoRoute(
                path: '/appearance',
                builder: (context, state) => const AppearanceSettingsScreen(),
              ),
            ],
          ),
        ],
      ),
      
      // Standalone routes
      GoRoute(
        path: '/add-user',
        builder: (context, state) => const AddUserScreen(),
      ),
      
      GoRoute(
        path: '/login',
        builder: (context, state) => const LoginScreen(),
      ),
    ],
    
    // Error handling
    errorBuilder: (context, state) => ErrorScreen(error: state.error),
    
    // Redirects
    redirect: (context, state) {
      // Example: redirect to login if not authenticated
      final isAuthenticated = true; // Check authentication status
      
      if (!isAuthenticated && !state.location.startsWith('/login')) {
        return '/login';
      }
      
      return null;
    },
  );
});

// Main scaffold with bottom navigation
class MainScaffold extends ConsumerWidget {
  const MainScaffold({
    required this.child,
    super.key,
  });
  
  final Widget child;
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final selectedIndex = ref.watch(bottomNavigationIndexProvider);
    
    return Scaffold(
      body: child,
      bottomNavigationBar: NavigationBar(
        selectedIndex: selectedIndex,
        onDestinationSelected: (index) {
          ref.read(bottomNavigationIndexProvider.notifier).state = index;
          
          switch (index) {
            case 0:
              context.go('/');
              break;
            case 1:
              context.go('/users');
              break;
            case 2:
              context.go('/settings');
              break;
          }
        },
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.home_outlined),
            selectedIcon: Icon(Icons.home),
            label: 'Home',
          ),
          NavigationDestination(
            icon: Icon(Icons.people_outlined),
            selectedIcon: Icon(Icons.people),
            label: 'Users',
          ),
          NavigationDestination(
            icon: Icon(Icons.settings_outlined),
            selectedIcon: Icon(Icons.settings),
            label: 'Settings',
          ),
        ],
      ),
    );
  }
}

// Provider for bottom navigation state
final bottomNavigationIndexProvider = StateProvider<int>((ref) => 0);

Performance Considerations

Widget Performance

// Performance-optimized widgets with const constructors
class OptimizedUserCard extends StatelessWidget {
  final User user;
  final VoidCallback? onTap;
  final VoidCallback? onEdit;
  final VoidCallback? onDelete;
  
  const OptimizedUserCard({
    super.key,
    required this.user,
    this.onTap,
    this.onEdit,
    this.onDelete,
  });
  
  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              // Use Hero widget for smooth avatar transitions
              Hero(
                tag: 'user-avatar-${user.id}',
                child: UserAvatar(
                  imageUrl: user.avatarUrl,
                  name: user.name,
                  size: 48,
                ),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: _buildUserInfo(),
              ),
              _buildActionButtons(),
            ],
          ),
        ),
      ),
    );
  }
  
  Widget _buildUserInfo() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(
          user.name,
          style: const TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w600,
          ),
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
        ),
        const SizedBox(height: 4),
        Text(
          user.email,
          style: const TextStyle(
            fontSize: 14,
            color: Colors.grey,
          ),
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
        ),
      ],
    );
  }
  
  Widget _buildActionButtons() {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        if (onEdit != null)
          IconButton(
            icon: const Icon(Icons.edit_outlined),
            onPressed: onEdit,
            visualDensity: VisualDensity.compact,
          ),
        if (onDelete != null)
          IconButton(
            icon: const Icon(Icons.delete_outline),
            onPressed: onDelete,
            visualDensity: VisualDensity.compact,
          ),
      ],
    );
  }
}

// Efficient image loading and caching
class CachedNetworkImage extends StatefulWidget {
  final String imageUrl;
  final double? width;
  final double? height;
  final Widget? placeholder;
  final Widget? errorWidget;
  final BoxFit fit;
  
  const CachedNetworkImage({
    super.key,
    required this.imageUrl,
    this.width,
    this.height,
    this.placeholder,
    this.errorWidget,
    this.fit = BoxFit.cover,
  });
  
  @override
  State<CachedNetworkImage> createState() => _CachedNetworkImageState();
}

class _CachedNetworkImageState extends State<CachedNetworkImage> {
  final Map<String, ui.Image> _imageCache = {};
  bool _isLoading = true;
  bool _hasError = false;
  
  @override
  void initState() {
    super.initState();
    _loadImage();
  }
  
  Future<void> _loadImage() async {
    if (_imageCache.containsKey(widget.imageUrl)) {
      if (mounted) {
        setState(() {
          _isLoading = false;
        });
      }
      return;
    }
    
    try {
      final image = await _fetchImage();
      _imageCache[widget.imageUrl] = image;
      
      if (mounted) {
        setState(() {
          _isLoading = false;
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _isLoading = false;
          _hasError = true;
        });
      }
    }
  }
  
  Future<ui.Image> _fetchImage() async {
    final completer = Completer<ui.Image>();
    final codec = await ui.instantiateImageCodec(
      await NetworkAssetBundle(Uri.parse(widget.imageUrl)).load(widget.imageUrl).then((bytes) => bytes.buffer.asUint8List()),
    );
    final frame = await codec.getNextFrame();
    completer.complete(frame.image);
    return completer.future;
  }
  
  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return widget.placeholder ??
          Container(
            width: widget.width,
            height: widget.height,
            color: Colors.grey[200],
            child: const Center(
              child: CircularProgressIndicator(),
            ),
          );
    }
    
    if (_hasError) {
      return widget.errorWidget ??
          Container(
            width: widget.width,
            height: widget.height,
            color: Colors.grey[200],
            child: const Icon(Icons.error),
          );
    }
    
    final image = _imageCache[widget.imageUrl];
    return CustomPaint(
      size: Size(widget.width ?? double.infinity, widget.height ?? double.infinity),
      painter: _ImagePainter(image: image!, fit: widget.fit),
    );
  }
}

class _ImagePainter extends CustomPainter {
  final ui.Image image;
  final BoxFit fit;
  
  _ImagePainter({required this.image, required this.fit});
  
  @override
  void paint(Canvas canvas, Size size) {
    final imageSize = Size(image.width.toDouble(), image.height.toDouble());
    final scales = _calculateScales(imageSize, size);
    
    final paint = Paint()
      ..isAntiAlias = true
      ..filterQuality = FilterQuality.high;
    
    canvas.save();
    
    if (fit == BoxFit.cover) {
      canvas.scale(scales.dx, scales.dy);
      canvas.drawImageRect(
        image,
        Rect.fromLTWH(0, 0, imageSize.width, imageSize.height),
        Rect.fromLTWH(0, 0, size.width / scales.dx, size.height / scales.dy),
        paint,
      );
    }
    
    canvas.restore();
  }
  
  Offset _calculateScales(Size inputSize, Size outputSize) {
    final scaleX = outputSize.width / inputSize.width;
    final scaleY = outputSize.height / inputSize.height;
    return Offset(scaleX, scaleY);
  }
  
  @override
  bool shouldRepaint(covariant _ImagePainter oldDelegate) {
    return image != oldDelegate.image || fit != oldDelegate.fit;
  }
}

// ListView with lazy loading and recycling
class OptimizedListView<T> extends StatelessWidget {
  final List<T> items;
  final Widget Function(BuildContext context, T item, int index) itemBuilder;
  final VoidCallback? onLoadMore;
  final bool hasMore;
  final bool isLoading;
  
  const OptimizedListView({
    super.key,
    required this.items,
    required this.itemBuilder,
    this.onLoadMore,
    this.hasMore = false,
    this.isLoading = false,
  });
  
  @override
  Widget build(BuildContext context) {
    return NotificationListener<ScrollNotification>(
      onNotification: (notification) {
        if (notification is ScrollEndNotification &&
            notification.metrics.extentAfter == 0 &&
            hasMore &&
            !isLoading &&
            onLoadMore != null) {
          onLoadMore!();
        }
        return false;
      },
      child: ListView.builder(
        itemCount: items.length + (hasMore ? 1 : 0),
        itemBuilder: (context, index) {
          if (index == items.length) {
            return const Center(
              child: Padding(
                padding: EdgeInsets.all(16.0),
                child: CircularProgressIndicator(),
              ),
            );
          }
          
          return itemBuilder(context, items[index], index);
        },
      ),
    );
  }
}

Memory Management

// Efficient memory usage with ImageCache
class ImageCacheManager {
  static final ImageCacheManager _instance = ImageCacheManager._internal();
  factory ImageCacheManager() => _instance;
  ImageCacheManager._internal();
  
  final PaintingBinding _paintingBinding = PaintingBinding.instance;
  final Map<String, ui.Image> _memoryCache = {};
  final int _maxCacheSize = 100 * 1024 * 1024; // 100MB
  int _currentCacheSize = 0;
  
  Future<ui.Image?> getImage(String url) async {
    // Check memory cache first
    if (_memoryCache.containsKey(url)) {
      return _memoryCache[url];
    }
    
    // Check painting binding cache
    final cachedImage = _paintingBinding.imageCache?.image;
    if (cachedImage != null) {
      return cachedImage;
    }
    
    try {
      final image = await _loadImage(url);
      _addToMemoryCache(url, image);
      return image;
    } catch (e) {
      return null;
    }
  }
  
  Future<ui.Image> _loadImage(String url) async {
    final completer = Completer<ui.Image>();
    final codec = await ui.instantiateImageCodec(
      await _fetchImageData(url),
    );
    final frame = await codec.getNextFrame();
    completer.complete(frame.image);
    return completer.future;
  }
  
  Future<Uint8List> _fetchImageData(String url) async {
    final response = await http.get(Uri.parse(url));
    return response.bodyBytes;
  }
  
  void _addToMemoryCache(String url, ui.Image image) {
    final imageSize = image.width * image.height * 4; // 4 bytes per pixel
    
    if (_currentCacheSize + imageSize > _maxCacheSize) {
      _evictLeastRecentlyUsed(imageSize);
    }
    
    _memoryCache[url] = image;
    _currentCacheSize += imageSize;
  }
  
  void _evictLeastRecentlyUsed(int requiredSize) {
    final entries = _memoryCache.entries.toList();
    entries.sort((a, b) => a.key.compareTo(b.key));
    
    int freedSize = 0;
    for (final entry in entries) {
      final imageSize = entry.value.width * entry.value.height * 4;
      _memoryCache.remove(entry.key);
      freedSize += imageSize;
      _currentCacheSize -= imageSize;
      
      if (freedSize >= requiredSize) {
        break;
      }
    }
  }
  
  void clearCache() {
    _memoryCache.clear();
    _currentCacheSize = 0;
    _paintingBinding.imageCache?.clear();
    _paintingBinding.imageCache?.clearLiveImages();
  }
}

// Resource management with automatic cleanup
class ResourceManager {
  final Map<String, StreamSubscription> _subscriptions = {};
  final Map<String, Timer> _timers = {};
  
  StreamSubscription<T>? addSubscription<T>(
    String key,
    StreamSubscription<T> subscription,
  ) {
    _subscriptions[key] = subscription as StreamSubscription;
    return subscription;
  }
  
  Timer? addTimer(String key, Duration duration, VoidCallback callback) {
    final timer = Timer(duration, callback);
    _timers[key] = timer;
    return timer;
  }
  
  void removeSubscription(String key) {
    final subscription = _subscriptions.remove(key);
    subscription?.cancel();
  }
  
  void removeTimer(String key) {
    final timer = _timers.remove(key);
    timer?.cancel();
  }
  
  void dispose() {
    for (final subscription in _subscriptions.values) {
      subscription.cancel();
    }
    _subscriptions.clear();
    
    for (final timer in _timers.values) {
      timer.cancel();
    }
    _timers.clear();
  }
}

// Widget with automatic resource cleanup
class AutoCleanupWidget extends StatefulWidget {
  final Widget child;
  final VoidCallback? onInit;
  final VoidCallback? onDispose;
  
  const AutoCleanupWidget({
    super.key,
    required this.child,
    this.onInit,
    this.onDispose,
  });
  
  @override
  State<AutoCleanupWidget> createState() => _AutoCleanupWidgetState();
}

class _AutoCleanupWidgetState extends State<AutoCleanupWidget> {
  final ResourceManager _resourceManager = ResourceManager();
  
  @override
  void initState() {
    super.initState();
    widget.onInit?.call();
  }
  
  @override
  void dispose() {
    _resourceManager.dispose();
    widget.onDispose?.call();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}

Testing Strategy

Unit Testing with Dart Test Framework

// Unit tests for business logic
void main() {
  group('UserRepository', () {
    late UserRepository userRepository;
    late MockDio mockDio;
    
    setUp(() {
      mockDio = MockDio();
      userRepository = HttpUserRepository(mockDio);
    });
    
    test('should return user when getUser is called with valid ID', () async {
      // Arrange
      final userId = '123';
      final userJson = {
        'id': userId,
        'name': 'John Doe',
        'email': 'john@example.com',
        'avatarUrl': 'https://example.com/avatar.jpg',
      };
      
      when(() => mockDio.get('/users/$userId'))
          .thenAnswer((_) async => Response(data: userJson, statusCode: 200));
      
      // Act
      final result = await userRepository.getUser(userId);
      
      // Assert
      expect(result.id, equals(userId));
      expect(result.name, equals('John Doe'));
      expect(result.email, equals('john@example.com'));
      expect(result.avatarUrl, equals('https://example.com/avatar.jpg'));
      
      verify(() => mockDio.get('/users/$userId')).called(1);
    });
    
    test('should throw UserRepositoryException when API call fails', () async {
      // Arrange
      final userId = '123';
      
      when(() => mockDio.get('/users/$userId'))
          .thenThrow(DioException(requestOptions: RequestOptions(path: '/users/$userId')));
      
      // Act & Assert
      expect(
        () => userRepository.getUser(userId),
        throwsA(isA<UserRepositoryException>()),
      );
      
      verify(() => mockDio.get('/users/$userId')).called(1);
    });
    
    test('should return users list when getUsers is called', () async {
      // Arrange
      final usersJson = [
        {
          'id': '1',
          'name': 'John Doe',
          'email': 'john@example.com',
          'avatarUrl': 'https://example.com/avatar1.jpg',
        },
        {
          'id': '2',
          'name': 'Jane Smith',
          'email': 'jane@example.com',
          'avatarUrl': 'https://example.com/avatar2.jpg',
        },
      ];
      
      when(() => mockDio.get('/users', queryParameters: any(named: 'queryParameters')))
          .thenAnswer((_) async => Response(data: usersJson, statusCode: 200));
      
      // Act
      final result = await userRepository.getUsers();
      
      // Assert
      expect(result, hasLength(2));
      expect(result[0].name, equals('John Doe'));
      expect(result[1].name, equals('Jane Smith'));
      
      verify(() => mockDio.get('/users', queryParameters: any(named: 'queryParameters'))).called(1);
    });
  });
  
  group('User model', () {
    test('should create user with valid data', () {
      // Arrange
      const user = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act & Assert
      expect(user.id, equals('123'));
      expect(user.name, equals('John Doe'));
      expect(user.email, equals('john@example.com'));
      expect(user.avatarUrl, equals('https://example.com/avatar.jpg'));
    });
    
    test('should copy user with updated name', () {
      // Arrange
      const originalUser = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act
      final updatedUser = originalUser.copyWith(name: 'Jane Doe');
      
      // Assert
      expect(updatedUser.id, equals(originalUser.id));
      expect(updatedUser.name, equals('Jane Doe'));
      expect(updatedUser.email, equals(originalUser.email));
      expect(updatedUser.avatarUrl, equals(originalUser.avatarUrl));
    });
    
    test('should compare users correctly', () {
      // Arrange
      const user1 = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      const user2 = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      const user3 = User(
        id: '456',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act & Assert
      expect(user1, equals(user2));
      expect(user1, isNot(equals(user3)));
      expect(user1.hashCode, equals(user2.hashCode));
      expect(user1.hashCode, isNot(equals(user3.hashCode)));
    });
  });
}

// Test for extensions
void main() {
  group('StringExtension', () {
    test('should validate email correctly', () {
      expect('test@example.com'.isValidEmail, isTrue);
      expect('test.name@example.com'.isValidEmail, isTrue);
      expect('test+tag@example.com'.isValidEmail, isTrue);
      
      expect('invalid-email'.isValidEmail, isFalse);
      expect('@example.com'.isValidEmail, isFalse);
      expect('test@'.isValidEmail, isFalse);
      expect('test@example'.isValidEmail, isFalse);
    });
    
    test('should capitalize first letter correctly', () {
      expect('hello'.capitalize, equals('Hello'));
      expect('WORLD'.capitalize, equals('WORLD'));
      expect(''.capitalize, equals(''));
    });
    
    test('should truncate string correctly', () {
      expect('short'.truncate(10), equals('short'));
      expect('this is a long string'.truncate(10), equals('this is a...'));
      expect('this is a long string'.truncate(10, suffix: ' [more]'), equals('this is a [more]'));
    });
  });
}

Widget Testing

// Widget tests for UI components
void main() {
  group('UserCard Widget Tests', () {
    testWidgets('should display user information correctly', (WidgetTester tester) async {
      // Arrange
      const user = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act
      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: UserCard(user: user),
          ),
        ),
      );
      
      // Assert
      expect(find.text('John Doe'), findsOneWidget);
      expect(find.text('john@example.com'), findsOneWidget);
      expect(find.byType(CircleAvatar), findsOneWidget);
    });
    
    testWidgets('should call onTap when card is tapped', (WidgetTester tester) async {
      // Arrange
      const user = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      bool wasTapped = false;
      
      // Act
      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: UserCard(
              user: user,
              onTap: () => wasTapped = true,
            ),
          ),
        ),
      );
      
      await tester.tap(find.byType(UserCard));
      await tester.pump();
      
      // Assert
      expect(wasTapped, isTrue);
    });
    
    testWidgets('should show edit and delete buttons when callbacks provided', (WidgetTester tester) async {
      // Arrange
      const user = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act
      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: UserCard(
              user: user,
              onEdit: () {},
              onDelete: () {},
            ),
          ),
        ),
      );
      
      // Assert
      expect(find.byIcon(Icons.edit_outlined), findsOneWidget);
      expect(find.byIcon(Icons.delete_outline), findsOneWidget);
    });
  });
  
  group('UserListScreen Widget Tests', () {
    testWidgets('should show loading indicator initially', (WidgetTester tester) async {
      // Arrange
      await tester.pumpWidget(
        ProviderScope(
          overrides: [
            usersProvider.overrideWith((ref) => AsyncValue.loading()),
          ],
          child: MaterialApp(
            home: UserListScreen(),
          ),
        ),
      );
      
      // Act & Assert
      expect(find.byType(CircularProgressIndicator), findsOneWidget);
    });
    
    testWidgets('should show error message when loading fails', (WidgetTester tester) async {
      // Arrange
      const errorMessage = 'Failed to load users';
      
      await tester.pumpWidget(
        ProviderScope(
          overrides: [
            usersProvider.overrideWith(
              (ref) => AsyncValue.error(Exception(errorMessage), StackTrace.current),
            ),
          ],
          child: MaterialApp(
            home: UserListScreen(),
          ),
        ),
      );
      
      await tester.pump();
      
      // Act & Assert
      expect(find.text(errorMessage), findsOneWidget);
      expect(find.byType(ElevatedButton), findsOneWidget);
    });
    
    testWidgets('should show users when loading succeeds', (WidgetTester tester) async {
      // Arrange
      final users = [
        const User(
          id: '1',
          name: 'John Doe',
          email: 'john@example.com',
          avatarUrl: 'https://example.com/avatar1.jpg',
        ),
        const User(
          id: '2',
          name: 'Jane Smith',
          email: 'jane@example.com',
          avatarUrl: 'https://example.com/avatar2.jpg',
        ),
      ];
      
      await tester.pumpWidget(
        ProviderScope(
          overrides: [
            usersProvider.overrideWith((ref) => AsyncValue.data(users)),
          ],
          child: MaterialApp(
            home: UserListScreen(),
          ),
        ),
      );
      
      await tester.pump();
      
      // Act & Assert
      expect(find.text('John Doe'), findsOneWidget);
      expect(find.text('Jane Smith'), findsOneWidget);
      expect(find.text('john@example.com'), findsOneWidget);
      expect(find.text('jane@example.com'), findsOneWidget);
      expect(find.byType(UserCard), findsNWidgets(2));
    });
  });
}

Integration Testing

// Integration tests with Flutter integration_test package
void main() {
  group('User Management Integration Tests', () {
    IntegrationTestWidgetsFlutterBinding.ensureInitialized();
    
    testWidgets('should complete user creation flow', (WidgetTester tester) async {
      // Arrange
      app.main();
      await tester.pumpAndSettle();
      
      // Navigate to add user screen
      await tester.tap(find.byIcon(Icons.add));
      await tester.pumpAndSettle();
      
      // Fill out user form
      await tester.enterText(find.byKey(const Key('name_field')), 'John Doe');
      await tester.enterText(find.byKey(const Key('email_field')), 'john@example.com');
      
      // Submit form
      await tester.tap(find.byKey(const Key('submit_button')));
      await tester.pumpAndSettle();
      
      // Assert user appears in list
      expect(find.text('John Doe'), findsOneWidget);
      expect(find.text('john@example.com'), findsOneWidget);
    });
    
    testWidgets('should navigate to user details and back', (WidgetTester tester) async {
      // Arrange
      app.main();
      await tester.pumpAndSettle();
      
      // Wait for users to load
      await tester.pumpAndSettle(const Duration(seconds: 3));
      
      // Tap on first user
      await tester.tap(find.byType(UserCard).first);
      await tester.pumpAndSettle();
      
      // Assert we're on user details screen
      expect(find.byType(UserDetailScreen), findsOneWidget);
      
      // Navigate back
      await tester.tap(find.byIcon(Icons.arrow_back));
      await tester.pumpAndSettle();
      
      // Assert we're back on user list
      expect(find.byType(UserListScreen), findsOneWidget);
    });
    
    testWidgets('should handle network errors gracefully', (WidgetTester tester) async {
      // Arrange - mock network failure
      setUpAll(() {
        HttpOverrides.global = MockHttpOverrides();
      });
      
      app.main();
      await tester.pumpAndSettle();
      
      // Wait for error to appear
      await tester.pumpAndSettle(const Duration(seconds: 3));
      
      // Assert error message is shown
      expect(find.byType(ErrorWidget), findsOneWidget);
      
      // Tap retry button
      await tester.tap(find.byType(ElevatedButton));
      await tester.pumpAndSettle();
      
      // Verify retry attempts
      expect(find.text('Retrying...'), findsOneWidget);
    });
  });
}

// Mock HTTP overrides for testing
class MockHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return MockHttpClient();
  }
}

class MockHttpClient extends FakeHttpClient {
  @override
  Future<HttpClientRequest> getUrl(Uri url) async {
    if (url.path.contains('/users')) {
      throw HttpClientException('Connection failed');
    }
    return super.getUrl(url);
  }
}

// Golden tests for visual regression
void main() {
  group('UserCard Golden Tests', () {
    testWidgets('should match golden snapshot', (WidgetTester tester) async {
      // Arrange
      const user = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act
      await tester.pumpWidget(
        MaterialApp(
          theme: ThemeData.light(),
          home: Scaffold(
            body: UserCard(user: user),
          ),
        ),
      );
      
      await tester.pumpAndSettle();
      
      // Assert
      await expectLater(
        find.byType(UserCard),
        matchesGoldenFile('goldens/user_card.png'),
      );
    });
    
    testWidgets('should match dark mode golden snapshot', (WidgetTester tester) async {
      // Arrange
      const user = User(
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatarUrl: 'https://example.com/avatar.jpg',
      );
      
      // Act
      await tester.pumpWidget(
        MaterialApp(
          theme: ThemeData.dark(),
          home: Scaffold(
            body: UserCard(user: user),
          ),
        ),
      );
      
      await tester.pumpAndSettle();
      
      // Assert
      await expectLater(
        find.byType(UserCard),
        matchesGoldenFile('goldens/user_card_dark.png'),
      );
    });
  });
}

Security Best Practices

Input Validation

// Input validation utilities
class InputValidator {
  static String? validateEmail(String? value) {
    if (value == null || value.isEmpty) {
      return 'Email is required';
    }
    
    if (!value.isValidEmail) {
      return 'Please enter a valid email address';
    }
    
    return null;
  }
  
  static String? validatePassword(String? value) {
    if (value == null || value.isEmpty) {
      return 'Password is required';
    }
    
    if (value.length < 8) {
      return 'Password must be at least 8 characters long';
    }
    
    if (!value.contains(RegExp(r'[A-Z]'))) {
      return 'Password must contain at least one uppercase letter';
    }
    
    if (!value.contains(RegExp(r'[a-z]'))) {
      return 'Password must contain at least one lowercase letter';
    }
    
    if (!value.contains(RegExp(r'[0-9]'))) {
      return 'Password must contain at least one digit';
    }
    
    return null;
  }
  
  static String? validateName(String? value) {
    if (value == null || value.isEmpty) {
      return 'Name is required';
    }
    
    if (value.length < 2) {
      return 'Name must be at least 2 characters long';
    }
    
    if (value.length > 50) {
      return 'Name must be less than 50 characters';
    }
    
    if (!value.contains(RegExp(r'^[a-zA-Z\s]+$'))) {
      return 'Name can only contain letters and spaces';
    }
    
    return null;
  }
}

// Secure storage for sensitive data
class SecureStorage {
  static const _storage = FlutterSecureStorage();
  
  static Future<void> storeToken(String token) async {
    await _storage.write(key: 'auth_token', value: token);
  }
  
  static Future<String?> getToken() async {
    return await _storage.read(key: 'auth_token');
  }
  
  static Future<void> deleteToken() async {
    await _storage.delete(key: 'auth_token');
  }
  
  static Future<void> storeUserCredentials(String username, String password) async {
    await _storage.write(key: 'username', value: username);
    await _storage.write(key: 'password', value: password);
  }
  
  static Future<Map<String, String?>> getUserCredentials() async {
    final username = await _storage.read(key: 'username');
    final password = await _storage.read(key: 'password');
    return {'username': username, 'password': password};
  }
  
  static Future<void> clearAll() async {
    await _storage.deleteAll();
  }
}

// Secure HTTP client with authentication
class SecureHttpClient {
  late final Dio _dio;
  
  SecureHttpClient() {
    _dio = Dio(BaseOptions(
      baseUrl: AppConfig.apiBaseUrl,
      connectTimeout: const Duration(seconds: 30),
      receiveTimeout: const Duration(seconds: 30),
    ));
    
    _setupInterceptors();
  }
  
  void _setupInterceptors() {
    // Authentication interceptor
    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (options, handler) async {
          final token = await SecureStorage.getToken();
          if (token != null) {
            options.headers['Authorization'] = 'Bearer $token';
          }
          handler.next(options);
        },
        onError: (error, handler) async {
          if (error.response?.statusCode == 401) {
            // Token expired, clear storage and navigate to login
            await SecureStorage.deleteToken();
            // Navigate to login screen
            _navigateToLogin();
          }
          handler.next(error);
        },
      ),
    );
    
    // Logging interceptor (only in debug mode)
    if (AppConfig.isDebug) {
      _dio.interceptors.add(LogInterceptor(
        requestBody: true,
        responseBody: true,
      ));
    }
    
    // Retry interceptor
    _dio.interceptors.add(RetryInterceptor(
      dio: _dio,
      options: const RetryOptions(
        retries: 3,
        retryInterval: Duration(seconds: 1),
      ),
    ));
  }
  
  void _navigateToLogin() {
    // Use navigator key to navigate to login screen
    navigatorKey.currentState?.pushNamedAndRemoveUntil(
      '/login',
      (route) => false,
    );
  }
  
  Future<Response<T>> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    return _dio.get<T>(path, queryParameters: queryParameters, options: options);
  }
  
  Future<Response<T>> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    return _dio.post<T>(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
    );
  }
  
  Future<Response<T>> put<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    return _dio.put<T>(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
    );
  }
  
  Future<Response<T>> delete<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    return _dio.delete<T>(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
    );
  }
}

// Local authentication with biometrics
class BiometricAuth {
  static final LocalAuthentication _auth = LocalAuthentication();
  
  static Future<bool> isAvailable() async {
    final isAvailable = await _auth.canCheckBiometrics;
    final isDeviceSupported = await _auth.isDeviceSupported();
    return isAvailable && isDeviceSupported;
  }
  
  static Future<bool> authenticate() async {
    try {
      final didAuthenticate = await _auth.authenticate(
        localizedReason: 'Please authenticate to access this feature',
        options: const AuthenticationOptions(
          biometricOnly: false,
          useErrorDialogs: true,
          stickyAuth: true,
        ),
      );
      return didAuthenticate;
    } catch (e) {
      return false;
    }
  }
  
  static Future<List<BiometricType>> getAvailableBiometrics() async {
    try {
      return await _auth.getAvailableBiometrics();
    } catch (e) {
      return [];
    }
  }
}

Data Protection

// Encrypted data model
class SecureUser {
  final String id;
  final String encryptedName;
  final String encryptedEmail;
  
  SecureUser({
    required this.id,
    required this.encryptedName,
    required this.encryptedEmail,
  });
  
  factory SecureUser.fromUser(User user, EncryptionService encryptionService) {
    return SecureUser(
      id: user.id,
      encryptedName: encryptionService.encrypt(user.name),
      encryptedEmail: encryptionService.encrypt(user.email),
    );
  }
  
  User toUser(EncryptionService encryptionService) {
    return User(
      id: id,
      name: encryptionService.decrypt(encryptedName),
      email: encryptionService.decrypt(encryptedEmail),
      avatarUrl: '', // Not encrypted in this example
    );
  }
}

// Encryption service
class EncryptionService {
  static final EncryptionService _instance = EncryptionService._internal();
  factory EncryptionService() => _instance;
  EncryptionService._internal();
  
  late final Encrypter _encrypter;
  late final IV _iv;
  
  Future<void> initialize() async {
    final key = await _getOrCreateKey();
    _encrypter = Encrypter(AES(key));
    _iv = IV.fromLength(16);
  }
  
  Future<Key> _getOrCreateKey() async {
    const storage = FlutterSecureStorage();
    
    String? keyString = await storage.read(key: 'encryption_key');
    if (keyString != null) {
      return Key.fromBase64(keyString);
    }
    
    final key = Key.fromSecureRandom(32);
    await storage.write(key: 'encryption_key', value: key.base64);
    return key;
  }
  
  String encrypt(String plaintext) {
    final encrypted = _encrypter.encrypt(plaintext, iv: _iv);
    return encrypted.base64;
  }
  
  String decrypt(String ciphertext) {
    final encrypted = Encrypted.fromBase64(ciphertext);
    return _encrypter.decrypt(encrypted, iv: _iv);
  }
  
  Future<void> clearKey() async {
    const storage = FlutterSecureStorage();
    await storage.delete(key: 'encryption_key');
  }
}

// API security with rate limiting
class RateLimiter {
  final Map<String, List<DateTime>> _requests = {};
  final int maxRequests;
  final Duration timeWindow;
  
  RateLimiter({
    this.maxRequests = 100,
    this.timeWindow = const Duration(minutes: 1),
  });
  
  bool canMakeRequest(String identifier) {
    final now = DateTime.now();
    final requests = _requests[identifier] ?? [];
    
    // Remove old requests outside the time window
    requests.removeWhere((request) => now.difference(request) > timeWindow);
    
    if (requests.length >= maxRequests) {
      return false;
    }
    
    requests.add(now);
    _requests[identifier] = requests;
    return true;
  }
  
  Duration? getTimeUntilNextRequest(String identifier) {
    final now = DateTime.now();
    final requests = _requests[identifier] ?? [];
    
    if (requests.length < maxRequests) {
      return null;
    }
    
    final oldestRequest = requests.first;
    final timeSinceOldest = now.difference(oldestRequest);
    
    if (timeSinceOldest > timeWindow) {
      return null;
    }
    
    return timeWindow - timeSinceOldest;
  }
}

// Content security for web applications
class ContentSecurityPolicy {
  static String get policy {
    return [
      "default-src 'self'",
      "script-src 'self' 'unsafe-inline' 'unsafe-eval'",
      "style-src 'self' 'unsafe-inline'",
      "img-src 'self' data: https:",
      "font-src 'self' data:",
      "connect-src 'self' https://api.example.com",
      "media-src 'self'",
      "object-src 'none'",
      "base-uri 'self'",
      "form-action 'self'",
      "frame-ancestors 'none'",
      "upgrade-insecure-requests",
    ].join('; ');
  }
}

Integration Patterns

Server-Side Dart with Shelf

// Server-side Dart application
import 'dart:io';
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf_cors_headers/shelf_cors_headers.dart';
import 'package:shelf_static/shelf_static.dart';

// User service for server-side
class UserService {
  final Map<String, User> _users = {};
  
  UserService() {
    _seedUsers();
  }
  
  void _seedUsers() {
    _users['1'] = const User(
      id: '1',
      name: 'John Doe',
      email: 'john@example.com',
      avatarUrl: 'https://example.com/avatar1.jpg',
    );
    _users['2'] = const User(
      id: '2',
      name: 'Jane Smith',
      email: 'jane@example.com',
      avatarUrl: 'https://example.com/avatar2.jpg',
    );
  }
  
  List<User> getUsers({int page = 1, int limit = 20}) {
    final skip = (page - 1) * limit;
    return _users.values.skip(skip).take(limit).toList();
  }
  
  User? getUser(String id) {
    return _users[id];
  }
  
  User createUser(CreateUserRequest request) {
    final id = (_users.keys.length + 1).toString();
    final user = User(
      id: id,
      name: request.name,
      email: request.email,
      avatarUrl: request.avatarUrl ?? '',
    );
    _users[id] = user;
    return user;
  }
  
  User? updateUser(String id, UpdateUserRequest request) {
    final user = _users[id];
    if (user == null) return null;
    
    final updatedUser = user.copyWith(
      name: request.name ?? user.name,
      email: request.email ?? user.email,
      avatarUrl: request.avatarUrl ?? user.avatarUrl,
    );
    
    _users[id] = updatedUser;
    return updatedUser;
  }
  
  bool deleteUser(String id) {
    return _users.remove(id) != null;
  }
}

// Request handlers
Handler getUsersHandler(UserService userService) {
  return (Request request) async {
    final page = int.tryParse(request.url.queryParameters['page'] ?? '1') ?? 1;
    final limit = int.tryParse(request.url.queryParameters['limit'] ?? '20') ?? 20;
    
    final users = userService.getUsers(page: page, limit: limit);
    
    final response = {
      'users': users.map((u) => u.toJson()).toList(),
      'page': page,
      'limit': limit,
      'total': users.length,
    };
    
    return Response.ok(
      json.encode(response),
      headers: {'content-type': 'application/json'},
    );
  };
}

Handler getUserHandler(UserService userService) {
  return (Request request) {
    final id = request.params['id']!;
    final user = userService.getUser(id);
    
    if (user == null) {
      return Response.notFound(json.encode({'error': 'User not found'}));
    }
    
    return Response.ok(
      json.encode(user.toJson()),
      headers: {'content-type': 'application/json'},
    );
  };
}

Handler createUserHandler(UserService userService) {
  return (Request request) async {
    try {
      final body = await request.readAsString();
      final jsonData = json.decode(body) as Map<String, dynamic>;
      final createRequest = CreateUserRequest.fromJson(jsonData);
      
      final user = userService.createUser(createRequest);
      
      return Response(201,
        body: json.encode(user.toJson()),
        headers: {'content-type': 'application/json'},
      );
    } catch (e) {
      return Response(400,
        body: json.encode({'error': 'Invalid request data: $e'}),
        headers: {'content-type': 'application/json'},
      );
    }
  };
}

// Router setup
Router setupRouter(UserService userService) {
  final router = Router();
  
  // CORS headers
  router.all('/<ignored|.*>', (Request request) {
    return Response.ok(null);
  });
  
  // API routes
  router.get('/users', getUsersHandler(userService));
  router.get('/users/<id>', getUserHandler(userService));
  router.post('/users', createUserHandler(userService));
  router.put('/users/<id>', updateUserHandler(userService));
  router.delete('/users/<id>', deleteUserHandler(userService));
  
  // Static file serving
  router.get('/<.*>', (Request request) {
    final path = request.url.path;
    if (path.startsWith('/api/')) {
      return Response.notFound('Not found');
    }
    
    return staticHandler('web')(request);
  });
  
  return router;
}

// Static file handler
Handler staticHandler(String directory) {
  return createStaticHandler(directory, defaultDocument: 'index.html');
}

// Main server function
Future<void> main() async {
  final userService = UserService();
  final router = setupRouter(userService);
  
  // Add CORS middleware
  final handler = const Pipeline()
      .addMiddleware(corsHeaders())
      .addMiddleware(logRequests())
      .addHandler(router);
  
  // Start server
  final server = await shelf_io.serve(
    handler,
    InternetAddress.anyIPv4,
    8080,
  );
  
  print('Server listening on port ${server.port}');
}

// Logging middleware
Middleware logRequests() {
  return (Handler innerHandler) {
    return (Request request) async {
      final startTime = DateTime.now();
      
      try {
        final response = await innerHandler(request);
        final duration = DateTime.now().difference(startTime);
        
        print(
          '${request.method} ${request.requestedUri} -> '
          '${response.statusCode} (${duration.inMilliseconds}ms)',
        );
        
        return response;
      } catch (error, stackTrace) {
        final duration = DateTime.now().difference(startTime);
        
        print(
          '${request.method} ${request.requestedUri} -> ERROR ($duration): $error',
        );
        
        rethrow;
      }
    };
  };
}

WebSockets for Real-time Communication

// WebSocket server implementation
import 'dart:io';
import 'dart:convert';

class WebSocketServer {
  late HttpServer _server;
  final Set<WebSocket> _connections = {};
  
  Future<void> start(int port) async {
    _server = await HttpServer.bind(InternetAddress.anyIPv4, port);
    print('WebSocket server listening on port $port');
    
    await for (HttpRequest request in _server) {
      if (request.uri.path == '/ws') {
        await _handleWebSocket(request);
      } else {
        request.response.statusCode = HttpStatus.notFound;
        await request.response.close();
      }
    }
  }
  
  Future<void> _handleWebSocket(HttpRequest request) async {
    try {
      final webSocket = await WebSocketTransformer.upgrade(request);
      _connections.add(webSocket);
      
      print('New WebSocket connection: ${webSocket.hashCode}');
      
      // Send welcome message
      _sendMessage(webSocket, {
        'type': 'welcome',
        'message': 'Connected to chat server',
        'timestamp': DateTime.now().toIso8601String(),
      });
      
      // Listen for messages
      webSocket.listen(
        (data) => _handleMessage(webSocket, data),
        onDone: () => _handleDisconnection(webSocket),
        onError: (error) => print('WebSocket error: $error'),
      );
    } catch (e) {
      print('Failed to upgrade to WebSocket: $e');
      request.response.statusCode = HttpStatus.internalServerError;
      await request.response.close();
    }
  }
  
  void _handleMessage(WebSocket webSocket, dynamic data) {
    try {
      final message = json.decode(data as String) as Map<String, dynamic>;
      final messageWithTimestamp = {
        ...message,
        'timestamp': DateTime.now().toIso8601String(),
      };
      
      print('Received message: $messageWithTimestamp');
      
      // Broadcast message to all connected clients
      _broadcastMessage(messageWithTimestamp);
    } catch (e) {
      print('Error handling message: $e');
      _sendMessage(webSocket, {
        'type': 'error',
        'message': 'Invalid message format',
        'timestamp': DateTime.now().toIso8601String(),
      });
    }
  }
  
  void _handleDisconnection(WebSocket webSocket) {
    _connections.remove(webSocket);
    print('WebSocket disconnected: ${webSocket.hashCode}');
    
    _broadcastMessage({
      'type': 'user_disconnected',
      'message': 'A user left the chat',
      'timestamp': DateTime.now().toIso8601String(),
    });
  }
  
  void _sendMessage(WebSocket webSocket, Map<String, dynamic> message) {
    try {
      webSocket.add(json.encode(message));
    } catch (e) {
      print('Error sending message: $e');
    }
  }
  
  void _broadcastMessage(Map<String, dynamic> message) {
    final messageString = json.encode(message);
    
    for (final connection in _connections) {
      try {
        connection.add(messageString);
      } catch (e) {
        print('Error broadcasting message: $e');
      }
    }
  }
  
  Future<void> stop() async {
    await _server.close();
    for (final connection in _connections) {
      await connection.close();
    }
  }
}

// Chat message models
class ChatMessage {
  final String id;
  final String username;
  final String content;
  final DateTime timestamp;
  
  ChatMessage({
    required this.id,
    required this.username,
    required this.content,
    required this.timestamp,
  });
  
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'username': username,
      'content': content,
      'timestamp': timestamp.toIso8601String(),
    };
  }
  
  factory ChatMessage.fromJson(Map<String, dynamic> json) {
    return ChatMessage(
      id: json['id'] as String,
      username: json['username'] as String,
      content: json['content'] as String,
      timestamp: DateTime.parse(json['timestamp'] as String),
    );
  }
}

// WebSocket client for Flutter
class WebSocketService {
  WebSocketChannel? _channel;
  final StreamController<ChatMessage> _messageController = StreamController<ChatMessage>.broadcast();
  final StreamController<String> _statusController = StreamController<String>.broadcast();
  
  Stream<ChatMessage> get messageStream => _messageController.stream;
  Stream<String> get statusStream => _statusController.stream;
  
  Future<void> connect(String url) async {
    try {
      _channel = WebSocketChannel.connect(Uri.parse(url));
      _statusController.add('Connected');
      
      _channel!.stream.listen(
        (data) {
          try {
            final message = ChatMessage.fromJson(json.decode(data));
            _messageController.add(message);
          } catch (e) {
            print('Error parsing message: $e');
          }
        },
        onDone: () {
          _statusController.add('Disconnected');
        },
        onError: (error) {
          _statusController.add('Error: $error');
        },
      );
    } catch (e) {
      _statusController.add('Connection failed: $e');
    }
  }
  
  void sendMessage(ChatMessage message) {
    if (_channel != null) {
      _channel!.sink.add(json.encode(message.toJson()));
    }
  }
  
  void disconnect() {
    _channel?.sink.close();
    _channel = null;
  }
  
  void dispose() {
    disconnect();
    _messageController.close();
    _statusController.close();
  }
}

Modern Development Workflow

Project Configuration

# pubspec.yaml
name: my_flutter_app
description: A comprehensive Flutter application
publish_to: 'none'

version: 1.0.0+1

environment:
  sdk: '>=3.5.0 <4.0.0'
  flutter: ">=3.24.0"

dependencies:
  flutter:
    sdk: flutter
  
  # State management
  flutter_riverpod: ^2.5.0
  provider: ^6.1.2
  
  # Navigation
  go_router: ^13.2.0
  
  # HTTP client
  dio: ^5.4.3+1
  retrofit: ^4.0.3
  json_annotation: ^4.8.1
  
  # Local storage
  shared_preferences: ^2.2.3
  flutter_secure_storage: ^9.0.0
  
  # Database
  sqflite: ^2.3.3
  drift: ^2.17.0
  
  # Authentication
  local_auth: ^2.2.0
  
  # UI components
  material_color_utilities: ^0.8.0
  flutter_svg: ^2.0.10+1
  
  # Utilities
  uuid: ^4.4.0
  intl: ^0.19.0
  equatable: ^2.0.5
  json_serializable: ^6.7.1
  
  # Testing
  flutter_test:
    sdk: flutter
  mockito: ^5.4.4
  build_runner: ^2.4.9
  retrofit_generator: ^8.0.6
  json_serializable: ^6.7.1
  drift_dev: ^2.17.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  
  # Code generation
  build_runner: ^2.4.9
  retrofit_generator: ^8.0.6
  json_serializable: ^6.7.1
  drift_dev: ^2.17.0
  
  # Linting and formatting
  flutter_lints: ^4.0.0
  very_good_analysis: ^5.1.0
  
  # Testing
  integration_test:
    sdk: flutter
  golden_toolkit: ^0.15.0
  network_image_mock: ^2.1.1

flutter:
  uses-material-design: true
  
  assets:
    - assets/images/
    - assets/icons/
    - assets/config/
  
  fonts:
    - family: Roboto
      fonts:
        - asset: fonts/Roboto-Regular.ttf
        - asset: fonts/Roboto-Bold.ttf
          weight: 700

Analysis Configuration

# analysis_options.yaml
include: package:very_good_analysis/analysis_options.yaml

analyzer:
  exclude:
    - "**/*.g.dart"
    - "**/*.freezed.dart"
  
  language:
    strict-casts: true
    strict-inference: true
    strict-raw-types: true
  
  errors:
    invalid_annotation_target: ignore
    missing_required_param: error
    missing_return: error
    todo: ignore

linter:
  rules:
    # Additional rules beyond very_good_analysis
    prefer_single_quotes: true
    sort_constructors_first: true
    sort_unnamed_constructors_first: true
    always_declare_return_types: true
    avoid_print: true
    avoid_unnecessary_containers: true
    sized_box_for_whitespace: true
    use_key_in_widget_constructors: true
    prefer_const_constructors: true
    prefer_const_declarations: true
    prefer_const_literals_to_create_immutables: true
    avoid_web_libraries_in_flutter: true
    prefer_const_constructors_in_immutables: true
    prefer_final_fields: true
    use_full_hex_values_for_flutter_colors: true

CI/CD Configuration

# .github/workflows/flutter.yml
name: Flutter CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        channel: stable
        flutter-version: '3.24.x'
    
    - name: Install dependencies
      run: flutter pub get
    
    - name: Generate code
      run: flutter packages pub run build_runner build --delete-conflicting-outputs
    
    - name: Analyze code
      run: flutter analyze
    
    - name: Run tests
      run: flutter test --coverage --test-randomize-ordering-seed random
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: coverage/lcov.info
    
    - name: Run widget tests
      run: flutter test integration_test/
  
  build_android:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        channel: stable
        flutter-version: '3.24.x'
    
    - name: Install dependencies
      run: flutter pub get
    
    - name: Generate code
      run: flutter packages pub run build_runner build --delete-conflicting-outputs
    
    - name: Build APK
      run: flutter build apk --release
    
    - name: Build App Bundle
      run: flutter build appbundle --release
    
    - name: Upload APK
      uses: actions/upload-artifact@v3
      with:
        name: android-apk
        path: build/app/outputs/flutter-apk/app-release.apk
    
    - name: Upload App Bundle
      uses: actions/upload-artifact@v3
      with:
        name: android-aab
        path: build/app/outputs/bundle/release/app-release.aab
  
  build_ios:
    needs: test
    runs-on: macos-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        channel: stable
        flutter-version: '3.24.x'
    
    - name: Install dependencies
      run: flutter pub get
    
    - name: Generate code
      run: flutter packages pub run build_runner build --delete-conflicting-outputs
    
    - name: Build iOS
      run: flutter build ios --release --no-codesign
    
    - name: Upload iOS build
      uses: actions/upload-artifact@v3
      with:
        name: ios-build
        path: build/ios/iphoneos/Runner.app
  
  build_web:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        channel: stable
        flutter-version: '3.24.x'
    
    - name: Install dependencies
      run: flutter pub get
    
    - name: Generate code
      run: flutter packages pub run build_runner build --delete-conflicting-outputs
    
    - name: Build web
      run: flutter build web --release
    
    - name: Deploy to GitHub Pages
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: build/web

Created by: MoAI Language Skill Factory
Last Updated: 2025-11-06
Version: 2.0.0
Dart Target: 3.5.x with Flutter 3.24.x and modern async patterns

This skill provides comprehensive Dart development guidance with 2025 best practices, covering everything from Flutter mobile applications to server-side development and real-time communication with WebSockets.