Skip to content

Commit 4a54ca8

Browse files
authored
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.
1 parent a5ca16e commit 4a54ca8

File tree

5 files changed

+105
-6
lines changed

5 files changed

+105
-6
lines changed

packages/flutter/lib/src/material/color_scheme.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import 'package:flutter/widgets.dart';
1414
import 'package:material_color_utilities/material_color_utilities.dart';
1515

1616
import 'colors.dart';
17-
import 'theme_data.dart';
17+
import 'theme.dart';
1818

1919
/// The algorithm used to construct a [ColorScheme] in [ColorScheme.fromSeed].
2020
///
@@ -1920,4 +1920,9 @@ class ColorScheme with Diagnosticable {
19201920
DynamicSchemeVariant.fruitSalad => SchemeFruitSalad(sourceColorHct: sourceColor, isDark: isDark, contrastLevel: contrastLevel),
19211921
};
19221922
}
1923+
1924+
/// The [ThemeData.colorScheme] of the ambient [Theme].
1925+
///
1926+
/// Equivalent to `Theme.of(context).colorScheme`.
1927+
static ColorScheme of(BuildContext context) => Theme.of(context).colorScheme;
19231928
}

packages/flutter/lib/src/material/text_theme.dart

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
/// @docImport 'package:flutter/widgets.dart';
6-
///
75
/// @docImport 'elevated_button.dart';
86
/// @docImport 'material.dart';
97
/// @docImport 'outlined_button.dart';
@@ -13,8 +11,9 @@
1311
library;
1412

1513
import 'package:flutter/foundation.dart';
16-
import 'package:flutter/painting.dart';
14+
import 'package:flutter/widgets.dart';
1715

16+
import 'theme.dart';
1817
import 'typography.dart';
1918

2019
/// Material design text theme.
@@ -23,8 +22,9 @@ import 'typography.dart';
2322
/// (e.g., labelLarge, bodySmall). Rather than creating a [TextTheme] directly,
2423
/// you can obtain an instance as [Typography.black] or [Typography.white].
2524
///
26-
/// To obtain the current text theme, call [Theme.of] with the current
27-
/// [BuildContext] and read the [ThemeData.textTheme] property.
25+
/// To obtain the current text theme, call [TextTheme.of] with the current
26+
/// [BuildContext]. This is equivalent to calling [Theme.of] and reading
27+
/// the [ThemeData.textTheme] property.
2828
///
2929
/// The names of the TextTheme properties match this table from the
3030
/// [Material Design spec](https://m3.material.io/styles/typography/tokens).
@@ -597,6 +597,25 @@ class TextTheme with Diagnosticable {
597597
);
598598
}
599599

