This adds the initial version of AI rules for Flutter development to the flutter/flutter repo. These are currently published to https://docs.flutter.dev/ai/ai-rules cc: @sethladd @rodydavis --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
12 KiB
AI rules for Flutter
You are an expert in Flutter and Dart development. Your goal is to build beautiful, performant, and maintainable applications following modern best practices.
Project Structure
- Assumes a standard Flutter project structure with
lib/main.dartas the primary application entry point.
Package Management
- To manage packages, use the
pubtool, if available. - If a new feature requires an external package, use the
pub_dev_searchtool, if it is available. Otherwise, identify the most suitable and stable package from pub.dev. - To add a regular dependency, use the
pubtool, if it is available. Otherwise, runflutter pub add <package_name>. - To add a development dependency, use the
pubtool, if it is available, withdev:<package name>. Otherwise, runflutter pub add dev:<package_name>. - To add a dependency override, use the
pubtool, if it is available, withoverride:<package name>:1.0.0. Otherwise, runflutter pub add override:<package_name>:1.0.0. - To remove a dependency, use the
pubtool, if it is available. Otherwise, rundart pub remove <package_name>.
Code Quality
- Adhere to maintainable code structure and separation of concerns (e.g., UI logic separate from business logic).
- Adhere to meaningful and consistent naming conventions.
Dart Best Practices
- Follow the official Effective Dart guidelines (https://dart.dev/effective-dart)
- Define related classes within the same library file. For large libraries, export smaller, private libraries from a single top-level library.
- Group related libraries in the same folder.
- Add documentation comments to all public APIs, including classes, constructors, methods, and top-level functions.
- Write clear comments for complex or non-obvious code. Avoid over-commenting.
- Don't add trailing comments.
- Ensure proper use of
async/awaitfor asynchronous operations with robust error handling. - Use pattern matching features where they simplify the code.
Flutter Best Practices
- Widgets (especially
StatelessWidget) are immutable; when the UI needs to change, Flutter rebuilds the widget tree. - Prefer composing smaller widgets over extending existing ones.
- Use small, private
Widgetclasses instead of private helper methods that return aWidget. - Break down large
build()methods into smaller, reusable private Widget classes. - Use
ListView.builderto create lazy-loaded lists for performance. - Use
constconstructors for widgets and inbuild()methods whenever possible to optimize performance. - Avoid performing expensive operations, like network calls or complex
computations, directly within
build()methods.
Application Architecture
- Aim for separation of concerns similar to MVC/MVVM, with defined Model, View, and ViewModel/Controller roles.
State Management
-
Prefer Flutter's built-in state management solutions. Do not use a third-party package unless explicitly requested.
-
Use
StreamsandStreamBuilderfor handling a sequence of asynchronous events. -
Use
FuturesandFutureBuilderfor handling a single asynchronous operation that will complete in the future. -
Use
ValueNotifierwithValueListenableBuilderfor simple, local state that involves a single value.// Define a ValueNotifier to hold the state. final ValueNotifier<int> _counter = ValueNotifier<int>(0); // Use ValueListenableBuilder to listen and rebuild. ValueListenableBuilder<int>( valueListenable: _counter, builder: (context, value, child) { return Text('Count: $value'); }, ); -
For state that is more complex or shared across multiple widgets, use
ChangeNotifier. -
Use
ListenableBuilderto listen to changes from aChangeNotifieror otherListenable. -
When a more robust solution is needed, structure the app using the Model-View-ViewModel (MVVM) pattern.
-
Use manual dependency injection via constructors to make a class's dependencies explicit in its API.
-
If a dependency injection solution beyond manual constructor injection is explicitly requested,
providercan be used to make services, repositories, or complex state objects available to the UI layer without tight coupling (note: this document generally defaults against third-party packages for state management unless explicitly requested).
Data Flow
- Define data structures (classes) to represent the data used in the application.
- Abstract data sources (e.g., API calls, database operations) using Repositories/Services to promote testability.
Routing
-
Use
go_routerfor declarative navigation, deep linking, and web support.// 1. Add the dependency // flutter pub add go_router // 2. Configure the router final GoRouter _router = GoRouter( routes: <RouteBase>[ GoRoute( path: '/', builder: (context, state) => const HomeScreen(), routes: <RouteBase>[ GoRoute( path: 'details/:id', // Route with a path parameter builder: (context, state) { final String id = state.pathParameters['id']!; return DetailScreen(id: id); }, ), ], ), ], ); // 3. Use it in your MaterialApp MaterialApp.router( routerConfig: _router, ); -
Use the built-in
Navigatorfor short-lived screens that do not need to be deep-linkable, such as dialogs or temporary views.// Push a new screen onto the stack Navigator.push( context, MaterialPageRoute(builder: (context) => const DetailsScreen()), ); // Pop the current screen to go back Navigator.pop(context);
Data Handling & Serialization
-
Use
json_serializableandjson_annotationfor parsing and encoding JSON data. -
When encoding data, use
fieldRename: FieldRename.snaketo convert Dart's camelCase fields to snake_case JSON keys.// In your model file import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable(fieldRename: FieldRename.snake) class User { final String firstName; final String lastName; User({required this.firstName, required this.lastName}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); }
Logging
-
Use the
logfunction fromdart:developerfor structured logging that integrates with Dart DevTools.import 'dart:developer' as developer; // For simple messages developer.log('User logged in successfully.'); // For structured error logging try { // ... code that might fail } catch (e, s) { developer.log( 'Failed to fetch data', name: 'myapp.network', level: 1000, // SEVERE error: e, stackTrace: s, ); }
Error Handling
- Implement mechanisms to gracefully handle errors across the application (e.g., using try-catch blocks, Either types for functional error handling, or global error handlers).
Code Generation
-
Use
build_runnerfor all code generation tasks, such as forjson_serializable. -
After modifying files that require code generation, run the build command:
dart run build_runner build --delete-conflicting-outputs
Testing
- To run tests, use the
run_teststool if it is available, otherwise useflutter test. - Use
package:testfor unit tests. - Use
package:flutter_testfor widget tests. - Use
package:integration_testfor integration tests. - Prefer using
package:checksfor more expressive and readable assertions over the defaultmatchers.
Visual Design & Theming
- Build beautiful and intuitive user interfaces that follow modern design guidelines.
- Ensure the app is mobile responsive and adapts to different screen sizes, working perfectly on mobile and web.
- If there are multiple pages for the user to interact with, provide an intuitive and easy navigation bar or controls.
- Stress and emphasize font sizes to ease understanding, e.g., hero text, section headlines, list headlines, keywords in paragraphs.
- Apply subtle noise texture to the main background to add a premium, tactile feel.
- Multi-layered drop shadows create a strong sense of depth; cards have a soft, deep shadow to look "lifted."
- Incorporate icons to enhance the user’s understanding and the logical navigation of the app.
- Buttons, checkboxes, sliders, lists, charts, graphs, and other interactive elements have a shadow with elegant use of color to create a "glow" effect.
Theming
-
Define a centralized
ThemeDataobject to ensure a consistent application-wide style. -
Use Material 3 by setting
useMaterial3: truein yourThemeData. -
Implement support for both light and dark themes, ideal for a user-facing theme toggle (
ThemeMode.light,ThemeMode.dark,ThemeMode.system). -
Generate harmonious color palettes from a single color using
ColorScheme.fromSeed.final ThemeData lightTheme = ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepPurple, brightness: Brightness.light, ), // ... other theme properties ); -
Include a wide range of color concentrations and hues in the palette to create a vibrant and energetic look and feel.
-
Use specific theme properties (e.g.,
appBarTheme,elevatedButtonTheme) to customize the appearance of individual Material components. -
For custom fonts, use the
google_fontspackage. Define aTextThemeto apply fonts consistently.// 1. Add the dependency // flutter pub add google_fonts // 2. Define a TextTheme with a custom font final TextTheme appTextTheme = TextTheme( displayLarge: GoogleFonts.oswald(fontSize: 57, fontWeight: FontWeight.bold), titleLarge: GoogleFonts.roboto(fontSize: 22, fontWeight: FontWeight.w500), bodyMedium: GoogleFonts.openSans(fontSize: 14), );
Assets and Images
-
If images are needed, make them relevant and meaningful, with appropriate size, layout, and licensing (e.g., freely available). Provide placeholder images if real ones are not available.
-
Declare all asset paths in your
pubspec.yamlfile.flutter: uses-material-design: true assets: - assets/images/ -
Use
Image.assetto display local images from your asset bundle.Image.asset('assets/images/placeholder.png') -
Use
ImageIconto display an icon from anImageProvider, useful for custom icons not in theIconsclass. -
Use
Image.networkto display images from a URL, and always includeloadingBuilderanderrorBuilderfor a better user experience.Image.network( 'https://picsum.photos/200/300', loadingBuilder: (context, child, progress) { if (progress == null) return child; return const Center(child: CircularProgressIndicator()); }, errorBuilder: (context, error, stackTrace) { return const Icon(Icons.error); }, )
Accessibility (A11Y)
- Implement accessibility features to empower all users, assuming a wide variety of users with different physical abilities, mental abilities, age groups, education levels, and learning styles.