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