600+
/// The [ThemeData.textTheme] property of the ambient [Theme].
601+
///
602+
/// Equivalent to `Theme.of(context).textTheme`.
603+
///
604+
/// See also:
605+
/// * [TextTheme.primaryOf], which returns the [ThemeData.primaryTextTheme] property of
606+
/// the ambient [Theme] instead.
607+
static TextTheme of(BuildContext context) => Theme.of(context).textTheme;
608+
609+
/// The [ThemeData.primaryTextTheme] property of the ambient [Theme].
610+
///
611+
///
612+
/// Equivalent to `Theme.of(context).primaryTextTheme`.
613+
///
614+
/// See also:
615+
/// * [TextTheme.of], which returns the [ThemeData.textTheme] property of the ambient
616+
/// [Theme] instead.
617+
static TextTheme primaryOf(BuildContext context) => Theme.of(context).primaryTextTheme;
618+
600619
@override
601620
bool operator ==(Object other) {
602621
if (identical(this, other)) {

packages/flutter/lib/src/material/theme.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// found in the LICENSE file.
44

55
/// @docImport 'app.dart';
6+
/// @docImport 'color_scheme.dart';
7+
/// @docImport 'text_theme.dart';
68
library;
79

810
import 'package:flutter/cupertino.dart';
@@ -104,6 +106,15 @@ class Theme extends StatelessWidget {
104106
/// );
105107
/// }
106108
/// ```
109+
///
110+
/// See also:
111+
///
112+
/// * [ColorScheme.of], a convenience method that returns [ThemeData.colorScheme]
113+
/// from the closest [Theme] ancestor. (equivalent to `Theme.of(context).colorScheme`).
114+
/// * [TextTheme.of], a convenience method that returns [ThemeData.textTheme]
115+
/// from the closest [Theme] ancestor. (equivalent to `Theme.of(context).textTheme`).
116+
/// * [IconTheme.of], that returns [ThemeData.iconTheme] from the closest [Theme] or
117+
/// [IconThemeData.fallback] if there is no [IconTheme] ancestor.
107118
static ThemeData of(BuildContext context) {
108119
final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
109120
final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);

packages/flutter/test/material/color_scheme_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,25 @@ void main() {
901901
colorsMatchDynamicSchemeColors(schemeVariant, Brightness.dark, 0.5);
902902
}
903903
});
904+
905+
testWidgets('ColorScheme.of(context) is equivalent to Theme.of(context).colorScheme', (WidgetTester tester) async {
906+
const Key sizedBoxKey = Key('sizedBox');
907+
final ColorScheme colorScheme = ColorScheme.fromSeed(seedColor: Colors.red);
908+
909+
await tester.pumpWidget(
910+
MaterialApp(
911+
theme: ThemeData.from(colorScheme: colorScheme),
912+
home: const SizedBox(key: sizedBoxKey),
913+
),
914+
);
915+
916+
final BuildContext context = tester.element(find.byKey(sizedBoxKey));
917+
final ColorScheme colorSchemeOfTheme = Theme.of(context).colorScheme;
918+
final ColorScheme colorSchemeFromContext = ColorScheme.of(context);
919+
920+
expect(colorSchemeOfTheme, colorScheme);
921+
expect(colorSchemeFromContext, colorScheme);
922+
});
904923
}
905924

906925
Future<void> _testFilledButtonColor(WidgetTester tester, ColorScheme scheme, Color expectation) async {

packages/flutter/test/material/text_theme_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,49 @@ void main() {
246246
expect(fullLerp.horizontal, 2.0);
247247
expect(fullLerp.vertical, 1.0);
248248
});
249+
250+
testWidgets('TextTheme.of(context) is equivalent to Theme.of(context).textTheme', (WidgetTester tester) async {
251+
const Key sizedBoxKey = Key('sizedBox');
252+
253+
await tester.pumpWidget(
254+
MaterialApp(
255+
theme: ThemeData(
256+
textTheme: const TextTheme(
257+
displayLarge: TextStyle(color: Colors.blue, fontSize: 30.0),
258+
),
259+
),
260+
home: const SizedBox(key: sizedBoxKey),
261+
),
262+
);
263+
final BuildContext context = tester.element(find.byKey(sizedBoxKey));
264+
265+
final ThemeData themeData = Theme.of(context);
266+
final TextTheme expectedTextTheme = themeData.textTheme;
267+
final TextTheme actualTextTheme = TextTheme.of(context);
268+
269+
expect(actualTextTheme, equals(expectedTextTheme));
270+
271+
});
272+
273+
testWidgets('TextTheme.primaryOf(context) is equivalent to Theme.of(context).primaryTextTheme', (WidgetTester tester) async {
274+
const Key sizedBoxKey = Key('sizedBox');
275+
276+
await tester.pumpWidget(
277+
MaterialApp(
278+
theme: ThemeData(
279+
primaryTextTheme: const TextTheme(
280+
displayLarge: TextStyle(backgroundColor: Colors.green, fontStyle: FontStyle.italic),
281+
),
282+
),
283+
home: const SizedBox(key: sizedBoxKey),
284+
),
285+
);
286+
287+
final BuildContext context = tester.element(find.byKey(sizedBoxKey));
288+
final ThemeData themeData = Theme.of(context);
289+
final TextTheme expectedTextTheme = themeData.primaryTextTheme;
290+
final TextTheme actualTextTheme = TextTheme.primaryOf(context);
291+
292+
expect(actualTextTheme, equals(expectedTextTheme));
293+
});
249294
}

0 commit comments

Comments
 (0)