From c5236a99adafdbdbc482a6b22d6f0cd1e7ffd341 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 8 Oct 2026 11:41:44 -0700 Subject: [PATCH 1/9] Adds enum styleguide changes rfc --- rfc/710.0000-style-guide-enums.md | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 rfc/710.0000-style-guide-enums.md diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md new file mode 100644 index 0000000..08505c2 --- /dev/null +++ b/rfc/710.0000-style-guide-enums.md @@ -0,0 +1,55 @@ +--- +type: rfc +rfc: '710.0000' +title: Style guide changes for enums +description: Updates the Flutter style guide and breaking change policy for enums and enum-like classes. +status: draft +created: 2026-10-08T00:00:00Z +updated: 2026-10-08T16:48:00Z +tags: + - 710-style-guide + - 030-breaking-changes +authors: + - https://github.com/chunhtai +--- + +# RFC 710.0000: Style guide changes for enums + +## Overview + +This document proposes updating the [Flutter repository style guide](https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md) and [Flutter breaking change policy](https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes) to establish conventions for public enums. + +For public APIs, a standard `enum` is used when consumers must handle all values exhaustively (making new values a breaking change), and a class with `static const` fields is used when consumers need a fallback (making new values non-breaking). + +## Context + +Inside the Flutter codebase, requiring exhaustive `switch` statements and expressions over `enum` values (without `default` branches) is intentional and remains unchanged. However, the style guide lacks guidelines for designing enum-like types in public APIs, and the breaking change policy does not explicitly classify adding a value to a public `enum` as a breaking change. + +Because external callers also switch exhaustively over public `enum` types, adding a new value breaks downstream code. Without criteria for choosing between a standard `enum` and a class with `static const` fields, public APIs in the framework are inconsistent: + +* `TargetPlatform` and `AppLifecycleState` are `enum` declarations even though consumers typically handle only specific values with a fallback. Adding new values breaks callers. +* `TextInputType` and `LogicalKeyboardKey` use classes with `static const` fields to allow non-breaking additions, but the style guide does not document when to use this pattern instead of an `enum`. + +## Proposal + +### Non-public enums + +Library-private (`_`) and `@internal` enums must use a standard `enum` declaration so framework `switch` statements and expressions are exhaustively checked. + +### Public APIs + +Choose between a standard `enum` and an enum-like class based on whether consumers must handle every value exhaustively, rather than whether new values might be added later: + +#### Standard `enum` (exhaustive) + +Use a standard `enum` when consumers are expected to handle every case without a `default` or `_` branch (such as `Axis`, `AxisDirection`, `VerticalDirection`, and `GrowthDirection`). + +* Provides compile-time exhaustiveness checking. +* Adding a value to a public `enum` is a breaking change and requires to go through the breaking change process. + +#### Enum-like class with `static const` values (non-exhaustive) + +Use a `final` class with a private `const` constructor and `static const` fields when consumers handle a subset of values with a `default` or `_` fallback (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). + +* Adding a `static const` field is not a breaking change. +* Does not provide compile-time exhaustiveness checking. From 7cf86b1f1de9c755d5b8eab939e7bdb16fc68da5 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 8 Oct 2026 14:18:33 -0700 Subject: [PATCH 2/9] update --- rfc/710.0000-style-guide-enums.md | 83 ++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index 08505c2..684c676 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -5,7 +5,7 @@ title: Style guide changes for enums description: Updates the Flutter style guide and breaking change policy for enums and enum-like classes. status: draft created: 2026-10-08T00:00:00Z -updated: 2026-10-08T16:48:00Z +updated: 2026-10-08T18:55:00Z tags: - 710-style-guide - 030-breaking-changes @@ -17,18 +17,20 @@ authors: ## Overview -This document proposes updating the [Flutter repository style guide](https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md) and [Flutter breaking change policy](https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes) to establish conventions for public enums. +This document proposes updating the [Flutter repository style guide](https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md) and [Flutter breaking change policy](https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes) to establish conventions for public enums and enum-like classes. -For public APIs, a standard `enum` is used when consumers must handle all values exhaustively (making new values a breaking change), and a class with `static const` fields is used when consumers need a fallback (making new values non-breaking). +* For public APIs, use a standard `enum` when the set of values is closed by nature or when consumers must handle every value exhaustively; otherwise, prefer an enum-like class with `static const` fields. +* Adding a value to a public `enum` (or a new subtype to a public `sealed class`) breaks exhaustive switches and requires going through the breaking change process. +* These rules apply to new APIs; migrating existing public `enum` declarations is left to case-by-case evaluation. ## Context -Inside the Flutter codebase, requiring exhaustive `switch` statements and expressions over `enum` values (without `default` branches) is intentional and remains unchanged. However, the style guide lacks guidelines for designing enum-like types in public APIs, and the breaking change policy does not explicitly classify adding a value to a public `enum` as a breaking change. +Inside the Flutter codebase, requiring exhaustive `switch` statements and expressions over `enum` values (without `default` branches) is intentional and remains unchanged. However, when external callers or `flutter/packages` switch exhaustively over a public `enum`, adding a new value breaks downstream compilation and rolls. -Because external callers also switch exhaustively over public `enum` types, adding a new value breaks downstream code. Without criteria for choosing between a standard `enum` and a class with `static const` fields, public APIs in the framework are inconsistent: +Language-level non-exhaustive ("open") enums were requested in [dart-lang/sdk#63678](https://github.com/dart-lang/sdk/issues/63678), but the Dart team noted ([comment](https://github.com/dart-lang/sdk/issues/63678#issuecomment-5375670420)) that this feature is unlikely to be prioritized in the near future. Without a library-level convention in the style guide, public APIs in the framework are inconsistent: -* `TargetPlatform` and `AppLifecycleState` are `enum` declarations even though consumers typically handle only specific values with a fallback. Adding new values breaks callers. -* `TextInputType` and `LogicalKeyboardKey` use classes with `static const` fields to allow non-breaking additions, but the style guide does not document when to use this pattern instead of an `enum`. +* `TargetPlatform` and `AppLifecycleState` are `enum` declarations even though platforms and OS lifecycle states grow over time. Adding new values breaks callers. +* `TextInputType` and `LogicalKeyboardKey` use classes with `static const` fields to allow non-breaking additions, but the style guide does not document when or how to use this pattern. ## Proposal @@ -38,18 +40,73 @@ Library-private (`_`) and `@internal` enums must use a standard `enum` declarati ### Public APIs -Choose between a standard `enum` and an enum-like class based on whether consumers must handle every value exhaustively, rather than whether new values might be added later: +Use the following decision order when choosing between a standard `enum` and an enum-like class: + +1. If the set of values is closed by nature (such as `Axis` or `VerticalDirection`), use a standard `enum`. +2. Otherwise, if consumers of the API must handle all values exhaustively (such as layout-related enums), use a standard `enum`. +3. Otherwise, prefer an enum-like class with `static const` values. #### Standard `enum` (exhaustive) -Use a standard `enum` when consumers are expected to handle every case without a `default` or `_` branch (such as `Axis`, `AxisDirection`, `VerticalDirection`, and `GrowthDirection`). +Use a standard `enum` when the values are complete by definition within Flutter's model (such as `Axis` with `horizontal` and `vertical`, or `VerticalDirection` with `up` and `down`) or when consumers must handle every case without a fallback (such as layout-related enums like `MainAxisAlignment` or `WrapAlignment`). -* Provides compile-time exhaustiveness checking. -* Adding a value to a public `enum` is a breaking change and requires to go through the breaking change process. +* Provides compile-time exhaustiveness checking for `switch` statements and expressions. +* Adding a value to a public `enum` is a breaking change and requires going through the breaking change process. The same rule applies to adding a subtype to a public `sealed class`. #### Enum-like class with `static const` values (non-exhaustive) -Use a `final` class with a private `const` constructor and `static const` fields when consumers handle a subset of values with a `default` or `_` fallback (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). +Use an `@immutable` class with `static const` fields when the set of values is not closed and consumers do not need to handle every value exhaustively, such as platforms, OS lifecycle states, hardware keys, image formats, or input types (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). See [extensible enums in plugins](https://flutter.dev/go/extensible-enums-plugins) for prior art. * Adding a `static const` field is not a breaking change. -* Does not provide compile-time exhaustiveness checking. +* Include at least one non-private constructor. Dart's recommended [`exhaustive_cases`](https://dart.dev/tools/linter-rules/exhaustive_cases) lint enforces exhaustiveness on `switch` statements when a class has only private constructors. + * Use a `@protected const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). + * Use a public `const` constructor when values outside the predefined constants are meaningful (such as `LogicalKeyboardKey` for arbitrary hardware key IDs or `TextInputType.numberWithOptions`). +* Require a distinguishing field (such as `index` or `name`) on each instance so Dart `const` canonicalization does not collapse distinct constants into a single instance. + +##### Example: closed to external instantiation + +When only Flutter defines valid instances, annotate the constructor with `@protected`: + +```dart +@immutable +class MyEnum { + @protected + const MyEnum(this.index); + + final int index; + + static const MyEnum foo = MyEnum(0); + static const MyEnum bar = MyEnum(1); + + @override + bool operator ==(Object other) { + return other is MyEnum && other.index == index; + } + + @override + int get hashCode => index.hashCode; +} +``` + +##### Example: open to external instantiation + +When callers may construct custom instances, use a public `const` constructor: + +```dart +@immutable +class MyEnum { + const MyEnum(this.name); + + final String name; + + static const MyEnum foo = MyEnum('foo'); + static const MyEnum bar = MyEnum('bar'); + + @override + int get hashCode => name.hashCode; +} +``` + +### Scope and migration + +These conventions apply to new public APIs. Existing public `enum` declarations that represent open sets (such as `TargetPlatform` and `AppLifecycleState`) are not migrated by this proposal and should be evaluated case-by-case, such as when adding a new value already requires a breaking change. From 184759b9b35644f320e281870f042f4040dc5b44 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 8 Oct 2026 15:19:42 -0700 Subject: [PATCH 3/9] update' --- rfc/710.0000-style-guide-enums.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index 684c676..b8eda32 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -55,9 +55,10 @@ Use a standard `enum` when the values are complete by definition within Flutter' #### Enum-like class with `static const` values (non-exhaustive) -Use an `@immutable` class with `static const` fields when the set of values is not closed and consumers do not need to handle every value exhaustively, such as platforms, OS lifecycle states, hardware keys, image formats, or input types (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). See [extensible enums in plugins](https://flutter.dev/go/extensible-enums-plugins) for prior art. +Use an `@immutable` `final` class with `static const` fields when the set of values is not closed and consumers do not need to handle every value exhaustively, such as platforms, OS lifecycle states, hardware keys, image formats, or input types (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). See [extensible enums in plugins](https://flutter.dev/go/extensible-enums-plugins) for prior art. * Adding a `static const` field is not a breaking change. +* Mark the class `final` so external packages cannot `implement` or `extend` it, keeping future member additions non-breaking without triggering `switch` exhaustiveness. * Include at least one non-private constructor. Dart's recommended [`exhaustive_cases`](https://dart.dev/tools/linter-rules/exhaustive_cases) lint enforces exhaustiveness on `switch` statements when a class has only private constructors. * Use a `@protected const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). * Use a public `const` constructor when values outside the predefined constants are meaningful (such as `LogicalKeyboardKey` for arbitrary hardware key IDs or `TextInputType.numberWithOptions`). @@ -69,7 +70,7 @@ When only Flutter defines valid instances, annotate the constructor with `@prote ```dart @immutable -class MyEnum { +final class MyEnum { @protected const MyEnum(this.index); @@ -94,7 +95,7 @@ When callers may construct custom instances, use a public `const` constructor: ```dart @immutable -class MyEnum { +final class MyEnum { const MyEnum(this.name); final String name; @@ -102,6 +103,11 @@ class MyEnum { static const MyEnum foo = MyEnum('foo'); static const MyEnum bar = MyEnum('bar'); + @override + bool operator ==(Object other) { + return other is MyEnum && other.name == name; + } + @override int get hashCode => name.hashCode; } From 1c0b092beede21e8b0677241ce7c94664a14e446 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Fri, 9 Oct 2026 12:56:16 -0700 Subject: [PATCH 4/9] address comments --- rfc/710.0000-style-guide-enums.md | 54 ++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index b8eda32..9847c36 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -40,29 +40,25 @@ Library-private (`_`) and `@internal` enums must use a standard `enum` declarati ### Public APIs -Use the following decision order when choosing between a standard `enum` and an enum-like class: +Use the following decision order to choose between a standard `enum` and an enum-like class: -1. If the set of values is closed by nature (such as `Axis` or `VerticalDirection`), use a standard `enum`. -2. Otherwise, if consumers of the API must handle all values exhaustively (such as layout-related enums), use a standard `enum`. -3. Otherwise, prefer an enum-like class with `static const` values. +1. If the set of values is closed by nature (such as `Axis` with `horizontal` and `vertical`, `VerticalDirection` with `up` and `down`, or `GrowthDirection`), use a standard `enum`. +2. Otherwise, if consumers receiving the value from a callback, builder, or state object must handle every case because no safe fallback exists (such as `ConnectionState` in `FutureBuilder` and `StreamBuilder`, or `TextSelectionHandleType` in `TextSelectionControls.buildHandle`), use a standard `enum`. +3. Otherwise, prefer an enum-like class with `static const` values (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). #### Standard `enum` (exhaustive) -Use a standard `enum` when the values are complete by definition within Flutter's model (such as `Axis` with `horizontal` and `vertical`, or `VerticalDirection` with `up` and `down`) or when consumers must handle every case without a fallback (such as layout-related enums like `MainAxisAlignment` or `WrapAlignment`). - * Provides compile-time exhaustiveness checking for `switch` statements and expressions. * Adding a value to a public `enum` is a breaking change and requires going through the breaking change process. The same rule applies to adding a subtype to a public `sealed class`. #### Enum-like class with `static const` values (non-exhaustive) -Use an `@immutable` `final` class with `static const` fields when the set of values is not closed and consumers do not need to handle every value exhaustively, such as platforms, OS lifecycle states, hardware keys, image formats, or input types (such as `TextInputType` and `LogicalKeyboardKey`; existing enums like `TargetPlatform` and `AppLifecycleState` also fit this category). See [extensible enums in plugins](https://flutter.dev/go/extensible-enums-plugins) for prior art. +Implement as an `@immutable` `final` class with `static const` fields (see [extensible enums in plugins](https://flutter.dev/go/extensible-enums-plugins) for prior art): * Adding a `static const` field is not a breaking change. -* Mark the class `final` so external packages cannot `implement` or `extend` it, keeping future member additions non-breaking without triggering `switch` exhaustiveness. -* Include at least one non-private constructor. Dart's recommended [`exhaustive_cases`](https://dart.dev/tools/linter-rules/exhaustive_cases) lint enforces exhaustiveness on `switch` statements when a class has only private constructors. - * Use a `@protected const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). - * Use a public `const` constructor when values outside the predefined constants are meaningful (such as `LogicalKeyboardKey` for arbitrary hardware key IDs or `TextInputType.numberWithOptions`). -* Require a distinguishing field (such as `index` or `name`) on each instance so Dart `const` canonicalization does not collapse distinct constants into a single instance. +* Marking the class `final` prevents external packages from using `implement` or `extend`, keeping future member additions non-breaking without triggering `switch` exhaustiveness. +* Use `@protected const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). +* Use `index` field to prevent Dart `const` canonicalization from collapsing distinct constants into a single instance. ##### Example: closed to external instantiation @@ -79,6 +75,17 @@ final class MyEnum { static const MyEnum foo = MyEnum(0); static const MyEnum bar = MyEnum(1); + static const List _names = [ + 'foo', + 'bar', + ]; + + @override + String toString() { + final name = _names[index]; + return '${objectRuntimeType(this, 'MyEnum')}(name: MyEnum.$name)'; + } + @override bool operator ==(Object other) { return other is MyEnum && other.index == index; @@ -96,20 +103,31 @@ When callers may construct custom instances, use a public `const` constructor: ```dart @immutable final class MyEnum { - const MyEnum(this.name); + const MyEnum(this.index); - final String name; + final int index; - static const MyEnum foo = MyEnum('foo'); - static const MyEnum bar = MyEnum('bar'); + static const MyEnum foo = MyEnum(0); + static const MyEnum bar = MyEnum(1); + + static const List _names = [ + 'foo', + 'bar', + ]; + + @override + String toString() { + final name = _names[index]; + return '${objectRuntimeType(this, 'MyEnum')}(name: MyEnum.$name)'; + } @override bool operator ==(Object other) { - return other is MyEnum && other.name == name; + return other is MyEnum && other.index == index; } @override - int get hashCode => name.hashCode; + int get hashCode => index.hashCode; } ``` From 9a171d3853198a785ecc233cbf33189d4ec4b68f Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Fri, 9 Oct 2026 13:03:12 -0700 Subject: [PATCH 5/9] update --- rfc/710.0000-style-guide-enums.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index 9847c36..748d583 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -27,7 +27,7 @@ This document proposes updating the [Flutter repository style guide](https://git Inside the Flutter codebase, requiring exhaustive `switch` statements and expressions over `enum` values (without `default` branches) is intentional and remains unchanged. However, when external callers or `flutter/packages` switch exhaustively over a public `enum`, adding a new value breaks downstream compilation and rolls. -Language-level non-exhaustive ("open") enums were requested in [dart-lang/sdk#63678](https://github.com/dart-lang/sdk/issues/63678), but the Dart team noted ([comment](https://github.com/dart-lang/sdk/issues/63678#issuecomment-5375670420)) that this feature is unlikely to be prioritized in the near future. Without a library-level convention in the style guide, public APIs in the framework are inconsistent: +Without a library-level convention in the style guide, public APIs in the framework are inconsistent: * `TargetPlatform` and `AppLifecycleState` are `enum` declarations even though platforms and OS lifecycle states grow over time. Adding new values breaks callers. * `TextInputType` and `LogicalKeyboardKey` use classes with `static const` fields to allow non-breaking additions, but the style guide does not document when or how to use this pattern. @@ -53,7 +53,7 @@ Use the following decision order to choose between a standard `enum` and an enum #### Enum-like class with `static const` values (non-exhaustive) -Implement as an `@immutable` `final` class with `static const` fields (see [extensible enums in plugins](https://flutter.dev/go/extensible-enums-plugins) for prior art): +Implement as an `@immutable` `final` class with `static const` fields * Adding a `static const` field is not a breaking change. * Marking the class `final` prevents external packages from using `implement` or `extend`, keeping future member additions non-breaking without triggering `switch` exhaustiveness. From e966ce7cfddf5e58549012e0011c490c1612f431 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Fri, 9 Oct 2026 13:18:51 -0700 Subject: [PATCH 6/9] update --- rfc/710.0000-style-guide-enums.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index 748d583..89021a3 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -57,23 +57,22 @@ Implement as an `@immutable` `final` class with `static const` fields * Adding a `static const` field is not a breaking change. * Marking the class `final` prevents external packages from using `implement` or `extend`, keeping future member additions non-breaking without triggering `switch` exhaustiveness. -* Use `@protected const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). +* Use a private `const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). * Use `index` field to prevent Dart `const` canonicalization from collapsing distinct constants into a single instance. ##### Example: closed to external instantiation -When only Flutter defines valid instances, annotate the constructor with `@protected`: +When only Flutter defines valid instances, use a private `const` constructor: ```dart @immutable final class MyEnum { - @protected - const MyEnum(this.index); + const MyEnum._(this.index); final int index; - static const MyEnum foo = MyEnum(0); - static const MyEnum bar = MyEnum(1); + static const MyEnum foo = MyEnum._(0); + static const MyEnum bar = MyEnum._(1); static const List _names = [ 'foo', From b2905a333274bef968196d6a187fffd9745f58e9 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Tue, 20 Oct 2026 12:18:00 -0700 Subject: [PATCH 7/9] update --- rfc/710.0000-style-guide-enums.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index 89021a3..ce8d64b 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -56,7 +56,7 @@ Use the following decision order to choose between a standard `enum` and an enum Implement as an `@immutable` `final` class with `static const` fields * Adding a `static const` field is not a breaking change. -* Marking the class `final` prevents external packages from using `implement` or `extend`, keeping future member additions non-breaking without triggering `switch` exhaustiveness. +* Consider marking the class `final` to prevent external packages from extending or implementing it. * Use a private `const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). * Use `index` field to prevent Dart `const` canonicalization from collapsing distinct constants into a single instance. @@ -82,7 +82,7 @@ final class MyEnum { @override String toString() { final name = _names[index]; - return '${objectRuntimeType(this, 'MyEnum')}(name: MyEnum.$name)'; + return 'MyEnum.$name'; } @override @@ -117,7 +117,7 @@ final class MyEnum { @override String toString() { final name = _names[index]; - return '${objectRuntimeType(this, 'MyEnum')}(name: MyEnum.$name)'; + return 'MyEnum.$name'; } @override From 30046a9fc74ae4bdd5c188fd4b62061b998e8f52 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Wed, 21 Oct 2026 12:45:19 -0700 Subject: [PATCH 8/9] update --- rfc/710.0000-style-guide-enums.md | 38 +++++++++++-------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index ce8d64b..7b4da7f 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -58,7 +58,7 @@ Implement as an `@immutable` `final` class with `static const` fields * Adding a `static const` field is not a breaking change. * Consider marking the class `final` to prevent external packages from extending or implementing it. * Use a private `const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). -* Use `index` field to prevent Dart `const` canonicalization from collapsing distinct constants into a single instance. +* Include a distinguishing field to prevent Dart `const` canonicalization from collapsing distinct constants into a single instance. Choose the field type based on the domain of the enum (such as `int` for hardware key codes); otherwise, prefer a `String name` field with the same value as the property name by default. ##### Example: closed to external instantiation @@ -67,31 +67,25 @@ When only Flutter defines valid instances, use a private `const` constructor: ```dart @immutable final class MyEnum { - const MyEnum._(this.index); + const MyEnum._(this.name); - final int index; + final String name; - static const MyEnum foo = MyEnum._(0); - static const MyEnum bar = MyEnum._(1); - - static const List _names = [ - 'foo', - 'bar', - ]; + static const MyEnum foo = MyEnum._('foo'); + static const MyEnum bar = MyEnum._('bar'); @override String toString() { - final name = _names[index]; return 'MyEnum.$name'; } @override bool operator ==(Object other) { - return other is MyEnum && other.index == index; + return other.name == name; } @override - int get hashCode => index.hashCode; + int get hashCode => name.hashCode; } ``` @@ -102,31 +96,25 @@ When callers may construct custom instances, use a public `const` constructor: ```dart @immutable final class MyEnum { - const MyEnum(this.index); - - final int index; + const MyEnum(this.name); - static const MyEnum foo = MyEnum(0); - static const MyEnum bar = MyEnum(1); + final String name; - static const List _names = [ - 'foo', - 'bar', - ]; + static const MyEnum foo = MyEnum('foo'); + static const MyEnum bar = MyEnum('bar'); @override String toString() { - final name = _names[index]; return 'MyEnum.$name'; } @override bool operator ==(Object other) { - return other is MyEnum && other.index == index; + return other.name == name; } @override - int get hashCode => index.hashCode; + int get hashCode => name.hashCode; } ``` From 87d088e4f64fbbf4964430be98fa29a83bb9d1ae Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 22 Oct 2026 13:33:27 -0700 Subject: [PATCH 9/9] update --- rfc/710.0000-style-guide-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfc/710.0000-style-guide-enums.md b/rfc/710.0000-style-guide-enums.md index 7b4da7f..c48f122 100644 --- a/rfc/710.0000-style-guide-enums.md +++ b/rfc/710.0000-style-guide-enums.md @@ -56,7 +56,7 @@ Use the following decision order to choose between a standard `enum` and an enum Implement as an `@immutable` `final` class with `static const` fields * Adding a `static const` field is not a breaking change. -* Consider marking the class `final` to prevent external packages from extending or implementing it. +* Marking the class `final` to prevent external packages from extending or implementing it. * Use a private `const` constructor when only Flutter defines valid values (such as a platform or lifecycle state). * Include a distinguishing field to prevent Dart `const` canonicalization from collapsing distinct constants into a single instance. Choose the field type based on the domain of the enum (such as `int` for hardware key codes); otherwise, prefer a `String name` field with the same value as the property name by default.