John Ryan 377ed85dd2
[docs] Add initial version of Flutter AI rules (#175011)
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>
2025-09-16 20:04:10 +00:00

12 KiB
Raw Blame History

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.dart as the primary application entry point.

Package Management

  • To manage packages, use the pub tool, if available.
  • If a new feature requires an external package, use the pub_dev_search tool, if it is available. Otherwise, identify the most suitable and stable package from pub.dev.
  • To add a regular dependency, use the pub tool, if it is available. Otherwise, run flutter pub add <package_name>.
  • To add a development dependency, use the pub tool, if it is available, with dev:<package name>. Otherwise, run flutter pub add dev:<package_name>.
  • To add a dependency override, use the pub tool, if it is available, with override:<package name>:1.0.0. Otherwise, run flutter pub add override:<package_name>:1.0.0.
  • To remove a dependency, use the pub tool, if it is available. Otherwise, run dart 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/await for 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 Widget classes instead of private helper methods that return a Widget.
  • Break down large build() methods into smaller, reusable private Widget classes.
  • Use ListView.builder to create lazy-loaded lists for performance.
  • Use const constructors for widgets and in build() 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 Streams and StreamBuilder for handling a sequence of asynchronous events.

  • Use Futures and FutureBuilder for handling a single asynchronous operation that will complete in the future.

  • Use ValueNotifier with ValueListenableBuilder for 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 ListenableBuilder to listen to changes from a ChangeNotifier or other Listenable.

  • 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, provider can 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_router for 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 Navigator for 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_serializable and json_annotation for parsing and encoding JSON data.

  • When encoding data, use fieldRename: FieldRename.snake to 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 log function from dart:developer for 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_runner for all code generation tasks, such as for json_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_tests tool if it is available, otherwise use flutter test.
  • Use package:test for unit tests.
  • Use package:flutter_test for widget tests.
  • Use package:integration_test for integration tests.
  • Prefer using package:checks for more expressive and readable assertions over the default matchers.

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 users 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 ThemeData object to ensure a consistent application-wide style.

  • Use Material 3 by setting useMaterial3: true in your ThemeData.

  • 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_fonts package. Define a TextTheme to 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.yaml file.

    flutter:
      uses-material-design: true
      assets:
        - assets/images/
    
  • Use Image.asset to display local images from your asset bundle.

    Image.asset('assets/images/placeholder.png')
    
  • Use ImageIcon to display an icon from an ImageProvider, useful for custom icons not in the Icons class.

  • Use Image.network to display images from a URL, and always include loadingBuilder and errorBuilder for 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.