Add static of accessor methods to ColorScheme and TextTheme (#154073)

The most common use case to lookup a `ThemeData` instance using `Theme.of(context)` is to access either the `ColorScheme`, the `TextTheme`, or both.

Before this change:
```dart
final colors = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final primaryTextTheme = Theme.of(context).primaryTextTheme;
```
or
```dart
final ThemeData(
      :colorScheme,
      :textTheme,
      :primaryTextTheme,
    ) = Theme.of(context);
```
After this change:
```dart
final colors = ColorScheme.of(context);
final textTheme = TextTheme.of(context);
final primaryTextTheme = TextTheme.primaryOf(context);
```

### Primary Changes
This PR adds static `of` convenience methods to `ColorScheme` and `TextTheme` that delegate to the `ThemeData`'s respective properties. The methods added are:
* `ColorScheme.of(context)` that returns `Theme.of(context).colorScheme`.
* `TextTheme.of(context)` that returns `Theme.of(context).textTheme`.
* `TextTheme.primaryOf(context)` that returns `Theme.of(context).primaryTextTheme`.

### Side-effects
To allow the above changes to function, this PR adds:
* A `theme.dart` import to `color_scheme.dart` to access to `Theme`.
* A `theme.dart` import to `text_theme.dart` to access to `Theme`.
* A `package:flutter/widgets.dart` import to `text_theme.dart` to access `BuildContext`. 
* The above import allowed getting rid of the same `@docImport` from `text_theme.dart`.

### Documentation updates
This PR also updates the following documentation elements:
* Adds docs to the newly added members.
* Updates `TextTheme`'s docs to instruct using `TextTheme.of(context)` instead of `Theme.of(context).textTheme`.
* Updates `Theme.of` to add a "See also" section to `ColorScheme.of` and `TextTheme.of` since these use cases are among the most common ones for `Theme.of(context)`.

Fixes #72201.
This commit is contained in:
Amal Krishna 2024-09-04 21:57:48 +05:30 committed by GitHub
parent a5ca16ea94
commit 4a54ca8285
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 105 additions and 6 deletions

View File

@ -14,7 +14,7 @@ import 'package:flutter/widgets.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
import 'colors.dart';
import 'theme_data.dart';
import 'theme.dart';
/// The algorithm used to construct a [ColorScheme] in [ColorScheme.fromSeed].
///
@ -1920,4 +1920,9 @@ class ColorScheme with Diagnosticable {
DynamicSchemeVariant.fruitSalad => SchemeFruitSalad(sourceColorHct: sourceColor, isDark: isDark, contrastLevel: contrastLevel),
};
}
/// The [ThemeData.colorScheme] of the ambient [Theme].
///
/// Equivalent to `Theme.of(context).colorScheme`.
static ColorScheme of(BuildContext context) => Theme.of(context).colorScheme;
}

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// @docImport 'package:flutter/widgets.dart';
///
/// @docImport 'elevated_button.dart';
/// @docImport 'material.dart';
/// @docImport 'outlined_button.dart';
@ -13,8 +11,9 @@
library;
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/widgets.dart';
import 'theme.dart';
import 'typography.dart';
/// Material design text theme.
@ -23,8 +22,9 @@ import 'typography.dart';
/// (e.g., labelLarge, bodySmall). Rather than creating a [TextTheme] directly,
/// you can obtain an instance as [Typography.black] or [Typography.white].
///
/// To obtain the current text theme, call [Theme.of] with the current
/// [BuildContext] and read the [ThemeData.textTheme] property.
/// To obtain the current text theme, call [TextTheme.of] with the current
/// [BuildContext]. This is equivalent to calling [Theme.of] and reading
/// the [ThemeData.textTheme] property.
///
/// The names of the TextTheme properties match this table from the
/// [Material Design spec](https://m3.material.io/styles/typography/tokens).
@ -597,6 +597,25 @@ class TextTheme with Diagnosticable {
);
}
/// The [ThemeData.textTheme] property of the ambient [Theme].
///
/// Equivalent to `Theme.of(context).textTheme`.
///
/// See also:
/// * [TextTheme.primaryOf], which returns the [ThemeData.primaryTextTheme] property of
/// the ambient [Theme] instead.
static TextTheme of(BuildContext context) => Theme.of(context).textTheme;
/// The [ThemeData.primaryTextTheme] property of the ambient [Theme].
///
///
/// Equivalent to `Theme.of(context).primaryTextTheme`.
///
/// See also:
/// * [TextTheme.of], which returns the [ThemeData.textTheme] property of the ambient
/// [Theme] instead.
static TextTheme primaryOf(BuildContext context) => Theme.of(context).primaryTextTheme;
@override
bool operator ==(Object other) {
if (identical(this, other)) {

View File

@ -3,6 +3,8 @@
// found in the LICENSE file.
/// @docImport 'app.dart';
/// @docImport 'color_scheme.dart';
/// @docImport 'text_theme.dart';
library;
import 'package:flutter/cupertino.dart';
@ -104,6 +106,15 @@ class Theme extends StatelessWidget {
/// );
/// }
/// ```
///
/// See also:
///
/// * [ColorScheme.of], a convenience method that returns [ThemeData.colorScheme]
/// from the closest [Theme] ancestor. (equivalent to `Theme.of(context).colorScheme`).
/// * [TextTheme.of], a convenience method that returns [ThemeData.textTheme]
/// from the closest [Theme] ancestor. (equivalent to `Theme.of(context).textTheme`).
/// * [IconTheme.of], that returns [ThemeData.iconTheme] from the closest [Theme] or
/// [IconThemeData.fallback] if there is no [IconTheme] ancestor.
static ThemeData of(BuildContext context) {
final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);

View File

@ -901,6 +901,25 @@ void main() {
colorsMatchDynamicSchemeColors(schemeVariant, Brightness.dark, 0.5);
}
});
testWidgets('ColorScheme.of(context) is equivalent to Theme.of(context).colorScheme', (WidgetTester tester) async {
const Key sizedBoxKey = Key('sizedBox');
final ColorScheme colorScheme = ColorScheme.fromSeed(seedColor: Colors.red);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: colorScheme),
home: const SizedBox(key: sizedBoxKey),
),
);
final BuildContext context = tester.element(find.byKey(sizedBoxKey));
final ColorScheme colorSchemeOfTheme = Theme.of(context).colorScheme;
final ColorScheme colorSchemeFromContext = ColorScheme.of(context);
expect(colorSchemeOfTheme, colorScheme);
expect(colorSchemeFromContext, colorScheme);
});
}
Future<void> _testFilledButtonColor(WidgetTester tester, ColorScheme scheme, Color expectation) async {

View File

@ -246,4 +246,49 @@ void main() {
expect(fullLerp.horizontal, 2.0);
expect(fullLerp.vertical, 1.0);
});
testWidgets('TextTheme.of(context) is equivalent to Theme.of(context).textTheme', (WidgetTester tester) async {
const Key sizedBoxKey = Key('sizedBox');
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
textTheme: const TextTheme(
displayLarge: TextStyle(color: Colors.blue, fontSize: 30.0),
),
),
home: const SizedBox(key: sizedBoxKey),
),
);
final BuildContext context = tester.element(find.byKey(sizedBoxKey));
final ThemeData themeData = Theme.of(context);
final TextTheme expectedTextTheme = themeData.textTheme;
final TextTheme actualTextTheme = TextTheme.of(context);
expect(actualTextTheme, equals(expectedTextTheme));
});
testWidgets('TextTheme.primaryOf(context) is equivalent to Theme.of(context).primaryTextTheme', (WidgetTester tester) async {
const Key sizedBoxKey = Key('sizedBox');
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
primaryTextTheme: const TextTheme(
displayLarge: TextStyle(backgroundColor: Colors.green, fontStyle: FontStyle.italic),
),
),
home: const SizedBox(key: sizedBoxKey),
),
);
final BuildContext context = tester.element(find.byKey(sizedBoxKey));
final ThemeData themeData = Theme.of(context);
final TextTheme expectedTextTheme = themeData.primaryTextTheme;
final TextTheme actualTextTheme = TextTheme.primaryOf(context);
expect(actualTextTheme, equals(expectedTextTheme));
});
}