Pascal Welsch
Follow3 followers81 principles
Follow this expert and all of their principles run in your agent, along with everything they publish later.
principles
Avoid the late keyword, because it turns a compile time check into a runtime LateInitializationError; initialize in the constructor or use a nullable type.
Keep model objects immutable and hold changing state, such as which items are checked, separately, because mutating a field may not trigger setState and leaves the UI stale.
Test a page by launching the app and navigating to it, not by wrapping it in its own MaterialApp, so routes, providers and results are tested for real.
Prefer switch, polymorphism, collection functions or early returns over if-else, and keep if-else for two equally weighted branches.
Log through package:chirp, or developer.log in a Flutter app without chirp, and never print(), because print loses the error and stack trace.
Keep ternaries on one line; when a condition spans lines, use an immediately invoked function or if-else so each branch stays readable and debuggable.
Replace if-else and multi-line or nested ternaries in build methods with a Builder that uses early returns, so each conditional state is easy to read.
Give reusable widgets Widget parameters with a DefaultTextStyle instead of String parameters for user-facing text, so callers can pass icons, rich text or their own style.
Keep reactive state in signals scoped to a controller, preferably state_beacon, instead of ChangeNotifier or ValueNotifier, so derived values cannot go stale.
In Flutter mobile apps, tapping the background outside a text field must dismiss the keyboard by removing focus; check this for every field you add.
Extract UI sections into public widget classes instead of Widget _buildX() methods, so tests can find them by type and the tree stays easy to query.
When one condition picks between UI variants, branch once and build one complete widget per variant instead of repeating the condition on every property.
Configure Dart linting with package:lint and never suggest other lint packages such as very_good_analysis or flutter_lints.
In widgets and other classes with dispose(), delay work with a Timer you cancel in dispose(), because Future.delayed cannot be cancelled.
Give the main widget of every widget file a dartdoc comment that says where it is used and what makes it unique, not a restatement of its name.
Use package:provider with context.read and context.watch for state management, instead of riverpod or get_it.
Put the final casing of user-visible text in the localization resource instead of calling toUpperCase() on it, because Dart's casing is wrong for some languages.
Handle Flutter forms with package:reactive_forms instead of Form, TextFormField and TextEditingController, so form state and validation live in one model.
Organize code by feature and call repositories directly, because UseCase and layer-first folders add indirection and boilerplate without value.
Use package:context_plus to bind objects to widget scopes and watch them, instead of provider, riverpod, get_it or hand-written InheritedWidgets.
Keep a StatefulWidget and its State class next to each other, with no function, variable or other class declared between them.
Name a widget that a route pushes *Page in *_page.dart, never *Screen, because Flutter already calls a route's configuration a Page and a screen is hardware.
Do not use package:bloc or flutter_bloc for state management; streams are hard to test, and a synchronous ChangeNotifier is simpler for most code.
Never send analytics events from a Flutter build() method, directly or through a helper, because rebuilds are not user events and produce duplicate counts.