Best Practices
Code Style
Naming Conventions
dart
// Classes: UpperCamelCase
class UserProfile {}
// Variables, functions: lowerCamelCase
String userName = 'Alice';
void calculateTotal() {}
// Constants: lowerCamelCase
const maxRetries = 3;
const apiEndpoint = 'https://api.example.com';
// Private members: _leadingUnderscore
class BankAccount {
double _balance;
}
// Libraries: lowercase_with_underscores
import 'package:my_app/user_service.dart';Use Type Annotations
dart
// Good
String getName() => 'Alice';
List<int> getNumbers() => [1, 2, 3];
// Avoid
getName() => 'Alice'; // Type unclearPrefer final over var
dart
// Good
final name = 'Alice';
final numbers = [1, 2, 3];
// Less preferred
var name = 'Alice';Null Safety
dart
// Always handle nulls safely
String? getName() => null;
void main() {
// Good
String name = getName() ?? 'Guest';
// Good
String? maybeName = getName();
if (maybeName != null) {
print(maybeName.toUpperCase());
}
// Avoid
// String name = getName()!; // Risky!
}Functions
Keep Functions Small
dart
// Good - single responsibility
bool isValidEmail(String email) {
return email.contains('@') && email.contains('.');
}
bool isAdult(int age) {
return age >= 18;
}
// Avoid - doing too much
void processUser(Map user) {
// 100 lines of code...
}Use Named Parameters
dart
// Good
void createUser({
required String name,
required String email,
int age = 0,
}) {}
// Less clear
void createUser(String name, String email, int age) {}Collections
Use Collection Literals
dart
// Good
var list = <int>[];
var map = <String, int>{};
var set = <String>{};
// Avoid
var list = List<int>();
var map = Map<String, int>();Use Collection If/For
dart
// Good
var numbers = [
1,
2,
if (includeThree) 3,
for (var i in extras) i,
];
// Avoid
var numbers = [1, 2];
if (includeThree) numbers.add(3);
for (var i in extras) numbers.add(i);Classes
Use Initializing Formals
dart
// Good
class Point {
final double x, y;
Point(this.x, this.y);
}
// Avoid
class Point {
final double x, y;
Point(double x, double y) {
this.x = x;
this.y = y;
}
}Prefer Composition Over Inheritance
dart
// Good
class Car {
final Engine engine;
Car(this.engine);
}
// Less flexible
class Car extends Engine {}Async Programming
Always Use async/await
dart
// Good
Future<String> fetchData() async {
var response = await http.get(url);
return response.body;
}
// Avoid
Future<String> fetchData() {
return http.get(url).then((response) {
return response.body;
});
}Handle Errors
dart
Future<void> loadData() async {
try {
var data = await fetchData();
processData(data);
} catch (e) {
print('Error: $e');
}
}Performance
Use const Constructors
dart
// Good - created once
const icon = Icon(Icons.home);
// Less efficient - created every time
final icon = Icon(Icons.home);Avoid Unnecessary Rebuilds
dart
// Good - extract to const
class MyWidget extends StatelessWidget {
static const title = Text('Hello');
@override
Widget build(BuildContext context) {
return title;
}
}Documentation
Document Public APIs
dart
/// Calculates the area of a circle.
///
/// The [radius] must be positive.
/// Returns the area as a double.
double calculateCircleArea(double radius) {
assert(radius > 0, 'Radius must be positive');
return 3.14159 * radius * radius;
}Testing
Write Tests
dart
import 'package:test/test.dart';
void main() {
test('addition works', () {
expect(2 + 2, equals(4));
});
test('string contains substring', () {
expect('Hello, World', contains('World'));
});
}Summary
- Follow naming conventions
- Use type annotations for clarity
- Embrace null safety
- Keep functions small and focused
- Use named parameters for clarity
- Prefer
constandfinal - Handle errors properly
- Document public APIs
- Write tests
- Use linter (analysis_options.yaml)