Best Practices
Dart Best Practices
Section titled “Dart Best Practices”Null Safety
Section titled “Null Safety”- Always enable null safety (
dart migrate). - Avoid usage of
dynamicandObjectdeclaration, always usevaror explicit typing. - Follow the order of
const>>final>>varwith no nullability specifier >>final late>>var?. - Avoid non const top-level variables.
- Favor immutable data classes (with
freezedorequatable).
Typing
Section titled “Typing”- Avoid
dynamicwhere possible; use explicit types (List<int>instead ofList).
Concurrency
Section titled “Concurrency”- Prefer
async/awaitover.then()for readability. - Use
Future.errorfor explicit errors instead of throwing strings. - Leverage
FutureOr<T>for flexible async/sync returns. - Offload CPU-intensive tasks to isolates (e.g., image processing).
- Use
compute()for simple parallelism orIsolate.spawnfor complex cases.
Collections
Section titled “Collections”- Use collection
if/for/spreadsfor concise code. - Prefer
.map()``.where()And.fold()over manual loops where appropriate.
Error Handling
Section titled “Error Handling”- Catch specific exceptions (
on SomeException), not just allcatch (e). - Use
rethrowwhen needed to preserve stack traces.
Flutter Best Practices
Section titled “Flutter Best Practices”State Management
Section titled “State Management”- Prefer Provider, Riverpod, Bloc, or GetX over
setState - Avoid global state unless necessary (e.g., use scoped providers).
- Follow BLoC/Cubit or MVVM patterns.
- Keep UI (Widgets) and business logic (Models/Controllers) separate.
- Reactive Programming:
- Use
StreamBuilder/FutureBuilderfor async UI updates. - Avoid nested reactive widgets (e.g., minimize
StreamBuilderlayers). - State Persistence:
- Use
hydrated_blocorshared_preferencesfor local state persistence. - Avoid prop drilling with
ProviderorRiverpod. - Use
freezedfor immutable models and unions. - Serialize JSON with
json_serializable. - Use
MethodChannelfor native integrations (Kotlin/Swift). - Organize layers into
data``domainAndpresentation.
Widget Optimizations
Section titled “Widget Optimizations”- Mark widgets as
constwhen possible to prevent unnecessary rebuilds. - Use
constconstructors for children in lists/grids. - Avoid
Opacityfor animations; preferAnimatedOpacityorTransform. - Use
ListView.builder(orCustomScrollView) for infinite/large lists (lazy loading). - Extract expensive computations from
build()methods. - Use
KeyS (e.g.,ValueKey``UniqueKey) when modifying collections of stateful widgets. - Use
precacheImagefor images loaded on-demand.
UI/UX Considerations
Section titled “UI/UX Considerations”- Use
LayoutBuilder``MediaQueryOrSafeArea. - Test on multiple screen sizes (e.g., using
DevicePreview). - Add semantic labels (
Semanticswidget), useExcludeSemanticswhere needed. - Support dynamic text sizing (
TextScaler). - Use
intlpackage with ARB files orflutter_localizations. - Define a consistent
ThemeDatainMaterialApp. - Use
ThemeExtensionsfor custom theming (Flutter 3+). - Use named routes with
go_routerfor deep linking and simplified navigation.
Performance
Section titled “Performance”- Test performance in profile mode (
flutter run --profile). - Use DevTools to check for jank, memory leaks, and CPU usage.
- Dispose controllers (
ScrollController``TextEditingController) and subscriptions. - Use
constwidgets to reduce garbage collection. - Minimize
ClipPath``OpacityAndShaderMaskusages in animations. - Prefer
CustomPaintfor complex UIs over deep widget trees. - Compress images (use
.webpformat). - Cache images with
cached_network_image.
Testing
Section titled “Testing”- Unit Test with
mockitoormocktailfor mocking. - Use
WidgetTesterto verify UI behavior (e.g.,pumpAndSettle). - Run end-to-end tests with
integration_test. - Use
golden_toolkitfor pixel-perfect UI comparisons. - Generate reports with
flutter test --coverage+lcov.
Deployment & Maintenance
Section titled “Deployment & Maintenance”- Use
flutter analyzeanddart fix. - Configure
analysis_options.yamlwith strict lints (flutter_lintspackage). - Pin versions in
pubspec.yaml(^for SemVer-compatible updates). - Audit dependencies with
dart pub outdated. - Use doc comments (
///) and generate docs viadart doc. - Keep
README.mdupdated with project setup and architecture. - Use
sentry_flutterfor error tracking.
Platform specific
Section titled “Platform specific”- ndk version mismatch
- Set
ndkVersionin build.gradle.kts to the specific ndk version installed
Intuition
Section titled “Intuition”Writing clean code: Best practices are like good manners — they make your code easier for others (and future you) to read, understand, and maintain.
Why it matters: Clean code is easier to debug, test, and extend. It reduces bugs and improves team productivity.
The key insight: Code is read much more often than it’s written — optimize for readability, not cleverness.
Common Pitfalls
Section titled “Common Pitfalls”Neglecting to normalise database designs, leading to data redundancy and update anomalies.
Misunderstanding the difference between a stack (LIFO) and a queue (FIFO) in data structure applications.
Writing pseudocode that is too language-specific rather than using standard algorithmic constructs.
Forgetting that average-case for quicksort becomes worst-case on already sorted input.
Summary
Section titled “Summary”The key principles covered in this topic are linked in the sub-pages above. Focus on understanding the definitions, applying the formulas or frameworks, and evaluating strengths and limitations of each approach.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.
Cross-References
Section titled “Cross-References”- Error Handling: Detailed exception hierarchy and try-catch patterns referenced in best practices.
- Classes and Inheritance: Object-oriented design patterns for immutable data classes.
- Async and Futures: Concurrency best practices including async/await and isolate usage.
- Class Modifiers: Dart 3 modifier patterns for API boundary enforcement.