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 'package:flutter/cupertino.dart';
6/// @docImport 'package:flutter/material.dart';
7///
8/// @docImport 'icon.dart';
9/// @docImport 'icon_theme.dart';
10library;
11
12import 'dart:ui' as ui show lerpDouble;
13
14import 'package:flutter/foundation.dart';
15import 'package:flutter/painting.dart';
16
17import 'framework.dart' show BuildContext;
18
19/// Defines the size, font variations, color, opacity, and shadows of icons.
20///
21/// Used by [IconTheme] to control those properties in a widget subtree.
22///
23/// To obtain the current icon theme, use [IconTheme.of]. To convert an icon
24/// theme to a version with all the fields filled in, use
25/// [IconThemeData.fallback].
26@immutable
27class IconThemeData with Diagnosticable {
28 /// Creates an icon theme data.
29 ///
30 /// The opacity applies to both explicit and default icon colors. The value
31 /// is clamped between 0.0 and 1.0.
32 const IconThemeData({
33 this.size,
34 this.fill,
35 this.weight,
36 this.grade,
37 this.opticalSize,
38 this.color,
39 double? opacity,
40 this.shadows,
41 this.applyTextScaling,
42 }) : _opacity = opacity,
43 assert(fill == null || (0.0 <= fill && fill <= 1.0)),
44 assert(weight == null || (0.0 < weight)),
45 assert(opticalSize == null || (0.0 < opticalSize));
46
47 /// Creates an icon theme with some reasonable default values.
48 ///
49 /// The [size] is 24.0, [fill] is 0.0, [weight] is 400.0, [grade] is 0.0,
50 /// opticalSize is 48.0, [color] is black, and [opacity] is 1.0.
51 const IconThemeData.fallback()
52 : size = 24.0,
53 fill = 0.0,
54 weight = 400.0,
55 grade = 0.0,
56 opticalSize = 48.0,
57 color = const Color(0xFF000000),
58 _opacity = 1.0,
59 shadows = null,
60 applyTextScaling = false;
61
62 /// Creates a copy of this icon theme but with the given fields replaced with
63 /// the new values.
64 IconThemeData copyWith({
65 double? size,
66 double? fill,
67 double? weight,
68 double? grade,
69 double? opticalSize,
70 Color? color,
71 double? opacity,
72 List<Shadow>? shadows,
73 bool? applyTextScaling,
74 }) {
75 return IconThemeData(
76 size: size ?? this.size,
77 fill: fill ?? this.fill,
78 weight: weight ?? this.weight,
79 grade: grade ?? this.grade,
80 opticalSize: opticalSize ?? this.opticalSize,
81 color: color ?? this.color,
82 opacity: opacity ?? this.opacity,
83 shadows: shadows ?? this.shadows,
84 applyTextScaling: applyTextScaling ?? this.applyTextScaling,
85 );
86 }
87
88 /// Returns a new icon theme that matches this icon theme but with some values
89 /// replaced by the non-null parameters of the given icon theme. If the given
90 /// icon theme is null, returns this icon theme.
91 IconThemeData merge(IconThemeData? other) {
92 if (other == null) {
93 return this;
94 }
95 return copyWith(
96 size: other.size,
97 fill: other.fill,
98 weight: other.weight,
99 grade: other.grade,
100 opticalSize: other.opticalSize,
101 color: other.color,
102 opacity: other.opacity,
103 shadows: other.shadows,
104 applyTextScaling: other.applyTextScaling,
105 );
106 }
107
108 /// Called by [IconTheme.of] to convert this instance to an [IconThemeData]
109 /// that fits the given [BuildContext].
110 ///
111 /// This method gives the ambient [IconThemeData] a chance to update itself,
112 /// after it's been retrieved by [IconTheme.of], and before being returned as
113 /// the final result. For instance, [CupertinoIconThemeData] overrides this method
114 /// to resolve [color], in case [color] is a [CupertinoDynamicColor] and needs
115 /// to be resolved against the given [BuildContext] before it can be used as a
116 /// regular [Color].
117 ///
118 /// The default implementation returns this [IconThemeData] as-is.
119 ///
120 /// See also:
121 ///
122 /// * [CupertinoIconThemeData.resolve] an implementation that resolves
123 /// the color of [CupertinoIconThemeData] before returning.
124 IconThemeData resolve(BuildContext context) => this;
125
126 /// Whether all the properties (except shadows) of this object are non-null.
127 bool get isConcrete =>
128 size != null &&
129 fill != null &&
130 weight != null &&
131 grade != null &&
132 opticalSize != null &&
133 color != null &&
134 opacity != null &&
135 applyTextScaling != null;
136
137 /// The default for [Icon.size].
138 ///
139 /// Falls back to 24.0.
140 final double? size;
141
142 /// The default for [Icon.fill].
143 ///
144 /// Falls back to 0.0.
145 final double? fill;
146
147 /// The default for [Icon.weight].
148 ///
149 /// Falls back to 400.0.
150 final double? weight;
151
152 /// The default for [Icon.grade].
153 ///
154 /// Falls back to 0.0.
155 final double? grade;
156
157 /// The default for [Icon.opticalSize].
158 ///
159 /// Falls back to 48.0.
160 final double? opticalSize;
161
162 /// The default for [Icon.color].
163 ///
164 /// In material apps, if there is a [Theme] without any [IconTheme]s
165 /// specified, icon colors default to white if [ThemeData.brightness] is dark
166 /// and black if [ThemeData.brightness] is light.
167 ///
168 /// Otherwise, falls back to black.
169 final Color? color;
170
171 /// An opacity to apply to both explicit and default icon colors.
172 ///
173 /// Falls back to 1.0.
174 double? get opacity => _opacity == null ? null : clampDouble(_opacity, 0.0, 1.0);
175 final double? _opacity;
176
177 /// The default for [Icon.shadows].
178 final List<Shadow>? shadows;
179
180 /// The default for [Icon.applyTextScaling].
181 final bool? applyTextScaling;
182
183 /// Linearly interpolate between two icon theme data objects.
184 ///
185 /// {@macro dart.ui.shadow.lerp}
186 static IconThemeData lerp(IconThemeData? a, IconThemeData? b, double t) {
187 if (identical(a, b) && a != null) {
188 return a;
189 }
190 return IconThemeData(
191 size: ui.lerpDouble(a?.size, b?.size, t),
192 fill: ui.lerpDouble(a?.fill, b?.fill, t),
193 weight: ui.lerpDouble(a?.weight, b?.weight, t),
194 grade: ui.lerpDouble(a?.grade, b?.grade, t),
195 opticalSize: ui.lerpDouble(a?.opticalSize, b?.opticalSize, t),
196 color: Color.lerp(a?.color, b?.color, t),
197 opacity: ui.lerpDouble(a?.opacity, b?.opacity, t),
198 shadows: Shadow.lerpList(a?.shadows, b?.shadows, t),
199 applyTextScaling: t < 0.5 ? a?.applyTextScaling : b?.applyTextScaling,
200 );
201 }
202
203 @override
204 bool operator ==(Object other) {
205 if (other.runtimeType != runtimeType) {
206 return false;
207 }
208 return other is IconThemeData &&
209 other.size == size &&
210 other.fill == fill &&
211 other.weight == weight &&
212 other.grade == grade &&
213 other.opticalSize == opticalSize &&
214 other.color == color &&
215 other.opacity == opacity &&
216 listEquals(other.shadows, shadows) &&
217 other.applyTextScaling == applyTextScaling;
218 }
219
220 @override
221 int get hashCode => Object.hash(
222 size,
223 fill,
224 weight,
225 grade,
226 opticalSize,
227 color,
228 opacity,
229 shadows == null ? null : Object.hashAll(shadows!),
230 applyTextScaling,
231 );
232
233 @override
234 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
235 super.debugFillProperties(properties);
236 properties.add(DoubleProperty('size', size, defaultValue: null));
237 properties.add(DoubleProperty('fill', fill, defaultValue: null));
238 properties.add(DoubleProperty('weight', weight, defaultValue: null));
239 properties.add(DoubleProperty('grade', grade, defaultValue: null));
240 properties.add(DoubleProperty('opticalSize', opticalSize, defaultValue: null));
241 properties.add(ColorProperty('color', color, defaultValue: null));
242 properties.add(DoubleProperty('opacity', opacity, defaultValue: null));
243 properties.add(IterableProperty<Shadow>('shadows', shadows, defaultValue: null));
244 properties.add(
245 DiagnosticsProperty<bool>('applyTextScaling', applyTextScaling, defaultValue: null),
246 );
247 }
248}
249