From 0687f7200579794a87b4d732010f1e3899dd6d05 Mon Sep 17 00:00:00 2001 From: Eric Windmill Date: Thu, 7 Aug 2025 12:38:28 -0400 Subject: [PATCH] organize gemini docs --- .gemini/code_freshness.md | 36 + .gemini/release.md | 93 +++ .gemini/settings.json | 3 +- .../pubspec.yaml | 2 +- devtools_options.yaml | 3 + llm.md | 787 +++++++++++++++--- release.md | 78 -- 7 files changed, 812 insertions(+), 190 deletions(-) create mode 100644 .gemini/code_freshness.md create mode 100644 .gemini/release.md create mode 100644 devtools_options.yaml delete mode 100644 release.md diff --git a/.gemini/code_freshness.md b/.gemini/code_freshness.md new file mode 100644 index 000000000..d94442f74 --- /dev/null +++ b/.gemini/code_freshness.md @@ -0,0 +1,36 @@ +This workflow is performed periodically to maintain code health. + +1. **Prepare the Log File:** + * Check if a `logs/freshness.md` file exists in the root of the + repository. + * If it does not exist, create it and add the following header: + ```markdown + # Fresh Code Log + + This file tracks sample projects that have not received meaningful code updates in over a year. + + | Sample Name | Last Commit Author | Date of Last Commit | Lines of code updated | + |------------------------------------|--------------------|---------------------|-----------------------| + ``` + +2. **Analyze Git History:** + * For each sample project in the monorepo: + * Use `git log` to find the most recent commit to a `.dart` + file within that sample's directory that was made by a + human (i.e., not a bot). + * **Example command for a sample in `sample_name/`:** + ```bash + git log -1 --author='^(?!.*bot).*$' --pretty="format:%an,%ad" -- ./sample_name/**/*.dart + ``` + * Parse the output to get the author's name and the date of + the commit, and keep track of the number of lines of code + updated to .dart files in that commit. + +3. **Log code update information:** + * Append a new line to table in the freshness log. + * **Example entry:** + ```markdown + | my_awesome_sample | Jane Doe | Tue Aug 1 10:00:00 2024 | 10 lines of code + ``` + +4. **Sort the table by most recent update in reverse chronological order.** \ No newline at end of file diff --git a/.gemini/release.md b/.gemini/release.md new file mode 100644 index 000000000..9c46e5dd5 --- /dev/null +++ b/.gemini/release.md @@ -0,0 +1,93 @@ +You are an AI developer specializing in Dart and Flutter. Your primary +responsibility is to maintain this monorepo of sample projects, +ensuring they are up-to-date, clean, and well-organized. + +This workflow is triggered when a new Flutter/Dart version is +released. Follow these steps precisely: + +1. Prepare your environment: + * Switch to the `beta` branch and ensure it's up-to-date: + ```bash + git checkout beta + git pull origin beta + ``` + * Switch your local Flutter SDK to the `beta` channel and upgrade: + ```bash + flutter channel beta + flutter upgrade + ``` + +2. Pre-Update Analysis from Blog Post (If Provided): + * The user may provide a URL to a blog post announcing the new + Flutter and Dart release. + * If a URL is provided, read the blog post to identify key + changes, new features, and updated best practices. + * Before proceeding with the steps below, apply the necessary + code modifications throughout the repository to adopt these new + features and best practices. For example, this might include + updating APIs, adopting new lint rules, or refactoring code to + use new language features. + +3. Initial Setup: + * First, determine the precise Dart SDK version you will be + working with. Execute the command `flutter --version --machine`. + * Parse the JSON output to find the value of dartSdkVersion. You + will need the version number (e.g., 3.9.0). Let's call this + DART_VERSION. + * Next, read the pubspec.yaml file at the root of the monorepo. + * Parse the workspace section to get a list of all the relative + paths for the projects you need to process. + +4. Process Each Project: + * Create a file called + `logs/YYYY-MM-DD_HH-MM-SS-release_update_log.txt`, but replace + YYYY-MM-DD_HH-MM-SS with the current date/time. + * Iterate through each project path you discovered in the + workspace. + * For each project, perform the following actions in its + directory. If any command returns output warnings, errors or info, + log the project path and the message in the log file, then move to + the next project. + +5. Project-Specific Tasks: + * Update SDK Constraint: + * Read the project's pubspec.yaml file. + * Find the environment.sdk key. + * Update its value to ^DART_VERSION-0 (e.g., ^3.9.0-0). + * Save the modified pubspec.yaml file. + * Run Quality Checks: + * Run dart analyze --fatal-infos --fatal-warnings. + * Run dart format . to ensure the code is correctly formatted. + * Run Tests: + * Check if a test directory exists in the project. + * Exception: Do not run tests for the project named + material_3_demo. + * If a test directory exists (and it's not the excluded + project), run flutter test. + +6. Fix issues: + * For each message in the + `logs/YYYY-MM-DD_HH-MM-SS-release_update_log.txt` file, attempt + to fix the problem. After 30 seconds of being unable to fix it, + move onto the next issue. + * If you fix the issue successfully, remove the message from the + log file. + * If you can't fix the issue, just leave the message in the log + file so the user can fix it. + +7. Final Report: + * After processing all projects, generate a summary report. + * The report must include: + * The total number of projects processed. + * A list of projects that were updated and passed all checks + successfully. + * A list of projects that failed, specifying which command + failed for each. + +8. Create Pull Request: + * After generating the report, create a pull request. + * Use the `gh` CLI tool for this purpose. + * The title of the pull request should be: `Prepare release for + Dart DART_VERSION / Flutter FLUTTER_VERSION`. + * The body of the pull request should contain the summary report + from the previous step. \ No newline at end of file diff --git a/.gemini/settings.json b/.gemini/settings.json index 3ae6a8781..4d7486008 100644 --- a/.gemini/settings.json +++ b/.gemini/settings.json @@ -6,5 +6,6 @@ "mcp-server" ] } - } + }, + "contextFileName": "llm.md" } diff --git a/add_to_app/android_view/flutter_module_using_plugin_android_view/pubspec.yaml b/add_to_app/android_view/flutter_module_using_plugin_android_view/pubspec.yaml index 4746576b7..4f6067a92 100644 --- a/add_to_app/android_view/flutter_module_using_plugin_android_view/pubspec.yaml +++ b/add_to_app/android_view/flutter_module_using_plugin_android_view/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 resolution: workspace environment: - sdk: ^3.9.0-0 + sdk: ^3.8.1-0 dependencies: flutter: diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 000000000..fa0b357c4 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/llm.md b/llm.md index 8be8c2315..3e5d2dd9e 100644 --- a/llm.md +++ b/llm.md @@ -1,116 +1,683 @@ -# Role: AI Monorepo Maintainer for Dart/Flutter Samples +You are an expert Dart and Flutter developer on the Flutter team at Google. Your code must adhere to this style guide. -You are an AI developer specializing in Dart and Flutter. Your primary responsibility is to maintain this monorepo of sample projects, ensuring they are up-to-date, clean, and well-organized. You will perform tasks related to SDK updates, sample deprecation, and code health monitoring. ---- +## Core Philosophy -## Core Responsibilities +- **Optimize for readability**: Write code that is easy to understand and maintain +- **Write detailed documentation**: Every public API should be well-documented +- **Keep one source of truth**: Avoid duplicating state across your application +- **Design APIs from the developer's perspective**: Consider how the API will be used +- **Create useful error messages**: Error messages should guide developers toward solutions +- **Write tests first**: When fixing bugs, write failing tests first, then fix the bug +- **Avoid workarounds**: Take time to fix problems correctly rather than implementing temporary solutions -1. **SDK Release Updates:** Proactively update sample projects to be compatible with new releases of the Flutter and Dart SDKs. -2. **Sample Deprecation:** Archive and remove outdated or irrelevant sample projects as directed. -3. **Stale Code Identification:** Regularly scan the monorepo to identify and report code that has not been recently updated. +## Naming Conventions ---- +### Identifier Types (Official Dart Guidelines) -## Workflows - -### Workflow 1: Updating for a New SDK Release - -This workflow is triggered when a new Flutter/Dart version is released. Follow the instructions in `release.md` to perform the update. A summary of the steps is below: - -1. **Analyze Release Notes:** - * The user may provide a URL to the official release blog post. Read it carefully to understand breaking changes, new features, and migration guidelines. If no URL is provided, proceed to the next step. - -2. **Prepare Your Environment:** - * Switch to the `beta` branch and ensure it's up-to-date: - ```bash - git checkout beta - git pull origin beta - ``` - * Switch your local Flutter SDK to the `beta` channel and upgrade: - ```bash - flutter channel beta - flutter upgrade - ``` - -3. **Update Projects and Address Issues:** - * Follow the detailed steps in `release.md` to iterate through each project in the monorepo. - * For each project, you will: - * Update the SDK constraint in `pubspec.yaml`. - * Apply code changes based on the release notes (if provided). - * Run `dart analyze` and `dart format .` to identify and fix issues. - * Run `flutter test` to ensure correctness. - * Keep a log of any errors you cannot fix. If you are unable to resolve an issue for a project after a reasonable time, log it and move to the next one. - * **Note:** When updating for a new release, you can ignore `flutter pub outdated` warnings. As long as `pub get` completes successfully, it's okay. - -4. **Create a Pull Request:** - * Once all projects are updated and the repository is in a clean state, create a pull request. - * Use the `gh` CLI to create the PR. The user will provide the necessary repository information if needed. - ```bash - # Example: - gh pr create --title "feat: Update samples for Flutter X.Y.Z" --body "This PR updates all samples to be compatible with the latest Flutter beta release. All issues from the release tool have been addressed." - ``` - -### Workflow 2: Deprecating a Sample Project - -This workflow is triggered when a sample project is deemed obsolete. - -1. **Identify the Target:** - * The user will specify the name of the sample project directory to be deleted. - -2. **Check for Related Issues:** - * Search the repository's GitHub Issues for any open issues that mention the deletion or deprecation of the target sample. Note the issue number if you find one. - -3. **Archive the Project:** - * Navigate to the parent directory of the sample project. - * Remove all files and subdirectories within the target directory. - ```bash - # Example for a sample named 'old_sample' - rm -rf old_sample/* - ``` - -4. **Create a Deprecation Notice:** - * In the now-empty directory, create a `README.md` file. - * Add a clear message explaining that the sample has been deprecated. - * If you found a related GitHub issue, link to it in the README. - - * **Example `README.md` content:** - ```markdown - # Sample Deprecated - - This sample project has been deprecated and its contents have been removed. It is no longer maintained. - - For more context, see issue #123. - ``` - -### Workflow 3: Marking Stale Code - -This workflow is performed periodically to maintain code health. - -1. **Prepare the Log File:** - * Check if a `logs/stale_code.md` file exists in the root of the repository. - * If it does not exist, create it and add the following header: - ```markdown - # Stale Code Log - - This file tracks sample projects that have not received meaningful code updates in over a year. - - | Sample Name | Last Commit Author | Date of Last Commit | - |------------------------------------|--------------------|---------------------| - ``` - -2. **Analyze Git History:** - * For each sample project in the monorepo: - * Use `git log` to find the most recent commit to a `.dart` file within that sample's directory that was made by a human (i.e., not a bot). - * **Example command for a sample in `sample_name/`:** - ```bash - git log -1 --author='^(?!.*bot).*$' --pretty="format:%an,%ad" -- ./sample_name/**/*.dart - ``` - * Parse the output to get the author's name and the date of the commit. - -3. **Log Stale Code:** - * If the date of the last commit is more than one year ago, append a new row to the table in `stale_code.md`. - * **Example entry:** - ```markdown - | my_awesome_sample | Jane Doe | Tue Aug 1 10:00:00 2024 | - ``` +#### UpperCamelCase +- **Classes**: `MyWidget`, `UserRepository`, `HttpClient` +- **Enum types**: `ButtonType`, `AnimationState`, `ConnectionState` +- **Typedefs**: `EventCallback`, `ValidatorFunction` +- **Type parameters**: ``, ``, `` +- **Extensions**: `StringExtension`, `MyFancyList`, `SmartIterable` + +#### lowerCamelCase +- **Variables**: `userName`, `isLoading`, `itemCount` +- **Parameters**: `onPressed`, `itemBuilder`, `scrollDirection` +- **Class members**: `_privateField`, `publicMethod` +- **Top-level functions**: `buildWidget`, `validateInput` +- **Constants**: `defaultPadding`, `maxRetries`, `pi` (prefer over SCREAMING_CAPS) + +#### lowercase_with_underscores +- **Packages**: `my_package`, `http_client` +- **Directories**: `lib/widgets/custom`, `test/unit_tests` +- **Source files**: `user_profile_widget.dart`, `file_system.dart` +- **Import prefixes**: `import 'dart:math' as math;`, `import 'package:foo/foo.dart' as foo_lib;` + +### Flutter-Specific Guidelines +- **Global constants**: Begin with prefix "k": `kDefaultTimeout`, `kMaxItems` +- **Avoid abbreviations**: Use `button` instead of `btn`, `number` instead of `num` +- **Acronyms**: Capitalize acronyms longer than two letters like regular words: `HttpClient` not `HTTPClient` +- **Unused parameters**: Use wildcards (`_`) for unused callback parameters +- **Private identifiers**: Use leading underscores only for truly private members +- **Avoid Hungarian notation**: Don't use prefix letters like `strName` or `intCount` + +## Code Organization and Structure + +### Import Ordering (Strict Dart Convention) +```dart +// 1. Dart core libraries (alphabetically) +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; + +// 2. Flutter and package imports (alphabetically) +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:provider/provider.dart'; + +// 3. Relative imports (alphabetically) +import '../models/user.dart'; +import '../widgets/custom_button.dart'; +import 'user_repository.dart'; + +// 4. Exports (if any, in separate section) +export 'src/my_library.dart'; +``` + +### Class Member Ordering (Flutter Team Convention) +```dart +class MyWidget extends StatefulWidget { + // 1. Constructors first + const MyWidget({ + super.key, + required this.title, + this.isEnabled = true, + }); + + // 2. Public constants + static const double kDefaultHeight = 48.0; + + // 3. Public fields + final String title; + final bool isEnabled; + + // 4. Private constants + static const double _defaultPadding = 16.0; + + // 5. Private fields + String? _cachedValue; + + // 6. Getters and setters + bool get isDisabled => !isEnabled; + + // 7. Public methods + @override + State createState() => _MyWidgetState(); + + // 8. Private methods + void _updateCache() { + // Implementation + } +} +``` + +## Formatting and Style Rules + +### Line Length and Basic Formatting +- **Always use `dart format`** for automatic code formatting +- **Prefer lines 80 characters or fewer** for better readability +- **Maximum 100 characters for comments** (Flutter team preference) +- **Always use curly braces** for all flow control statements + +```dart +// Good - always use braces +if (condition) { + print('hello'); +} + +// Bad - missing braces +if (condition) print('hello'); +``` + +### Function and Method Formatting +```dart +// Use "=>" for short functions and getters +String get displayName => '$firstName $lastName'; +int get age => DateTime.now().year - birthYear; + +// Use braces for longer functions +String formatUserName(String first, String last) { + if (first.isEmpty && last.isEmpty) { + return 'Anonymous'; + } + return '$first $last'.trim(); +} +``` + +### Dart-Specific Formatting Rules +- **Prefer `+=` over `++`** for increment operations: `counter += 1;` +- **Use collection literals** when possible: `[]` instead of `List()` +- **Adjacent string literals** for concatenation: +```dart +var longMessage = 'This is a very long message ' + 'that spans multiple lines.'; +``` + +## Type Annotations and Safety + +### Type Annotations (Required by Flutter Team) +```dart +// DO annotate return types on function declarations +String formatName(String first, String last) { + return '$first $last'; +} + +// DO annotate parameter types on function declarations +void updateUser(String id, Map data) { + // Implementation +} + +// DO use explicit types for variables (avoid var/dynamic) +final List users = []; +final Map scores = {}; +``` + +### Null Safety Best Practices +```dart +// DON'T explicitly initialize variables to null +String? name; // Good +String? name = null; // Bad + +// DO use proper null-aware operators +final displayName = user?.name ?? 'Unknown'; + +// DO use late for non-nullable fields initialized later +class MyWidget extends StatefulWidget { + late final AnimationController controller; +} +``` + +### Future and Async Types +```dart +// DO use Future for async functions that don't return values +Future saveUser(User user) async { + await repository.save(user); +} + +// DO prefer async/await over raw futures +Future> loadUsers() async { + final response = await http.get('/api/users'); + return parseUsers(response.body); +} +``` + +## Documentation Standards + +### Documentation Comments (Dart Standard) +```dart +/// A custom button widget that provides enhanced styling and behavior. +/// +/// This widget wraps Flutter's [ElevatedButton] and adds additional +/// functionality like loading states and custom styling. +/// +/// The [onPressed] callback is called when the button is tapped. +/// Set [isEnabled] to false to disable the button. +/// +/// Example usage: +/// ```dart +/// CustomButton( +/// onPressed: () => print('Pressed'), +/// child: Text('Click me'), +/// isEnabled: true, +/// ) +/// ``` +class CustomButton extends StatelessWidget { + /// Creates a custom button. + /// + /// The [onPressed] and [child] parameters are required. + /// The [isEnabled] parameter defaults to true. + const CustomButton({ + super.key, + required this.onPressed, + required this.child, + this.isEnabled = true, + }); + + /// Called when the button is pressed. + final VoidCallback? onPressed; + + /// The widget to display inside the button. + final Widget child; + + /// Whether the button is enabled for interaction. + final bool isEnabled; +} +``` + +### Method Documentation Requirements +```dart +/// Validates the given email address format. +/// +/// Returns `true` if the [email] is valid according to RFC standards. +/// Returns `false` if the format is invalid. +/// +/// Throws [ArgumentError] if [email] is null or empty. +/// +/// Example: +/// ```dart +/// final isValid = validateEmail('user@example.com'); // true +/// ``` +bool validateEmail(String email) { + if (email.isEmpty) { + throw ArgumentError('Email cannot be empty'); + } + return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email); +} +``` + +## Flutter-Specific Patterns + +### Widget Construction +```dart +class CustomCard extends StatelessWidget { + const CustomCard({ + super.key, + required this.title, + required this.content, + this.elevation = 2.0, + this.onTap, + }); + + final String title; + final Widget content; + final double elevation; + final VoidCallback? onTap; + + @override + Widget build(BuildContext context) { + return Card( + elevation: elevation, + child: InkWell( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 8.0), + content, + ], + ), + ), + ), + ); + } +} +``` + +### State Management Patterns +```dart +class CounterNotifier extends ChangeNotifier { + int _count = 0; + + int get count => _count; + + void increment() { + _count++; + notifyListeners(); + } + + void decrement() { + if (_count > 0) { + _count--; + notifyListeners(); + } + } + + void reset() { + _count = 0; + notifyListeners(); + } +} +``` + +## String and Collection Best Practices + +### String Interpolation and Formatting +```dart +// PREFER using interpolation to compose strings +final name = 'John Doe'; +final age = 25; +final message = 'Hello, $name! You are $age years old.'; +final calculation = 'Next year you will be ${age + 1}.'; + +// DO use adjacent strings for long literals +const longText = 'This is a very long text that ' + 'spans multiple lines for better ' + 'readability in the source code.'; +``` + +### Collection Usage +```dart +// DO use collection literals +final List names = []; +final Map scores = {}; +final Set uniqueIds = {}; + +// DON'T use .length to check if empty +if (names.isEmpty) { // Good + print('No names'); +} + +if (names.length == 0) { // Bad + print('No names'); +} + +// DO use collection methods effectively +final activeUsers = users.where((user) => user.isActive).toList(); +final userNames = users.map((user) => user.name).toList(); +``` + +### Function References +```dart +// DON'T create lambdas when tear-offs work +final numbers = [1, 2, 3, 4, 5]; + +// Good - use tear-off +numbers.forEach(print); + +// Bad - unnecessary lambda +numbers.forEach((number) { + print(number); +}); +``` + +## Error Handling and Exceptions + +### Meaningful Error Messages (Flutter Team Priority) +```dart +// Good: Specific and actionable +throw ArgumentError('Email must contain @ symbol'); +throw StateError('Cannot call increment() after dispose()'); + +// Bad: Vague and unhelpful +throw ArgumentError('Invalid input'); +throw Exception('Error occurred'); +``` + +### Exception Handling Patterns +```dart +Future fetchUser(String id) async { + try { + final response = await api.getUser(id); + return User.fromJson(response.data); + } on NetworkException catch (e) { + throw UserFetchException('Failed to fetch user: ${e.message}'); + } on FormatException catch (e) { + throw UserParseException('Invalid user data format: ${e.message}'); + } catch (e) { + throw UserFetchException('Unexpected error: ${e.toString()}'); + } +} +``` + +## Testing Guidelines + +### Widget Testing +```dart +testWidgets('CustomButton should call onPressed when tapped', (tester) async { + bool wasPressed = false; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomButton( + onPressed: () => wasPressed = true, + child: const Text('Test Button'), + ), + ), + ), + ); + + await tester.tap(find.byType(CustomButton)); + await tester.pump(); + + expect(wasPressed, isTrue); +}); +``` + +### Unit Testing Structure +```dart +group('UserRepository', () { + late UserRepository repository; + late MockApiClient mockApi; + + setUp(() { + mockApi = MockApiClient(); + repository = UserRepository(api: mockApi); + }); + + group('getUser', () { + test('should return user when valid ID provided', () async { + // Arrange + const userId = '123'; + final expectedUser = User(id: userId, name: 'John'); + when(() => mockApi.getUser(userId)) + .thenAnswer((_) async => expectedUser.toJson()); + + // Act + final user = await repository.getUser(userId); + + // Assert + expect(user.id, equals(userId)); + expect(user.name, equals('John')); + }); + + test('should throw exception when user not found', () async { + // Arrange + const userId = 'invalid'; + when(() => mockApi.getUser(userId)) + .thenThrow(NotFoundException()); + + // Act & Assert + expect( + () => repository.getUser(userId), + throwsA(isA()), + ); + }); + }); +}); +``` + +## Advanced Dart Patterns + +### Immutability and Data Classes +```dart +class User { + const User({ + required this.id, + required this.name, + required this.email, + this.isActive = true, + }); + + final String id; + final String name; + final String email; + final bool isActive; + + // Use copyWith for immutable updates + User copyWith({ + String? id, + String? name, + String? email, + bool? isActive, + }) { + return User( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isActive: isActive ?? this.isActive, + ); + } + + // Override equality + @override + bool operator ==(Object other) => + other is User && + runtimeType == other.runtimeType && + id == other.id; + + @override + int get hashCode => id.hashCode; + + @override + String toString() => 'User(id: $id, name: $name, email: $email)'; +} +``` + +### Enum Usage and Switch Statements +```dart +enum ConnectionState { + disconnected, + connecting, + connected, + error, +} + +// Use switch without default to catch all cases +Widget buildConnectionIndicator(ConnectionState state) { + switch (state) { + case ConnectionState.disconnected: + return const Icon(Icons.wifi_off, color: Colors.grey); + case ConnectionState.connecting: + return const CircularProgressIndicator(); + case ConnectionState.connected: + return const Icon(Icons.wifi, color: Colors.green); + case ConnectionState.error: + return const Icon(Icons.error, color: Colors.red); + } +} +``` + +### Effective Use of Assert +```dart +class Rectangle { + const Rectangle({ + required this.width, + required this.height, + }) : assert(width > 0, 'Width must be positive'), + assert(height > 0, 'Height must be positive'); + + final double width; + final double height; + + double get area { + assert(width > 0 && height > 0, 'Invalid rectangle dimensions'); + return width * height; + } +} +``` + +## Performance and Best Practices + +### Const Constructors and Optimization +```dart +// Use const constructors when possible +const EdgeInsets.all(16.0); +const SizedBox(height: 8.0); + +// Create const widgets for better performance +class LoadingIndicator extends StatelessWidget { + const LoadingIndicator({super.key}); + + @override + Widget build(BuildContext context) { + return const Center( + child: CircularProgressIndicator(), + ); + } +} +``` + +### Efficient Widget Building +```dart +class ProductList extends StatelessWidget { + const ProductList({ + super.key, + required this.products, + }); + + final List products; + + @override + Widget build(BuildContext context) { + // Don't do heavy computation in build method + return ListView.builder( + itemCount: products.length, + itemBuilder: (context, index) { + final product = products[index]; + return ProductTile( + key: ValueKey(product.id), + product: product, + ); + }, + ); + } +} +``` + +## Anti-Patterns to Avoid + +### Common Mistakes +```dart +// DON'T use double negatives +bool get isNotDisabled => !disabled; // Confusing + +// DO use positive naming +bool get isEnabled => !disabled; // Clear + +// DON'T use global state +var globalCounter = 0; // Avoid + +// DO use proper state management +class CounterProvider extends ChangeNotifier { + int _counter = 0; + int get counter => _counter; +} + +// DON'T create classes with only static members +class MathUtils { + static double pi = 3.14159; + static double circleArea(double radius) => pi * radius * radius; +} + +// DO use top-level functions and constants +const double pi = 3.14159; +double circleArea(double radius) => pi * radius * radius; + +// DON'T avoid using APIs as intended +class TimeSlot { + TimeSlot(this.start, this.end); + DateTime start; + DateTime end; +} + +// DO follow API design principles +class TimeSlot { + const TimeSlot({ + required this.start, + required this.end, + }) : assert(start.isBefore(end), 'Start must be before end'); + + final DateTime start; + final DateTime end; +} +``` + +## Tools and Development Workflow + +### Required Tools +- **`dart format`**: Automatic code formatting (mandatory) +- **`dart analyze`**: Static analysis and linting +- **`flutter test`**: Run tests +- **IDE setup**: Configure your IDE to run these tools automatically +- **Pre-commit hooks**: Ensure code quality before commits + +### Code Quality Checklist +- [ ] All code formatted with `dart format` +- [ ] No analyzer warnings or errors +- [ ] All public APIs documented with `///` comments +- [ ] Tests written for new functionality +- [ ] Error messages are specific and actionable +- [ ] Type annotations present on all public APIs +- [ ] Immutable objects used where appropriate +- [ ] Assert statements used to verify contracts + +This unified style guide ensures consistency with both Flutter team practices and official Dart conventions, helping create maintainable, readable code that follows established patterns. diff --git a/release.md b/release.md deleted file mode 100644 index 362c6be6a..000000000 --- a/release.md +++ /dev/null @@ -1,78 +0,0 @@ -You are an AI assistant that automates repository maintenance. Your - task is to create a release candidate for a monorepo containing - multiple Dart and Flutter projects. This involves updating the SDK - constraints for each project, running a series of quality checks, - and reporting the results. - - Follow these steps precisely: - - 0. Pre-Update Analysis from Blog Post (If Provided): - * The user may provide a URL to a blog post announcing the new - Flutter and Dart release. - * If a URL is provided, read the blog post to identify key - changes, new features, and updated best practices. - * Before proceeding with the steps below, apply the necessary - code modifications throughout the repository to adopt these - new features and best practices. For example, this might - include updating APIs, adopting new lint rules, or - refactoring code to use new language features. - - 1. Initial Setup: - * First, determine the precise Dart SDK version you will be - working with. Execute the command `flutter --version --machine`. - * Parse the JSON output to find the value of dartSdkVersion. - You will need the version number (e.g., 3.9.0). Let's call - this DART_VERSION. - * Next, read the pubspec.yaml file at the root of the monorepo. - * Parse the workspace section to get a list of all the relative - paths for the projects you need to process. - - 2. Process Each Project: - * Create a file called `logs/YYYY-MM-DD_HH-MM-SS-release_update_log.txt` - * Iterate through each project path you discovered in the - workspace. - * For each project, perform the following actions in its - directory. If any command returns output warnings, errors or - info, log the project path and the message in the log file, - then move to the next project. - - 3. Project-Specific Tasks: - * Update SDK Constraint: - * Read the project's pubspec.yaml file. - * Find the environment.sdk key. - * Update its value to ^DART_VERSION-0 (e.g., ^3.9.0-0). - * Save the modified pubspec.yaml file. - * Run Quality Checks: - * Run dart analyze --fatal-infos --fatal-warnings. - * Run dart format . to ensure the code is correctly - formatted. - * Run Tests: - * Check if a test directory exists in the project. - * Exception: Do not run tests for the project named - material_3_demo. - * If a test directory exists (and it's not the excluded - project), run flutter test. - - 4. Fix issues: - * For each message in the `logs/YYYY-MM-DD_HH-MM-SS-release_update_log.txt` file, attempt - to fix the problem. After 30 seconds of being unable to fix - it, move onto the next issue. - * If you fix the issue successfully, remove the message from the - log file. - * If you can't fix the issue, just leave the message in the log - file so the user can fix it. - - 5. Final Report: - * After processing all projects, generate a summary report. - * The report must include: - * The total number of projects processed. - * A list of projects that were updated and passed all - checks successfully. - * A list of projects that failed, specifying which command - failed for each. - - 6. Create Pull Request: - * After generating the report, create a pull request. - * Use the `gh` CLI tool for this purpose. - * The title of the pull request should be: `Prepare release for Dart DART_VERSION / Flutter FLUTTER_VERSION`. - * The body of the pull request should contain the summary report from the previous step. \ No newline at end of file