Files
minimax-skills/skills/flutter-dev/references/project-structure.md
mljxxx 2995582a5e feat: add flutter-dev skill
Add comprehensive Flutter cross-platform development guide covering:
- Widget patterns and const optimization
- Riverpod/Bloc state management
- GoRouter navigation
- Performance optimization
- Testing strategies
- Platform-specific implementations

Made-with: Cursor
2026-03-26 17:35:55 +08:00

275 lines
6.6 KiB
Markdown

# Project Structure
Flutter project architecture guide covering feature-based structure, dependencies, and entry point setup.
## Feature-Based Structure
```
lib/
├── main.dart # Entry point
├── app.dart # App widget, MaterialApp.router
├── core/
│ ├── constants/
│ │ ├── app_colors.dart
│ │ ├── app_strings.dart
│ │ └── app_sizes.dart
│ ├── theme/
│ │ ├── app_theme.dart
│ │ └── text_styles.dart
│ ├── utils/
│ │ ├── extensions.dart
│ │ └── validators.dart
│ └── errors/
│ └── failures.dart
├── features/
│ ├── auth/
│ │ ├── data/
│ │ │ ├── repositories/
│ │ │ │ └── auth_repository_impl.dart
│ │ │ └── datasources/
│ │ │ ├── auth_remote_datasource.dart
│ │ │ └── auth_local_datasource.dart
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ └── user.dart
│ │ │ ├── repositories/
│ │ │ │ └── auth_repository.dart
│ │ │ └── usecases/
│ │ │ ├── login.dart
│ │ │ └── logout.dart
│ │ ├── presentation/
│ │ │ ├── screens/
│ │ │ │ ├── login_screen.dart
│ │ │ │ └── register_screen.dart
│ │ │ └── widgets/
│ │ │ └── auth_form.dart
│ │ └── providers/
│ │ └── auth_provider.dart
│ └── home/
│ ├── data/
│ ├── domain/
│ ├── presentation/
│ └── providers/
├── shared/
│ ├── widgets/
│ │ ├── buttons/
│ │ │ └── primary_button.dart
│ │ ├── inputs/
│ │ │ └── text_input.dart
│ │ └── cards/
│ │ └── info_card.dart
│ ├── services/
│ │ ├── api_service.dart
│ │ └── storage_service.dart
│ └── models/
│ └── api_response.dart
└── routes/
└── app_router.dart
```
## Feature Layer Responsibilities
| Layer | Responsibility |
|-------|----------------|
| **data/** | API calls, local storage, DTOs, repository implementations |
| **domain/** | Business logic, entities, abstract repositories, use cases |
| **presentation/** | UI screens, widgets, view logic |
| **providers/** | Riverpod providers or Bloc definitions |
## pubspec.yaml Essentials
```yaml
name: my_app
description: A Flutter application.
version: 1.0.0+1
publish_to: 'none'
environment:
sdk: '>=3.3.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
# State Management (choose one)
flutter_riverpod: ^2.5.0
riverpod_annotation: ^2.3.0
# OR
flutter_bloc: ^8.1.0
# Navigation
go_router: ^14.0.0
# Networking
dio: ^5.4.0
# Code Generation
freezed_annotation: ^2.4.0
json_annotation: ^4.9.0
# Storage
shared_preferences: ^2.2.0
hive_flutter: ^1.1.0
# Utilities
flutter_hooks: ^0.20.0
cached_network_image: ^3.3.0
intl: ^0.19.0
dev_dependencies:
flutter_test:
sdk: flutter
# Code Generation
build_runner: ^2.4.0
riverpod_generator: ^2.4.0
freezed: ^2.5.0
json_serializable: ^6.8.0
# Linting
flutter_lints: ^4.0.0
# Testing
bloc_test: ^9.1.0
mocktail: ^1.0.0
flutter:
uses-material-design: true
```
## Main Entry Point
```dart
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize services
await Hive.initFlutter();
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
```
```dart
// app.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
return MaterialApp.router(
title: 'My App',
routerConfig: router,
theme: AppTheme.light,
darkTheme: AppTheme.dark,
themeMode: ThemeMode.system,
debugShowCheckedModeBanner: false,
);
}
}
```
## Router Provider
```dart
// routes/app_router.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
final routerProvider = Provider<GoRouter>((ref) {
return GoRouter(
initialLocation: '/',
debugLogDiagnostics: true,
redirect: (context, state) {
// Auth guard logic
return null;
},
routes: [
GoRoute(
path: '/',
name: 'home',
builder: (context, state) => const HomeScreen(),
),
// Add more routes
],
);
});
```
## Environment Configuration
```dart
// core/constants/environment.dart
enum Environment { dev, staging, prod }
class EnvConfig {
static Environment current = Environment.dev;
static String get baseUrl {
switch (current) {
case Environment.dev:
return 'https://dev-api.example.com';
case Environment.staging:
return 'https://staging-api.example.com';
case Environment.prod:
return 'https://api.example.com';
}
}
}
```
## Dependency Injection with Riverpod
```dart
// shared/services/api_service.dart
final apiServiceProvider = Provider<ApiService>((ref) {
final dio = Dio(BaseOptions(
baseUrl: EnvConfig.baseUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
));
// Add interceptors
dio.interceptors.add(AuthInterceptor(ref));
dio.interceptors.add(LogInterceptor(responseBody: true));
return ApiService(dio);
});
// features/auth/providers/auth_provider.dart
final authRepositoryProvider = Provider<AuthRepository>((ref) {
final api = ref.watch(apiServiceProvider);
final storage = ref.watch(storageServiceProvider);
return AuthRepositoryImpl(api: api, storage: storage);
});
```
## Best Practices
| Practice | Description |
|----------|-------------|
| Feature isolation | Each feature is self-contained |
| Dependency inversion | Domain depends on abstractions |
| Single responsibility | One class, one purpose |
| Naming conventions | Clear, descriptive names |
| Barrel exports | One index.dart per folder |
---
*Flutter is a trademark of Google LLC.*