add more guidelines to DEVELOPMENT.md

This commit is contained in:
Henry Hiles 2026-10-14 17:04:05 -04:00
commit 7353955d4e
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -27,10 +27,8 @@ ref.read(MyController.provider.notifier).helperMethod()
We use an object oriented style for controllers, where `provider` is a static member on the controller class. E.g. We use an object oriented style for controllers, where `provider` is a static member on the controller class. E.g.
```dart ```dart
class MyController extends AsyncNotifier<DataThisControllerExposes> { class MyController(final SomeInputType input)
final SomeInputType input; extends AsyncNotifier<DataThisControllerExposes> {
MyController(this.input);
@override @override
Future<DataThisControllerExposes> build() async { Future<DataThisControllerExposes> build() async {
return input.foo; return input.foo;
@ -38,11 +36,17 @@ class MyController extends AsyncNotifier<DataThisControllerExposes> {
static final provider = static final provider =
AsyncNotifierProvider.family<MyController, DataThisControllerExposes, SomeInputType>( AsyncNotifierProvider.family<MyController, DataThisControllerExposes, SomeInputType>(
AuthorController.new, MyController.new,
); );
} }
``` ```
#### `watch` vs `read`
Only use `ref.watch` in a `build` method, where you want to rebuild when the value changes. Everywhere else, like callbacks, `useEffect`, and methods on a controller, use `ref.read`.
Use `.select` to narrow down what you depend on, e.g. `ref.watch(RoomsController.provider.select((rooms) => rooms[roomId]))`.
Providers which are not controllers, e.g. they expose no data, only methods, should instead live in `lib/helpers/`. For an example, see `lib/helpers/launch_helper.dart`. Other, non-provider helpers, like extensions or helper methods can also go in `lib/helpers/`. Providers which are not controllers, e.g. they expose no data, only methods, should instead live in `lib/helpers/`. For an example, see `lib/helpers/launch_helper.dart`. Other, non-provider helpers, like extensions or helper methods can also go in `lib/helpers/`.
### Don't use StatefulWidgets ([Flutter Hooks](https://pub.dev/packages/flutter_hooks)) ### Don't use StatefulWidgets ([Flutter Hooks](https://pub.dev/packages/flutter_hooks))
@ -63,6 +67,34 @@ When possible, we prefer not to create global variables or methods. You can usua
I'm also not a big fan of private methods or variables, but this is contextual. I'm also not a big fan of private methods or variables, but this is contextual.
### Modern Dart syntax
We try to use newer Dart language features where they make code shorter:
- Primary constructors for classes, including widgets, e.g. `class const MemberList(final String roomId, {super.key}) extends HookConsumerWidget`.
- Dot shorthands wherever the type can be inferred, e.g. `.new(...)`, `padding: .all(8)`, `relationType == .reply`.
- Patterns and switch expressions over null checks and casts, e.g. `if (event.content case MessageContent(:final body?))` rather than checking `is` then using `!`.
### Prefer `map` over inline `for` loops
When building a list (or set, or map) from another collection, use `.map`, `.expand` and friends rather than a `for` inside the collection literal:
```dart
// Prefer
children: rooms.map((room) => RoomTile(room)).toList(),
// Over
children: [for (final room in rooms) RoomTile(room)],
```
Regular `for` statements are fine for logic that doesn't just build a collection.
### Async values and errors
When rendering an `AsyncValue`, use the `betterWhen` extension, which shows our standard loading and error widgets, or a switch expression if you need more control (e.g. `AsyncData(:final value) || AsyncLoading(:final value?)` to keep showing old data while reloading).
For futures started from the UI that nothing awaits, like button presses, add `.onError(showError)` so failures are shown to the user instead of being lost.
## LLM/AI Assisted Contributions ## LLM/AI Assisted Contributions
LLM generated code is NOT allowed. All contributions should be written by humans. Using LLMs for interacting with others, e.g. for Comments, PRs, etc, is also not allowed. LLM generated code is NOT allowed. All contributions should be written by humans. Using LLMs for interacting with others, e.g. for Comments, PRs, etc, is also not allowed.