← Dart EnglishChapter 13 of 13

Best Practices

## Learning Objectives - Follow Dart style guide - Write clean, maintainable code - Master effective patterns - Learn Flutter-specific practices ## Naming Conventions ### Variables and Functions ```dart // Variables: camelCase var userName = 'Alice'; var isActive = true; var itemCount = 42; // Functions: camelCase void fetchUserData() {} int calculateTotal() {} // Private members: leading underscore class User { String _privateField = ''; void _privateMethod() {} } ``` ### Classes and Types ```dart // Classes: PascalCase class UserAccount {} class HttpClient {} class ApiResponse {} // Enums: PascalCase enum Status { pending, active, completed } enum Color { red, green, blue } // Type aliases: PascalCase typedef UserId = String; typedef Callback = void Function(); ``` ### Constants ```dart // Compile-time constants: PascalCase const int maxItems = 100; const String appName = 'MyApp'; // Enum values: camelCase (but keep enum PascalCase) enum Status { isPending, isActive, isCompleted, } ``` ### Files ```dart // lowercase_with_underscores.dart // user_repository.dart // api_client.dart // constants.dart ``` ## Code Organization ### File Structure ```dart // person.dart class Person { String name; int age; Person(this.name, this.age); void greet() => print('Hello, I am $name'); } ``` ### Import Organization ```dart // Order: SDK, package, relative import 'dart:async'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; import '../models/user.dart'; import 'constants.dart'; ``` ## Variables ### Prefer var for Local Variables ```dart // Good: type is clear from right side var name = 'Alice'; var count = 0; var items = ['a', 'b', 'c']; // Acceptable: explicit when type isn't obvious var points = []; var mapping = {}; ``` ### Final When Possible ```dart // Use final for values that don't change final name = 'Alice'; final items = ['a', 'b', 'c']; // Use const for compile-time constants const maxRetries = 3; const defaultTimeout = Duration(seconds: 30); ``` ### Avoid var for Public Fields ```dart // Good: explicit type for public API class User { final String name; final int age; User(this.name, this.age); } // Acceptable: var for local variables with clear context var result = computeResult(); ``` ## Functions ### Prefer Arrow Functions for Short Functions ```dart // Good int square(int x) => x * x; String capitalize(String s) => s.isEmpty ? '' : s[0].toUpperCase() + s.substring(1); // Avoid for complex logic void process() { // Complex logic here // Multiple statements // Don't use arrow for multi-line } ``` ### Use Void for Side Effects Only ```dart // Good: returns value Future fetchUser(String id) async { ... } // Good: void for side effects only Future saveUser(User user) async { ... } // Avoid: returning value but declared void // void process() => computeResult(); // Confusing ``` ## Classes ### Prefer Constructors with Initializer List ```dart class Point { final double x; final double y; Point(this.x, this.y); // Good // Avoid: unnecessary named parameters for simple cases // Point({required this.x, required this.y}); // Overkill here } ``` ### Use Named Constructors for Clarity ```dart class Date { int year; int month; int day; Date(this.year, this.month, this.day); // Named constructors for clarity Date.today() : this(DateTime.now().year, DateTime.now().month, DateTime.now().day); Date.fromJson(Map json) : this(json['year'], json['month'], json['day']); } ``` ### Private Fields for Encapsulation ```dart class BankAccount { String _accountNumber; // Private double _balance; // Private BankAccount(this._accountNumber, this._balance); double get balance => _balance; // Read-only access void deposit(double amount) { if (amount > 0) _balance += amount; } } ``` ## Null Safety ### Avoid Force Unwrap (!) ```dart // Bad String name = getName()!; // Good: handle null explicitly String? name = getName(); String displayName = name ?? 'Unknown'; ``` ### Prefer Late Sparingly ```dart // Good: when lazy initialization is needed class DataService { late String data; // Defer initialization Future load() async { data = await fetchData(); } } // Better: if possible, initialize in constructor class User { final String name; User(this.name); } ``` ## Collections ### Prefer Collection Literals ```dart // Good var list = [1, 2, 3]; var map = {'a': 1, 'b': 2}; var set = {1, 2, 3}; // Avoid: List<>() constructor // var list = List.of([1, 2, 3]); // Verbose ``` ### Use Spread Operators ```dart var base = [1, 2, 3]; var combined = [0, ...base, 4]; // [0, 1, 2, 3, 4] var defaults = {'a': 1, 'b': 2}; var overrides = {'b': 3, 'c': 4}; var config = {...defaults, ...overrides}; ``` ### Collection Conditionals ```dart var isPremium = true; var items = [ 'Home', if (isPremium) 'Premium Feature', 'About', ]; var filtered = [1, 2, 3, 4, 5] .where((n) => n % 2 == 0) .map((n) => n * 2) .toList(); ``` ## Async Programming ### async/await Over Futures ```dart // Good: clear async flow Future loadData() async { try { var user = await fetchUser(); var posts = await fetchPosts(user.id); print('Posts: $posts'); } catch (e) { print('Error: $e'); } } // Avoid: excessive then chaining // fetchUser().then((user) => fetchPosts(user.id)).then(...); ``` ### Cancel Unused Futures ```dart class MyWidget extends StatefulWidget { @override State createState() => _MyWidgetState(); } class _MyWidgetState extends State { Future? _future; @override void initState() { super.initState(); _future = loadData(); } @override void dispose() { _future = null; // Help garbage collection super.dispose(); } } ``` ## Error Handling ### Specific Exceptions ```dart try { await fetchData(); } on NetworkException { // Handle network errors showNetworkError(); } on ServerException catch (e) { // Handle server errors with context showServerError(e.message); } catch (e, stack) { // Unexpected errors reportError(e, stack); } ``` ### Result Pattern ```dart class Result { final T? data; final String? error; Result.success(this.data) : error = null; Result.failure(this.error) : data = null; bool get isSuccess => data != null; } Future> fetchUser() async { try { var user = await _api.getUser(); return Result.success(user); } catch (e) { return Result.failure(e.toString()); } } ``` ## Flutter-Specific ### StatelessWidget vs StatefulWidget ```dart // Use StatelessWidget when possible class Counter extends StatelessWidget { final int count; const Counter({required this.count}); @override Widget build(BuildContext context) { return Text('Count: $count'); } } ``` ### Const Widgets ```dart // Good: const when possible return const Text('Hello'); // Good: const constructor class MyWidget extends StatelessWidget { const MyWidget({super.key}); @override Widget build(BuildContext context) { return const Text('Hello'); } } ``` ### Avoid setState When Possible ```dart // Good: derive from existing state Text('Count: $props.count'); // Consider: Riverpod, Provider, Bloc for complex state ``` ## Documentation ### Use Doc Comments ```dart /// Calculates the sum of two numbers. /// /// [a] The first number /// [b] The second number /// /// Returns the sum of a and b int sum(int a, int b) => a + b; ``` ## Summary - Follow naming: camelCase (vars), PascalCase (types) - Use final for immutable, const for compile-time - Prefer var for local variables with clear types - Use const constructors in Flutter - Handle null safely: prefer `??` over `!` - Use async/await over Future.then - Be specific with exceptions - Document public APIs with doc comments - Keep files short and focused

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →