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 | import 'dart:ui' show lerpDouble; |
6 | |
7 | import 'package:flutter/foundation.dart'; |
8 | import 'package:flutter/rendering.dart'; |
9 | import 'package:flutter/widgets.dart'; |
10 | |
11 | import 'material_state.dart'; |
12 | import 'navigation_bar.dart'; |
13 | import 'theme.dart'; |
14 | |
15 | // Examples can assume: |
16 | // late BuildContext context; |
17 | |
18 | /// Defines default property values for descendant [NavigationBar] |
19 | /// widgets. |
20 | /// |
21 | /// Descendant widgets obtain the current [NavigationBarThemeData] object |
22 | /// using `NavigationBarTheme.of(context)`. Instances of |
23 | /// [NavigationBarThemeData] can be customized with |
24 | /// [NavigationBarThemeData.copyWith]. |
25 | /// |
26 | /// Typically a [NavigationBarThemeData] is specified as part of the |
27 | /// overall [Theme] with [ThemeData.navigationBarTheme]. Alternatively, a |
28 | /// [NavigationBarTheme] inherited widget can be used to theme [NavigationBar]s |
29 | /// in a subtree of widgets. |
30 | /// |
31 | /// All [NavigationBarThemeData] properties are `null` by default. |
32 | /// When null, the [NavigationBar] will provide its own defaults based on the |
33 | /// overall [Theme]'s textTheme and colorScheme. See the individual |
34 | /// [NavigationBar] properties for details. |
35 | /// |
36 | /// See also: |
37 | /// |
38 | /// * [ThemeData], which describes the overall theme information for the |
39 | /// application. |
40 | @immutable |
41 | class NavigationBarThemeData with Diagnosticable { |
42 | /// Creates a theme that can be used for [ThemeData.navigationBarTheme] and |
43 | /// [NavigationBarTheme]. |
44 | const NavigationBarThemeData({ |
45 | this.height, |
46 | this.backgroundColor, |
47 | this.elevation, |
48 | this.shadowColor, |
49 | this.surfaceTintColor, |
50 | this.indicatorColor, |
51 | this.indicatorShape, |
52 | this.labelTextStyle, |
53 | this.iconTheme, |
54 | this.labelBehavior, |
55 | this.overlayColor, |
56 | this.labelPadding, |
57 | }); |
58 | |
59 | /// Overrides the default value of [NavigationBar.height]. |
60 | final double? height; |
61 | |
62 | /// Overrides the default value of [NavigationBar.backgroundColor]. |
63 | final Color? backgroundColor; |
64 | |
65 | /// Overrides the default value of [NavigationBar.elevation]. |
66 | final double? elevation; |
67 | |
68 | /// Overrides the default value of [NavigationBar.shadowColor]. |
69 | final Color? shadowColor; |
70 | |
71 | /// Overrides the default value of [NavigationBar.surfaceTintColor]. |
72 | final Color? surfaceTintColor; |
73 | |
74 | /// Overrides the default value of [NavigationBar]'s selection indicator. |
75 | final Color? indicatorColor; |
76 | |
77 | /// Overrides the default shape of the [NavigationBar]'s selection indicator. |
78 | final ShapeBorder? indicatorShape; |
79 | |
80 | /// The style to merge with the default text style for |
81 | /// [NavigationDestination] labels. |
82 | /// |
83 | /// You can use this to specify a different style when the label is selected. |
84 | final MaterialStateProperty<TextStyle?>? labelTextStyle; |
85 | |
86 | /// The theme to merge with the default icon theme for |
87 | /// [NavigationDestination] icons. |
88 | /// |
89 | /// You can use this to specify a different icon theme when the icon is |
90 | /// selected. |
91 | final MaterialStateProperty<IconThemeData?>? iconTheme; |
92 | |
93 | /// Overrides the default value of [NavigationBar.labelBehavior]. |
94 | final NavigationDestinationLabelBehavior? labelBehavior; |
95 | |
96 | /// Overrides the default value of [NavigationBar.overlayColor]. |
97 | final MaterialStateProperty<Color?>? overlayColor; |
98 | |
99 | /// Overrides the default value of [NavigationBar.labelPadding]. |
100 | final EdgeInsetsGeometry? labelPadding; |
101 | |
102 | /// Creates a copy of this object with the given fields replaced with the |
103 | /// new values. |
104 | NavigationBarThemeData copyWith({ |
105 | double? height, |
106 | Color? backgroundColor, |
107 | double? elevation, |
108 | Color? shadowColor, |
109 | Color? surfaceTintColor, |
110 | Color? indicatorColor, |
111 | ShapeBorder? indicatorShape, |
112 | MaterialStateProperty<TextStyle?>? labelTextStyle, |
113 | MaterialStateProperty<IconThemeData?>? iconTheme, |
114 | NavigationDestinationLabelBehavior? labelBehavior, |
115 | MaterialStateProperty<Color?>? overlayColor, |
116 | EdgeInsetsGeometry? labelPadding, |
117 | }) { |
118 | return NavigationBarThemeData( |
119 | height: height ?? this.height, |
120 | backgroundColor: backgroundColor ?? this.backgroundColor, |
121 | elevation: elevation ?? this.elevation, |
122 | shadowColor: shadowColor ?? this.shadowColor, |
123 | surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor, |
124 | indicatorColor: indicatorColor ?? this.indicatorColor, |
125 | indicatorShape: indicatorShape ?? this.indicatorShape, |
126 | labelTextStyle: labelTextStyle ?? this.labelTextStyle, |
127 | iconTheme: iconTheme ?? this.iconTheme, |
128 | labelBehavior: labelBehavior ?? this.labelBehavior, |
129 | overlayColor: overlayColor ?? this.overlayColor, |
130 | labelPadding: labelPadding ?? this.labelPadding, |
131 | ); |
132 | } |
133 | |
134 | /// Linearly interpolate between two navigation rail themes. |
135 | /// |
136 | /// If both arguments are null then null is returned. |
137 | /// |
138 | /// {@macro dart.ui.shadow.lerp} |
139 | static NavigationBarThemeData? lerp( |
140 | NavigationBarThemeData? a, |
141 | NavigationBarThemeData? b, |
142 | double t, |
143 | ) { |
144 | if (identical(a, b)) { |
145 | return a; |
146 | } |
147 | return NavigationBarThemeData( |
148 | height: lerpDouble(a?.height, b?.height, t), |
149 | backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), |
150 | elevation: lerpDouble(a?.elevation, b?.elevation, t), |
151 | shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t), |
152 | surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t), |
153 | indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t), |
154 | indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t), |
155 | labelTextStyle: MaterialStateProperty.lerp<TextStyle?>( |
156 | a?.labelTextStyle, |
157 | b?.labelTextStyle, |
158 | t, |
159 | TextStyle.lerp, |
160 | ), |
161 | iconTheme: MaterialStateProperty.lerp<IconThemeData?>( |
162 | a?.iconTheme, |
163 | b?.iconTheme, |
164 | t, |
165 | IconThemeData.lerp, |
166 | ), |
167 | labelBehavior: t < 0.5 ? a?.labelBehavior : b?.labelBehavior, |
168 | overlayColor: MaterialStateProperty.lerp<Color?>( |
169 | a?.overlayColor, |
170 | b?.overlayColor, |
171 | t, |
172 | Color.lerp, |
173 | ), |
174 | labelPadding: EdgeInsetsGeometry.lerp(a?.labelPadding, b?.labelPadding, t), |
175 | ); |
176 | } |
177 | |
178 | @override |
179 | int get hashCode => Object.hash( |
180 | height, |
181 | backgroundColor, |
182 | elevation, |
183 | shadowColor, |
184 | surfaceTintColor, |
185 | indicatorColor, |
186 | indicatorShape, |
187 | labelTextStyle, |
188 | iconTheme, |
189 | labelBehavior, |
190 | overlayColor, |
191 | labelPadding, |
192 | ); |
193 | |
194 | @override |
195 | bool operator ==(Object other) { |
196 | if (identical(this, other)) { |
197 | return true; |
198 | } |
199 | if (other.runtimeType != runtimeType) { |
200 | return false; |
201 | } |
202 | return other is NavigationBarThemeData && |
203 | other.height == height && |
204 | other.backgroundColor == backgroundColor && |
205 | other.elevation == elevation && |
206 | other.shadowColor == shadowColor && |
207 | other.surfaceTintColor == surfaceTintColor && |
208 | other.indicatorColor == indicatorColor && |
209 | other.indicatorShape == indicatorShape && |
210 | other.labelTextStyle == labelTextStyle && |
211 | other.iconTheme == iconTheme && |
212 | other.labelBehavior == labelBehavior && |
213 | other.overlayColor == overlayColor && |
214 | other.labelPadding == labelPadding; |
215 | } |
216 | |
217 | @override |
218 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
219 | super.debugFillProperties(properties); |
220 | properties.add(DoubleProperty('height' , height, defaultValue: null)); |
221 | properties.add(ColorProperty('backgroundColor' , backgroundColor, defaultValue: null)); |
222 | properties.add(DoubleProperty('elevation' , elevation, defaultValue: null)); |
223 | properties.add(ColorProperty('shadowColor' , shadowColor, defaultValue: null)); |
224 | properties.add(ColorProperty('surfaceTintColor' , surfaceTintColor, defaultValue: null)); |
225 | properties.add(ColorProperty('indicatorColor' , indicatorColor, defaultValue: null)); |
226 | properties.add( |
227 | DiagnosticsProperty<ShapeBorder>('indicatorShape' , indicatorShape, defaultValue: null), |
228 | ); |
229 | properties.add( |
230 | DiagnosticsProperty<MaterialStateProperty<TextStyle?>>( |
231 | 'labelTextStyle' , |
232 | labelTextStyle, |
233 | defaultValue: null, |
234 | ), |
235 | ); |
236 | properties.add( |
237 | DiagnosticsProperty<MaterialStateProperty<IconThemeData?>>( |
238 | 'iconTheme' , |
239 | iconTheme, |
240 | defaultValue: null, |
241 | ), |
242 | ); |
243 | properties.add( |
244 | DiagnosticsProperty<NavigationDestinationLabelBehavior>( |
245 | 'labelBehavior' , |
246 | labelBehavior, |
247 | defaultValue: null, |
248 | ), |
249 | ); |
250 | properties.add( |
251 | DiagnosticsProperty<MaterialStateProperty<Color?>>( |
252 | 'overlayColor' , |
253 | overlayColor, |
254 | defaultValue: null, |
255 | ), |
256 | ); |
257 | properties.add( |
258 | DiagnosticsProperty<EdgeInsetsGeometry>('labelPadding' , labelPadding, defaultValue: null), |
259 | ); |
260 | } |
261 | } |
262 | |
263 | /// An inherited widget that defines visual properties for [NavigationBar]s and |
264 | /// [NavigationDestination]s in this widget's subtree. |
265 | /// |
266 | /// Values specified here are used for [NavigationBar] properties that are not |
267 | /// given an explicit non-null value. |
268 | /// |
269 | /// See also: |
270 | /// |
271 | /// * [ThemeData.navigationBarTheme], which describes the |
272 | /// [NavigationBarThemeData] in the overall theme for the application. |
273 | class NavigationBarTheme extends InheritedTheme { |
274 | /// Creates a navigation rail theme that controls the |
275 | /// [NavigationBarThemeData] properties for a [NavigationBar]. |
276 | const NavigationBarTheme({super.key, required this.data, required super.child}); |
277 | |
278 | /// Specifies the background color, label text style, icon theme, and label |
279 | /// type values for descendant [NavigationBar] widgets. |
280 | final NavigationBarThemeData data; |
281 | |
282 | /// The closest instance of this class that encloses the given context. |
283 | /// |
284 | /// If there is no enclosing [NavigationBarTheme] widget, then |
285 | /// [ThemeData.navigationBarTheme] is used. |
286 | /// |
287 | /// Typical usage is as follows: |
288 | /// |
289 | /// ```dart |
290 | /// NavigationBarThemeData theme = NavigationBarTheme.of(context); |
291 | /// ``` |
292 | static NavigationBarThemeData of(BuildContext context) { |
293 | final NavigationBarTheme? navigationBarTheme = |
294 | context.dependOnInheritedWidgetOfExactType<NavigationBarTheme>(); |
295 | return navigationBarTheme?.data ?? Theme.of(context).navigationBarTheme; |
296 | } |
297 | |
298 | @override |
299 | Widget wrap(BuildContext context, Widget child) { |
300 | return NavigationBarTheme(data: data, child: child); |
301 | } |
302 | |
303 | @override |
304 | bool updateShouldNotify(NavigationBarTheme oldWidget) => data != oldWidget.data; |
305 | } |
306 | |