Repository: flutter
Issue: Proposal: remove `UniqueWidget`, with no deprecation period
Labels: framework,P2,c: tech-debt,team-framework,triaged-framework

What if the Flutter framework had a class that was never used, never mentioned by anyone, and never even had a *hypothetical* use case to begin with?

That class would certainly be ***unique***.

```dart
abstract class UniqueWidget<T extends State<StatefulWidget>> extends StatefulWidget {
  const UniqueWidget({required GlobalKey<T> super.key});

  @override
  T createState();

  T? get currentState => (key! as GlobalKey<T>).currentState;
}
```

<br>

There are often times when someone creates a "unique" widget (one that doesn't show up more than once in the widget tree), and they'd like global access to its state.

```dart
void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThemeMode _themeMode = ThemeMode.system;
  void newThemeMode(ThemeMode newThemeMode) {
    setState(() {
      _themeMode = newThemeMode;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(),
      darkTheme: ThemeData.dark(),
      themeMode: _themeMode,
      home: const HomePage(),
    );
  }
}
```

There are a few ways you could go about calling the `newThemeMode()` function.

Without using `UniqueWidget`, you could do it like so:

```dart
class MyApp extends StatefulWidget {
  static void changeThemeMode(BuildContext context, ThemeMode themeMode) {
    final appState = context.findAncestorStateOfType<_MyAppState>()!;
    appState.newThemeMode(themeMode);
  }
}
```

Here's how it would look with a `UniqueWidget`:

```dart
class MyApp extends UniqueWidget<_MyAppState> {
  static void changeThemeMode(BuildContext context, ThemeMode themeMode) {
    final appState = context.findAncestorWidgetOfExactType<MyApp>()!.currentState!;
    appState.newThemeMode(themeMode);
  }
}
```

- The logic is now slightly *more* verbose.
- The return type of `createState()` is now guaranteed to violate the [`private_types_in_public_api`](https://dart.dev/tools/linter-rules/library_private_types_in_public_api) rule.
- A `Widget` instance was accessed in order to alter its `State`, which goes against Flutter's [declarative UI style](https://docs.flutter.dev/get-started/flutter-for/declarative).

That's quite the improvement!

<br>

Flutter's [simple app state management](https://docs.flutter.dev/data-and-backend/state-mgmt/simple) page recommends using the [provider](https://pub.dev/packages/provider) package, and that'd work great here.

There's also a solution that doesn't rely on additional packages:

```dart
class MyApp extends StatefulWidget {
  const MyApp() : super(key: _key);

  static const _key = GlobalObjectKey<_MyAppState>('app state');
  static _MyAppState get _appState => _key.currentState!;

  static ThemeMode get themeMode => _appState._themeMode;
  static set themeMode(ThemeMode newMode) {
    _appState.newThemeMode(newMode);
  }
}
```

Switching to `ThemeMode.dark` can now be accomplished as follows:

```dart
void onPressed() {
  MyApp.themeMode = ThemeMode.dark;
}
```

<br><br>

The deprecation period length has a huge impact on the developer experience, and the Flutter wiki's [tree hygiene page](https://github.com/flutter/flutter/blob/5187cab7bdd434ca74abb45895d17e9fa553678a/docs/contributing/Tree-hygiene.md#deprecations) is completely straightforward about it:

> Deprecations are removed in a consistent "first-in-first-out" fashion. The lifetime for a Flutter deprecation is 1 year after reaching the stable channel, or after 4 stable releases, whichever is longer.

<br>

But this is a unique situation: there's no evidence of this widget being used—or even being talked about—anywhere on the internet. (The only relevant Google search result is the [auto-generated documentation](https://api.flutter.dev/flutter/widgets/UniqueWidget-class.html).)

![google search](https://github.com/flutter/flutter/assets/10457200/cfafc965-4568-4f32-bb9b-ba5d74de97d9)

When compared with the tangible opportunity cost of tracking `UniqueWidget`'s deprecation cycle, the possibility of a negative impact created by its removal is virtually nonexistent.

<br><br>

If a tree falls in a forest, but there was never a widget tree containing this class, does it make a sound?