F
Flutter Conventions & Best Practices
About Flutter Conventions & Best Practices
A reference skill for Flutter developers, covering Dart 3.x syntax, naming, project structure, null safety, asynchronous code, and widget practices. It provides conventions and examples for organizing and writing Flutter code.
Key features
- 01Dart 3.x pattern matching and records
- 02File and identifier naming conventions
- 03Layer-first and feature-first layouts
- 04Null-safety guidance
- 05Async/await error handling
- 06Widget construction practices
Use cases
- 01Choosing a Flutter project layout
- 02Applying consistent Dart naming
- 03Reviewing asynchronous code
- 04Improving widget implementations
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ kaakati/ rails-enterprise-dev --skill flutter-conventions-&-best-practicesAfter installing, type/flutter-conventions-&-best-practicesin Claude Code to confirm it loaded.
Details
From the project READMEFlutter Conventions & Best Practices
Dart 3.x Features
Pattern Matching
String describeUser(User user) {
return switch (user) {
User(role: 'admin', isActive: true) => 'Active administrator',
User(role: 'user', isActive: true) => 'Active user',
User(isActive: false) => 'Inactive account',
_ => 'Unknown status',
};
}
Records
(int, String) getUserInfo() => (123, 'John Doe');
final (id, name) = getUserInfo();
Sealed Classes
sealed class Result<T> {}
class Success<T> extends Result<T> {
final T data;
Success(this.data);
}
class Error<T> extends Result<T> {
final String message;
Error(this.message);
}
File Naming Conventions
- Files:
snake_case.dart - Classes:
PascalCase - Variables/Functions:
camelCase - Constants:
lowerCamelCaseorSCREAMING_SNAKE_CASEfor compile-time constants
// user_controller.dart
class UserController extends GetxController {
static const int maxRetries = 3;
static const String BASE_URL = 'https://api.example.com';
final userName = 'John'.obs;
void fetchUserData() {
// ...
}
}
Directory Organization
Layer-first (recommended for Clean Architecture):
lib/
├── domain/
├── data/
└── presentation/
Feature-first (alternative):
lib/
└── features/
├── auth/
│ ├── domain/
│ ├── data/
│ └── presentation/
└── profile/
Null Safety
// Use late for non-nullable fields initialized later
class MyController extends GetxController {
late final UserRepository repository;
@override
void onInit() {
super.onInit();
repository = Get.find();
}
}
// Use ? for nullable types
String? userName;
// Use ! only when absolutely certain
final name = userName!; // Use sparingly
// Prefer ?? for defaults
final displayName = userName ?? 'Guest';
Async/Await Best Practices
// Use async/await for asynchronous operations
Future<User> fetchUser(String id) async {
try {
final response = await client.get(Uri.parse('/users/$id'));
return User.fromJson(jsonDecode(response.body));
} on SocketException {
throw NetworkException();
} catch (e) {
throw UnknownException(e.toString());
}
}
// Use Future.wait for parallel operations
Future<void> loadAllData() async {
final results = await Future.wait([
fetchUsers(),
fetchSettings(),
fetchPreferences(),
]);
}
// Use unawaited for fire-and-forget
unawaited(analytics.logEvent('page_view'));
Code Organization Within Files
class MyClass {
// 1. Constants
static const int maxRetries = 3;
// 2. Static fields
static final instance = MyClass._();
// 3. Instance fields
final String id;
final _isLoading = false.obs;
// 4. Constructors
MyClass(this.id);
MyClass._();
// 5. Getters/Setters
bool get isLoading => _isLoading.value;
// 6. Lifecycle methods
@override
void onInit() {}
// 7. Public methods
void publicMethod() {}
// 8. Private methods
void _privateMethod() {}
}
Widget Best Practices
// Prefer const constructors
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('Hello');
}
}
// Extract widgets for reusability
class UserCard extends StatelessWidget {
final User user;
const UserCard({Key? key, required this.user}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: _buildContent(),
);
}
Widget _buildContent() {
return Column(
children: [
Text(user.name),
Text(user.email),
],
);
}
}
Comments
Markdown supported