Updates to style guide (#181985)
Companion PR: https://github.com/flutter/flutter/pull/181934
Design doc: [Flutter Style
Updates](https://docs.google.com/document/d/1Ao6RZmI4F8VSCpN-89o0Y6bZXhYb1-qgVGV5tALxm48/edit?tab=t.0)
Closes https://github.com/flutter/flutter/issues/180607
Also closing https://github.com/flutter/flutter/pull/172296
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
If you need help, consider asking for advice on the #hackers-new channel
on [Discord].
**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.
<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
diff --git a/docs/contributing/Style-guide-for-Flutter-repo.md b/docs/contributing/Style-guide-for-Flutter-repo.md
index 4ee6fb4..1067d5d 100644
--- a/docs/contributing/Style-guide-for-Flutter-repo.md
+++ b/docs/contributing/Style-guide-for-Flutter-repo.md
@@ -1350,11 +1350,11 @@
Embrace code duplication in tests. It makes it easier to make new tests by copying and pasting them and
tweaking a few things.
-Avoid using `setUp`, `tearDown`, and similar features, as well as test-global variables or other state
-shared between tests. They make writing tests easier but make maintaining them, debugging them, and
-refactoring code much harder. (These are commonly used in Flutter's codebase today, but that is almost
-always a mistake. When you are editing a file that uses those features, aim to reduce the number of
-tests using them while you're there.)
+Avoid test-global variables or state shared between tests; these make maintaining, debugging, and refactoring
+significantly harder. Instead of `setUp`, use local helper functions called within each test block to
+initialize state. To handle cleanup, prefer `addTearDown` over the global `tearDown` callback. `addTearDown`
+allows you to register cleanup logic at the exact point where a resource is created, ensuring it only runs
+if the initialization was successful.
Specifically, we are trying to avoid shared state, which could persist across tests, and non-local
side-effects, which would prevent being able to move a test to another file without breaking the test.
@@ -1364,17 +1364,27 @@
### Prefer more test files, avoid long test files
-Avoid adding tests to files that already have more than one or two hundred lines of code. It's easier
+Organize tests into smaller files grouped by feature, widget, or behavior. It's easier
to understand a test file when it has only a few related tests, rather than when it has an entire test
suite. (It also makes developing the tests faster because you can run the test file faster.)
+Instead of keeping everything in a single file like:
-### Avoid using `pumpAndSettle`
+- `button_test.dart`
-As per the API docs for [pumpAndSettle](https://main-api.flutter.dev/flutter/flutter_test/WidgetController/pumpAndSettle.html), prefer using explicit [`pump`](https://main-api.flutter.dev/flutter/flutter_test/WidgetController/pump.html) calls rather than `pumpAndSettle`.
+that includes layout, semantics, and interaction tests, it can be split into:
-Using `pumpAndSettle`, especially without checking its return value, makes it very easy for bugs to sneak in where we trigger animations across multiple frames instead of immediately. It is almost always the case that a call to `pumpAndSettle` is more strictly correctly written as two `pump` calls, one to trigger the animations and one (with a duration) to jump to the point after the animations.
+- `button_layout_test.dart`
+- `button_semantics_test.dart`
+- `button_interaction_test.dart`
+Or group tests by behavior:
+
+- `navigator_push_test.dart`
+- `navigator_pop_test.dart`
+- `navigator_transition_test.dart`
+
+For more on writing tests, see [Running & writing tests](./testing/Running-and-writing-tests.md).
## Naming
@@ -1396,18 +1406,6 @@
when selecting a name for an identifier. In general, avoid one-character names unless one character is idiomatic
(for example, prefer `index` over `i`, but prefer `x` over `horizontalPosition`).
-
-### Avoid anonymous parameter names
-
-Provide full type information and names even for parameters that are otherwise unused. This makes it easier for
-people reading the code to tell what is actually going on (e.g. what is being ignored). For example:
-
-```dart
- onTapDown: (TapDownDetails details) { print('hello!'); }, // GOOD
- onTapUp: (_) { print('good bye'); }, // BAD
-```
-
-
### Naming rules for typedefs and function variables
When naming callbacks, use `FooCallback` for the typedef, `onFoo` for
@@ -1633,121 +1631,10 @@
Line length for code is automatically handled by `dart format`, which is configured to use a maximum
line length of 100.
-
-### Consider using `=>` for short functions and methods
-
-But only use `=>` when everything, including the function declaration, fits
-on a single line.
-
-Example:
-
-```dart
-// BAD:
-String capitalize(String s) =>
- '${s[0].toUpperCase()}${s.substring(1)}';
-
-// GOOD:
-String capitalize(String s) => '${s[0].toUpperCase()}${s.substring(1)}';
-
-String capitalize(String s) {
- return '${s[0].toUpperCase()}${s.substring(1)}';
-}
-```
-
-### Use `=>` for getters and callbacks that just return literals or switch expressions
-
-```dart
-// GOOD:
-List<Color> get favorites => <Color>[
- const Color(0xFF80FFFF),
- const Color(0xFF00FFF0),
- const Color(0xFF4000FF),
- _mysteryColor(),
-];
-
-// GOOD:
-bool get isForwardOrCompleted => switch (status) {
- AnimationStatus.forward || AnimationStatus.completed => true,
- AnimationStatus.reverse || AnimationStatus.dismissed => false,
-};
-```
-
-It's important to use discretion, since there are cases where a function body
-is easier to visually parse:
-
-```dart
-// OKAY, but the code is more dense than it could be:
-String? get validated => switch(input[_inputIndex]?.trim()) {
- final String value when value.isNotEmpty => value,
- _ => null,
-}
-
-// BETTER (more verbose, but also more readable):
-String? get validated {
- final String? value = input[_inputIndex]?.trim();
-
- if (value != null && value.isNotEmpty) {
- return value;
- }
- return null;
-}
-```
-
-If your code is passing an inline closure containing only a `return` statement,
-you can instead use the `=>` form.\
-When doing this, the closing `]`, `}`, or `)` bracket will have the same
-indentation as the line where the callback starts.
-
-For example:
-
-```dart
- // GOOD, but slightly more verbose than necessary since it doesn't use =>
- @override
- Widget build(BuildContext context) {
- return PopupMenuButton<String>(
- onSelected: (String value) { print('Selected: $value'); },
- itemBuilder: (BuildContext context) {
- return <PopupMenuItem<String>>[
- PopupMenuItem<String>(
- value: 'Friends',
- child: MenuItemWithIcon(Icons.people, 'Friends', '5 new'),
- ),
- PopupMenuItem<String>(
- value: 'Events',
- child: MenuItemWithIcon(Icons.event, 'Events', '12 upcoming'),
- ),
- ];
- }
- );
- }
-
- // GOOD, does use =>, slightly briefer
- @override
- Widget build(BuildContext context) {
- return PopupMenuButton<String>(
- onSelected: (String value) { print('Selected: $value'); },
- itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
- PopupMenuItem<String>(
- value: 'Friends',
- child: MenuItemWithIcon(Icons.people, 'Friends', '5 new'),
- ),
- PopupMenuItem<String>(
- value: 'Events',
- child: MenuItemWithIcon(Icons.event, 'Events', '12 upcoming'),
- ),
- ]
- );
- }
-```
-
-The important part is that the closing punctuation lines up with the start
-of the line that has the opening punctuation, so that you can easily determine
-what's going on by just scanning the indentation on the left edge.
-
-
### Use braces for long functions and methods
-Use a block (with braces) when a body would wrap onto more than one line (as opposed to using `=>`; the cases where you can use `=>` are discussed in the previous two guidelines).
+Use a block (with braces) when a body would wrap onto more than one line (as opposed to using arrow
+syntax, `=>`).
### Prefer `+=` over `++`