Flutter
What This Does
Provides expert guidance for building Flutter applications — from widget composition and state management to platform channels and performance optimization. Covers mobile (iOS/Android), web, and desktop targets with Dart best practices.
Instructions
Assess the project. Determine:
- New project or existing?
- Flutter SDK version and Dart version
- Target platforms: mobile, web, desktop, or all?
- State management: Riverpod, Bloc, Provider, or GetX?
- Navigation: GoRouter, auto_route, or Navigator 2.0?
Project setup. For new projects:
flutter create --org com.example --platforms ios,android my_app # With specific template flutter create --template app --org com.example my_appFollow Flutter architecture patterns:
- State management: Riverpod 2.0 (recommended) or Bloc for enterprise
- Navigation: GoRouter for declarative, type-safe routing
- Architecture: Feature-first folder structure with clean architecture layers
- DI: Riverpod providers or get_it for dependency injection
- Networking: dio + retrofit for API calls, freezed for data classes
- Storage: shared_preferences for simple, Hive/Isar for complex local data
Widget architecture:
// Prefer composition over inheritance // Small, focused widgets that compose into screens // Stateless for pure UI class UserAvatar extends StatelessWidget { final String imageUrl; final double size; const UserAvatar({required this.imageUrl, this.size = 40}); @override Widget build(BuildContext context) { return ClipOval( child: CachedNetworkImage( imageUrl: imageUrl, width: size, height: size, fit: BoxFit.cover, ), ); } } // ConsumerWidget for Riverpod state class UserProfile extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final user = ref.watch(userProvider); return user.when( data: (data) => UserProfileView(user: data), loading: () => const CircularProgressIndicator(), error: (err, stack) => ErrorWidget(error: err), ); } }State management with Riverpod:
// Async data provider @riverpod Future<List<Todo>> todos(Ref ref) async { final repository = ref.watch(todoRepositoryProvider); return repository.fetchAll(); } // Notifier for mutable state @riverpod class TodoList extends _$TodoList { @override Future<List<Todo>> build() => ref.watch(todoRepositoryProvider).fetchAll(); Future<void> addTodo(Todo todo) async { state = const AsyncLoading(); state = await AsyncValue.guard(() async { await ref.read(todoRepositoryProvider).create(todo); return ref.read(todoRepositoryProvider).fetchAll(); }); } }Performance optimization:
- Use
constconstructors wherever possible - Avoid rebuilding large widget trees — use
Consumerwidgets to scope rebuilds - Use
ListView.builderfor long lists (neverColumnwithSingleChildScrollView) - Profile with Flutter DevTools — look for jank in the timeline
- Use
RepaintBoundaryto isolate expensive paint operations - Enable impeller rendering engine for smoother animations
- Use
Platform channels. For native functionality:
// Method channel for platform-specific code static const platform = MethodChannel('com.example/native'); Future<String> getNativeValue() async { try { return await platform.invokeMethod('getValue'); } on PlatformException catch (e) { return 'Failed: ${e.message}'; } }
Output Format
When generating Flutter code:
- Dart with null safety
- Prefer
constconstructors - Use named parameters for widgets
- Follow effective Dart style guide
- Include necessary imports
- Use code generation annotations where applicable (freezed, riverpod_generator)
Tips
- Use
flutter analyzeanddart fix --applybefore committing - Riverpod with code generation (@riverpod annotation) is cleaner than manual providers
- freezed package eliminates boilerplate for immutable data classes
- Use
flutter_screenutilorMediaQueryfor responsive layouts, not hardcoded sizes - Test on both iOS and Android regularly — rendering differences exist
- Use
golden_toolkitfor widget snapshot testing - Keep widget build methods under 80 lines — extract sub-widgets aggressively