1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'app.dart';
6/// @docImport 'color_scheme.dart';
7/// @docImport 'text_theme.dart';
8library;
9
10import 'package:flutter/cupertino.dart';
11import 'package:flutter/foundation.dart';
12
13import 'material_localizations.dart';
14import 'theme_data.dart';
15import 'typography.dart';
16
17export 'theme_data.dart' show Brightness, ThemeData;
18
19/// The duration over which theme changes animate by default.
20const Duration kThemeAnimationDuration = Duration(milliseconds: 200);
21
22/// Applies a theme to descendant widgets.
23///
24/// A theme describes the colors and typographic choices of an application.
25///
26/// {@youtube 560 315 https://www.youtube.com/watch?v=oTvQDJOBXmM}
27///
28/// Descendant widgets obtain the current theme's [ThemeData] object using
29/// [Theme.of]. When a widget uses [Theme.of], it is automatically rebuilt if
30/// the theme later changes, so that the changes can be applied.
31///
32/// The [Theme] widget implies an [IconTheme] widget, set to the value of the
33/// [ThemeData.iconTheme] of the [data] for the [Theme].
34///
35/// See also:
36///
37/// * [ThemeData], which describes the actual configuration of a theme.
38/// * [AnimatedTheme], which animates the [ThemeData] when it changes rather
39/// than changing the theme all at once.
40/// * [MaterialApp], which includes an [AnimatedTheme] widget configured via
41/// the [MaterialApp.theme] argument.
42class Theme extends StatelessWidget {
43 /// Applies the given theme [data] to [child].
44 const Theme({
45 super.key,
46 required this.data,
47 required this.child,
48 });
49
50 /// Specifies the color and typography values for descendant widgets.
51 final ThemeData data;
52
53 /// The widget below this widget in the tree.
54 ///
55 /// {@macro flutter.widgets.ProxyWidget.child}
56 final Widget child;
57
58 static final ThemeData _kFallbackTheme = ThemeData.fallback();
59
60 /// The data from the closest [Theme] instance that encloses the given
61 /// context.
62 ///
63 /// If the given context is enclosed in a [Localizations] widget providing
64 /// [MaterialLocalizations], the returned data is localized according to the
65 /// nearest available [MaterialLocalizations].
66 ///
67 /// Defaults to [ThemeData.fallback] if there is no [Theme] in the given
68 /// build context.
69 ///
70 /// Typical usage is as follows:
71 ///
72 /// ```dart
73 /// @override
74 /// Widget build(BuildContext context) {
75 /// return Text(
76 /// 'Example',
77 /// style: Theme.of(context).textTheme.titleLarge,
78 /// );
79 /// }
80 /// ```
81 ///
82 /// When the [Theme] is actually created in the same `build` function
83 /// (possibly indirectly, e.g. as part of a [MaterialApp]), the `context`
84 /// argument to the `build` function can't be used to find the [Theme] (since
85 /// it's "above" the widget being returned). In such cases, the following
86 /// technique with a [Builder] can be used to provide a new scope with a
87 /// [BuildContext] that is "under" the [Theme]:
88 ///
89 /// ```dart
90 /// @override
91 /// Widget build(BuildContext context) {
92 /// return MaterialApp(
93 /// theme: ThemeData.light(),
94 /// home: Builder(
95 /// // Create an inner BuildContext so that we can refer to
96 /// // the Theme with Theme.of().
97 /// builder: (BuildContext context) {
98 /// return Center(
99 /// child: Text(
100 /// 'Example',
101 /// style: Theme.of(context).textTheme.titleLarge,
102 /// ),
103 /// );
104 /// },
105 /// ),
106 /// );
107 /// }
108 /// ```
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.
118 static ThemeData of(BuildContext context) {
119 final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
120 final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
121 final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
122 final InheritedCupertinoTheme? inheritedCupertinoTheme = context.dependOnInheritedWidgetOfExactType<InheritedCupertinoTheme>();
123 final ThemeData theme = inheritedTheme?.theme.data ?? (
124 inheritedCupertinoTheme != null ? CupertinoBasedMaterialThemeData(themeData: inheritedCupertinoTheme.theme.data).materialTheme : _kFallbackTheme
125 );
126 return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
127 }
128
129 // The inherited themes in widgets library can not infer their values from
130 // Theme in material library. Wraps the child with these inherited themes to
131 // overrides their values directly.
132 Widget _wrapsWidgetThemes(BuildContext context, Widget child) {
133 final DefaultSelectionStyle selectionStyle = DefaultSelectionStyle.of(context);
134 return IconTheme(
135 data: data.iconTheme,
136 child: DefaultSelectionStyle(
137 selectionColor: data.textSelectionTheme.selectionColor ?? selectionStyle.selectionColor,
138 cursorColor: data.textSelectionTheme.cursorColor ?? selectionStyle.cursorColor,
139 child: child,
140 ),
141 );
142 }
143
144 CupertinoThemeData _inheritedCupertinoThemeData(BuildContext context) {
145 final InheritedCupertinoTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<InheritedCupertinoTheme>();
146 return (inheritedTheme?.theme.data ?? MaterialBasedCupertinoThemeData(materialTheme: data)).resolveFrom(context);
147 }
148
149 @override
150 Widget build(BuildContext context) {
151 return _InheritedTheme(
152 theme: this,
153 child: CupertinoTheme(
154 // If a CupertinoThemeData doesn't exist, we're using a
155 // MaterialBasedCupertinoThemeData here instead of a CupertinoThemeData
156 // because it defers some properties to the Material ThemeData.
157 data: _inheritedCupertinoThemeData(context),
158 child: _wrapsWidgetThemes(context, child),
159 ),
160 );
161 }
162
163 @override
164 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
165 super.debugFillProperties(properties);
166 properties.add(DiagnosticsProperty<ThemeData>('data', data, showName: false));
167 }
168}
169
170class _InheritedTheme extends InheritedTheme {
171 const _InheritedTheme({
172 required this.theme,
173 required super.child,
174 });
175
176 final Theme theme;
177
178 @override
179 Widget wrap(BuildContext context, Widget child) {
180 return Theme(data: theme.data, child: child);
181 }
182
183 @override
184 bool updateShouldNotify(_InheritedTheme old) => theme.data != old.theme.data;
185}
186
187/// An interpolation between two [ThemeData]s.
188///
189/// This class specializes the interpolation of [Tween<ThemeData>] to call the
190/// [ThemeData.lerp] method.
191///
192/// See [Tween] for a discussion on how to use interpolation objects.
193class ThemeDataTween extends Tween<ThemeData> {
194 /// Creates a [ThemeData] tween.
195 ///
196 /// The [begin] and [end] properties must be non-null before the tween is
197 /// first used, but the arguments can be null if the values are going to be
198 /// filled in later.
199 ThemeDataTween({ super.begin, super.end });
200
201 @override
202 ThemeData lerp(double t) => ThemeData.lerp(begin!, end!, t);
203}
204
205/// Animated version of [Theme] which automatically transitions the colors,
206/// etc, over a given duration whenever the given theme changes.
207///
208/// Here's an illustration of what using this widget looks like, using a [curve]
209/// of [Curves.elasticInOut].
210/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_theme.mp4}
211///
212/// See also:
213///
214/// * [Theme], which [AnimatedTheme] uses to actually apply the interpolated
215/// theme.
216/// * [ThemeData], which describes the actual configuration of a theme.
217/// * [MaterialApp], which includes an [AnimatedTheme] widget configured via
218/// the [MaterialApp.theme] argument.
219class AnimatedTheme extends ImplicitlyAnimatedWidget {
220 /// Creates an animated theme.
221 ///
222 /// By default, the theme transition uses a linear curve.
223 const AnimatedTheme({
224 super.key,
225 required this.data,
226 super.curve,
227 super.duration = kThemeAnimationDuration,
228 super.onEnd,
229 required this.child,
230 });
231
232 /// Specifies the color and typography values for descendant widgets.
233 final ThemeData data;
234
235 /// The widget below this widget in the tree.
236 ///
237 /// {@macro flutter.widgets.ProxyWidget.child}
238 final Widget child;
239
240 @override
241 AnimatedWidgetBaseState<AnimatedTheme> createState() => _AnimatedThemeState();
242}
243
244class _AnimatedThemeState extends AnimatedWidgetBaseState<AnimatedTheme> {
245 ThemeDataTween? _data;
246
247 @override
248 void forEachTween(TweenVisitor<dynamic> visitor) {
249 _data = visitor(_data, widget.data, (dynamic value) => ThemeDataTween(begin: value as ThemeData))! as ThemeDataTween;
250 }
251
252 @override
253 Widget build(BuildContext context) {
254 return Theme(
255 data: _data!.evaluate(animation),
256 child: widget.child,
257 );
258 }
259
260 @override
261 void debugFillProperties(DiagnosticPropertiesBuilder description) {
262 super.debugFillProperties(description);
263 description.add(DiagnosticsProperty<ThemeDataTween>('data', _data, showName: false, defaultValue: null));
264 }
265}
266

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com