- name
- flutter-std
- description
- Build and debug Flutter/Dart apps with Riverpod (codegen), Freezed, and GoRouter. Use when implementing Flutter features, running apps, hot reload/restart, or debugging UI.
# Flutter Std Tools
## When to use
- Use when the user is working on Flutter/Dart features, running the app, hot reload/restart, or debugging UI.
- Use when requests mention Riverpod, Freezed, GoRouter, or Flutter widget structure.
## Instructions
1) **Setup**: Ensure the project root is added via `dart___add_roots`. List devices with `dart___list_devices` before launching.
2) **Run & lifecycle**: Use `dart___launch_app` to run on a device. Use `dart___hot_reload` for UI changes (preserves state) and `dart___hot_restart` for logic/state resets. Avoid `flutter run` via shell. Stop apps with `dart___stop_app`.
3) **Debug & inspect**: Connect DTD with `dart___connect_dart_tooling_daemon` when needed. Use `dart___get_widget_tree`, `dart___get_runtime_errors`, and `dart___get_app_logs`.
4) **Testing & maintenance**: Run tests with `dart___run_tests`. Manage deps via `dart___pub`. Analyze/fix with `dart___analyze_files` and `dart___dart_fix`.
5) **Code generation**: `dart run build_runner build --delete-conflicting-outputs` (one-time) or `dart run build_runner watch --delete-conflicting-outputs` (watch).
## Requirements
### Architecture & tech stack
1) **State Management**: Must use **Riverpod** with code generation (`@riverpod`).
2) **Data Models**: Must use **Freezed** for immutable data classes and unions.
3) **Dependency Injection**: Must use **GetIt** and **Injectable** for service location.
4) **Routing**: Must use **GoRouter** for navigation (declarative routing).
5) **Database**: Must use **SQLite** for structured data storage.
6) **Widget State Structure (Stateful/Stateless)**: Follow the Widget State Structure section below and split widget classes into UI, Actions, and Utils extensions.
## Project Structure
- core/ - Core services and singletons
- config/ - App configuration
- di/ - Dependency injection setup (GetIt/Injectable)
- routing/ - GoRouter configuration
- utils/ - General utilities
- data/ - Data models and storage
- consts/ - Constant definitions
- models/ - Freezed data models
- providers/ - Riverpod providers (using code generation)
- repos/ - Data access layer
- services/ - External services (API, Database, etc.)
- views/ - UI components and pages
- pages/ - Main pages
- widgets/ - Reusable widgets
- pops/ - Popups and dialogs
- app.dart - App entry point widget
- main.dart - Main entry file
## Additional guidelines
- **Formatting**: Do NOT run `dart format`. Follow the project's existing code style.
- **Deprecations**: Replace all `Color.withOpacity` usage with `Color.withValues` (Flutter 3.27+).
- **Testing**: `utils` classes require unit test coverage.
# Widget State Structure (Stateful/Stateless)
To maintain clean and manageable code, split widget classes into three parts using `extensions` within the same file. This applies to both `StatefulWidget` (`State` class) and `StatelessWidget` (the widget class), separating UI from logic and event handling.
## Structure
1. **UI**: The main `State` class containing the `build()` method and widget definitions.
2. **Actions**: An extension on the `State` class for event handlers (e.g., `_onTap`, `_submit`).
3. **Utils**: An extension on the `State` class for private helper methods and business logic.
## Example (StatefulWidget)
```dart
import 'package:flutter/material.dart';
class MyPage extends StatefulWidget {
const MyPage({super.key});
@override
State<MyPage> createState() => _MyPageState();
}
// 1. UI - Main State Class
class _MyPageState extends State<MyPage> {
final _controller = TextEditingController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('My Page')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller,
decoration: const InputDecoration(labelText: 'Enter text'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _onSubmit, // Reference to Actions extension
child: const Text('Submit'),
),
],
),
),
);
}
}
// 2. Actions - Event Handlers
extension _MyPageActions on _MyPageState {
void _onSubmit() {
final text = _controller.text;
if (_validateInput(text)) { // Reference to Utils extension
_showSuccessDialog(text);
} else {
_showErrorSnackBar();
}
}
void _showSuccessDialog(String text) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Success'),
content: Text('You entered: $text'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
}
// 3. Utils - Helper Methods & Logic
extension _MyPageUtils on _MyPageState {
bool _validateInput(String text) {
return text.isNotEmpty && text.length > 3;
}
void _showErrorSnackBar() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Input must be at least 4 characters')),
);
}
}
```
## Example (StatelessWidget)
```dart
import 'package:flutter/material.dart';
class MyStatelessPage extends StatelessWidget {
const MyStatelessPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('My Stateless Page')),
body: Center(
child: ElevatedButton(
onPressed: () => _onSubmit(context), // Reference to Actions extension
child: const Text('Submit'),
),
),
);
}
}
// 2. Actions - Event Handlers
extension _MyStatelessPageActions on MyStatelessPage {
void _onSubmit(BuildContext context) {
if (_validateInput('ok')) { // Reference to Utils extension
_showSuccessDialog(context);
} else {
_showErrorSnackBar(context);
}
}
void _showSuccessDialog(BuildContext context) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Success'),
content: const Text('Submitted.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
}
// 3. Utils - Helper Methods & Logic
extension _MyStatelessPageUtils on MyStatelessPage {
bool _validateInput(String text) {
return text.isNotEmpty;
}
void _showErrorSnackBar(BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Invalid input')),
);
}
}
```
## Example prompts
- "Run the flutter app on the iOS simulator"
- "Hot reload the app to show the new colors"
- "Create a new Riverpod provider for user authentication using code gen"
- "Implement a settings page using the project's existing preferences store"
- "Add a SQLite table for storing todo items"
- "Add a new route for the profile page using GoRouter"
- "Fix analysis errors in the project"
- "Generate a Freezed model for User with name and age fields"
Auf GitHub ansehen