Flutter / Dart Conventions
File and Directory Structure
lib/
├── main.dart
├── app.dart # MaterialApp / root widget
├── core/ # Shared utilities, constants, extensions
│ ├── constants/
│ ├── extensions/
│ └── utils/
├── features/ # Feature-first organization
│ └── {feature}/
│ ├── data/ # Repositories, data sources, DTOs
│ ├── domain/ # Entities, use cases, interfaces
│ └── presentation/ # Widgets, screens, view models / providers
└── shared/ # Reusable UI components
└── widgets/
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Files | snake_case.dart |
user_profile_card.dart |
| Classes | PascalCase |
UserProfileCard |
| Variables / fields | camelCase |
userName |
| Constants | camelCase (not SCREAMING) |
defaultTimeout |
| Private fields | _camelCase |
_isLoading |
| Providers (Riverpod) | camelCase + Provider suffix |
userProfileProvider |
Widget Conventions
- Use
constconstructors whenever no runtime-varying field exists - Always add
{super.key}to the constructor - Extract subtrees exceeding ~50 lines into named widget classes
- Never put business logic in
build()— call methods or read providers only - Access
ThemeviaTheme.of(context)— do not hardcode colors
// Good
class UserProfileCard extends StatelessWidget {
const UserProfileCard({super.key, required this.user});
final User user;
...
}
// Bad — missing const, missing key
class UserProfileCard extends StatelessWidget {
UserProfileCard({required this.user});
...
}
State Management (Riverpod)
ref.watchonly insidebuild()orHookWidget.build()ref.readonly in event handlers / callbacksref.listenfor side effects (navigation, snackbars)- Keep providers small and single-responsibility
- Prefer
AsyncNotifierProvideroverFutureProviderfor mutable async state
Null Safety
- Avoid
!unless null is provably impossible; add a comment explaining why - Prefer
?.,??, and null-aware patterns - Prefer non-nullable types in public APIs
- Never use
dynamicunless interacting with untyped external data (JSON deserialization)
Imports
Use package: imports for all cross-package and intra-package imports.
Avoid relative imports for files outside the same directory.
// Good
import 'package:my_app/features/auth/domain/user.dart';
// Bad
import '../../../features/auth/domain/user.dart';
Testing
- Widget tests go in
test/mirroring thelib/structure - Name test files
{source_file}_test.dart - Each
group()corresponds to one class - Use
testWidgetsfor widgets,testfor pure logic - Mock dependencies with
mocktail(preferred) ormockito
Useful Commands
/flutter-widget <WidgetName>— scaffold widget + test/flutter-test <path>— generate tests for existing code/flutter-l10n <key> <text>— add localization key