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/material.dart';
6///
7/// @docImport 'app.dart';
8/// @docImport 'image_icon.dart';
9library;
10
11import 'package:flutter/foundation.dart';
12import 'package:flutter/rendering.dart';
13
14import 'basic.dart';
15import 'debug.dart';
16import 'framework.dart';
17import 'icon_data.dart';
18import 'icon_theme.dart';
19import 'icon_theme_data.dart';
20import 'media_query.dart';
21
22/// A graphical icon widget drawn with a glyph from a font described in
23/// an [IconData] such as material's predefined [IconData]s in [Icons].
24///
25/// Icons are not interactive. For an interactive icon, consider material's
26/// [IconButton].
27///
28/// There must be an ambient [Directionality] widget when using [Icon].
29/// Typically this is introduced automatically by the [WidgetsApp] or
30/// [MaterialApp].
31///
32/// This widget assumes that the rendered icon is squared. Non-squared icons may
33/// render incorrectly.
34///
35/// {@tool snippet}
36///
37/// This example shows how to create a [Row] of [Icon]s in different colors and
38/// sizes. The first [Icon] uses a [semanticLabel] to announce in accessibility
39/// modes like TalkBack and VoiceOver.
40///
41/// ![The following code snippet would generate a row of icons consisting of a pink heart, a green musical note, and a blue umbrella, each progressively bigger than the last.](https://flutter.github.io/assets-for-api-docs/assets/widgets/icon.png)
42///
43/// ```dart
44/// const Row(
45/// mainAxisAlignment: MainAxisAlignment.spaceAround,
46/// children: <Widget>[
47/// Icon(
48/// Icons.favorite,
49/// color: Colors.pink,
50/// size: 24.0,
51/// semanticLabel: 'Text to announce in accessibility modes',
52/// ),
53/// Icon(
54/// Icons.audiotrack,
55/// color: Colors.green,
56/// size: 30.0,
57/// ),
58/// Icon(
59/// Icons.beach_access,
60/// color: Colors.blue,
61/// size: 36.0,
62/// ),
63/// ],
64/// )
65/// ```
66/// {@end-tool}
67///
68/// See also:
69///
70/// * [IconButton], for interactive icons.
71/// * [Icons], for the list of available Material Icons for use with this class.
72/// * [IconTheme], which provides ambient configuration for icons.
73/// * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s.
74class Icon extends StatelessWidget {
75 /// Creates an icon.
76 const Icon(
77 this.icon, {
78 super.key,
79 this.size,
80 this.fill,
81 this.weight,
82 this.grade,
83 this.opticalSize,
84 this.color,
85 this.shadows,
86 this.semanticLabel,
87 this.textDirection,
88 this.applyTextScaling,
89 this.blendMode,
90 this.fontWeight,
91 }) : assert(fill == null || (0.0 <= fill && fill <= 1.0)),
92 assert(weight == null || (0.0 < weight)),
93 assert(opticalSize == null || (0.0 < opticalSize));
94
95 /// The icon to display. The available icons are described in [Icons].
96 ///
97 /// The icon can be null, in which case the widget will render as an empty
98 /// space of the specified [size].
99 final IconData? icon;
100
101 /// The size of the icon in logical pixels.
102 ///
103 /// Icons occupy a square with width and height equal to size.
104 ///
105 /// Defaults to the nearest [IconTheme]'s [IconThemeData.size].
106 ///
107 /// If this [Icon] is being placed inside an [IconButton], then use
108 /// [IconButton.iconSize] instead, so that the [IconButton] can make the splash
109 /// area the appropriate size as well. The [IconButton] uses an [IconTheme] to
110 /// pass down the size to the [Icon].
111 final double? size;
112
113 /// The fill for drawing the icon.
114 ///
115 /// Requires the underlying icon font to support the `FILL` [FontVariation]
116 /// axis, otherwise has no effect. Variable font filenames often indicate
117 /// the supported axes. Must be between 0.0 (unfilled) and 1.0 (filled),
118 /// inclusive.
119 ///
120 /// Can be used to convey a state transition for animation or interaction.
121 ///
122 /// Defaults to nearest [IconTheme]'s [IconThemeData.fill].
123 ///
124 /// See also:
125 /// * [weight], for controlling stroke weight.
126 /// * [grade], for controlling stroke weight in a more granular way.
127 /// * [opticalSize], for controlling optical size.
128 final double? fill;
129
130 /// The stroke weight for drawing the icon.
131 ///
132 /// Requires the underlying icon font to support the `wght` [FontVariation]
133 /// axis, otherwise has no effect. Variable font filenames often indicate
134 /// the supported axes. Must be greater than 0.
135 ///
136 /// Defaults to nearest [IconTheme]'s [IconThemeData.weight].
137 ///
138 /// See also:
139 /// * [fill], for controlling fill.
140 /// * [grade], for controlling stroke weight in a more granular way.
141 /// * [opticalSize], for controlling optical size.
142 /// * https://fonts.google.com/knowledge/glossary/weight_axis
143 final double? weight;
144
145 /// The grade (granular stroke weight) for drawing the icon.
146 ///
147 /// Requires the underlying icon font to support the `GRAD` [FontVariation]
148 /// axis, otherwise has no effect. Variable font filenames often indicate
149 /// the supported axes. Can be negative.
150 ///
151 /// Grade and [weight] both affect a symbol's stroke weight (thickness), but
152 /// grade has a smaller impact on the size of the symbol.
153 ///
154 /// Grade is also available in some text fonts. One can match grade levels
155 /// between text and symbols for a harmonious visual effect. For example, if
156 /// the text font has a -25 grade value, the symbols can match it with a
157 /// suitable value, say -25.
158 ///
159 /// Defaults to nearest [IconTheme]'s [IconThemeData.grade].
160 ///
161 /// See also:
162 /// * [fill], for controlling fill.
163 /// * [weight], for controlling stroke weight in a less granular way.
164 /// * [opticalSize], for controlling optical size.
165 /// * https://fonts.google.com/knowledge/glossary/grade_axis
166 final double? grade;
167
168 /// The optical size for drawing the icon.
169 ///
170 /// Requires the underlying icon font to support the `opsz` [FontVariation]
171 /// axis, otherwise has no effect. Variable font filenames often indicate
172 /// the supported axes. Must be greater than 0.
173 ///
174 /// For an icon to look the same at different sizes, the stroke weight
175 /// (thickness) must change as the icon size scales. Optical size offers a way
176 /// to automatically adjust the stroke weight as icon size changes.
177 ///
178 /// Defaults to nearest [IconTheme]'s [IconThemeData.opticalSize].
179 ///
180 /// See also:
181 /// * [fill], for controlling fill.
182 /// * [weight], for controlling stroke weight.
183 /// * [grade], for controlling stroke weight in a more granular way.
184 /// * https://fonts.google.com/knowledge/glossary/optical_size_axis
185 final double? opticalSize;
186
187 /// The color to use when drawing the icon.
188 ///
189 /// Defaults to the nearest [IconTheme]'s [IconThemeData.color].
190 ///
191 /// The color (whether specified explicitly here or obtained from the
192 /// [IconTheme]) will be further adjusted by the nearest [IconTheme]'s
193 /// [IconThemeData.opacity].
194 ///
195 /// {@tool snippet}
196 /// Typically, a Material Design color will be used, as follows:
197 ///
198 /// ```dart
199 /// Icon(
200 /// Icons.widgets,
201 /// color: Colors.blue.shade400,
202 /// )
203 /// ```
204 /// {@end-tool}
205 final Color? color;
206
207 /// A list of [Shadow]s that will be painted underneath the icon.
208 ///
209 /// Multiple shadows are supported to replicate lighting from multiple light
210 /// sources.
211 ///
212 /// Shadows must be in the same order for [Icon] to be considered as
213 /// equivalent as order produces differing transparency.
214 ///
215 /// Defaults to the nearest [IconTheme]'s [IconThemeData.shadows].
216 final List<Shadow>? shadows;
217
218 /// Semantic label for the icon.
219 ///
220 /// Announced by assistive technologies (e.g TalkBack/VoiceOver).
221 /// This label does not show in the UI.
222 ///
223 /// * [SemanticsProperties.label], which is set to [semanticLabel] in the
224 /// underlying [Semantics] widget.
225 final String? semanticLabel;
226
227 /// The text direction to use for rendering the icon.
228 ///
229 /// If this is null, the ambient [Directionality] is used instead.
230 ///
231 /// Some icons follow the reading direction. For example, "back" buttons point
232 /// left in left-to-right environments and right in right-to-left
233 /// environments. Such icons have their [IconData.matchTextDirection] field
234 /// set to true, and the [Icon] widget uses the [textDirection] to determine
235 /// the orientation in which to draw the icon.
236 ///
237 /// This property has no effect if the [icon]'s [IconData.matchTextDirection]
238 /// field is false, but for consistency a text direction value must always be
239 /// specified, either directly using this property or using [Directionality].
240 final TextDirection? textDirection;
241
242 /// Whether to scale the size of this widget using the ambient [MediaQuery]'s [TextScaler].
243 ///
244 /// This is specially useful when you have an icon associated with a text, as
245 /// scaling the text without scaling the icon would result in a confusing
246 /// interface.
247 ///
248 /// Defaults to the nearest [IconTheme]'s
249 /// [IconThemeData.applyTextScaling].
250 final bool? applyTextScaling;
251
252 /// The [BlendMode] to apply to the foreground of the icon.
253 ///
254 /// Defaults to [BlendMode.srcOver]
255 final BlendMode? blendMode;
256
257 /// The typeface thickness to use when painting the text (e.g., bold).
258 final FontWeight? fontWeight;
259
260 @override
261 Widget build(BuildContext context) {
262 assert(this.textDirection != null || debugCheckHasDirectionality(context));
263 final TextDirection textDirection = this.textDirection ?? Directionality.of(context);
264
265 final IconThemeData iconTheme = IconTheme.of(context);
266
267 final bool applyTextScaling = this.applyTextScaling ?? iconTheme.applyTextScaling ?? false;
268
269 final double tentativeIconSize = size ?? iconTheme.size ?? kDefaultFontSize;
270
271 final double iconSize = applyTextScaling
272 ? MediaQuery.textScalerOf(context).scale(tentativeIconSize)
273 : tentativeIconSize;
274
275 final double? iconFill = fill ?? iconTheme.fill;
276
277 final double? iconWeight = weight ?? iconTheme.weight;
278
279 final double? iconGrade = grade ?? iconTheme.grade;
280
281 final double? iconOpticalSize = opticalSize ?? iconTheme.opticalSize;
282
283 final List<Shadow>? iconShadows = shadows ?? iconTheme.shadows;
284
285 final IconData? icon = this.icon;
286 if (icon == null) {
287 return Semantics(
288 label: semanticLabel,
289 child: SizedBox(width: iconSize, height: iconSize),
290 );
291 }
292
293 final double iconOpacity = iconTheme.opacity ?? 1.0;
294 Color? iconColor = color ?? iconTheme.color!;
295 Paint? foreground;
296 if (iconOpacity != 1.0) {
297 iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity);
298 }
299 if (blendMode != null) {
300 foreground = Paint()
301 ..blendMode = blendMode!
302 ..color = iconColor;
303 // Cannot provide both a color and a foreground.
304 iconColor = null;
305 }
306
307 final TextStyle fontStyle = TextStyle(
308 fontVariations: <FontVariation>[
309 if (iconFill != null) FontVariation('FILL', iconFill),
310 if (iconWeight != null) FontVariation('wght', iconWeight),
311 if (iconGrade != null) FontVariation('GRAD', iconGrade),
312 if (iconOpticalSize != null) FontVariation('opsz', iconOpticalSize),
313 ],
314 inherit: false,
315 color: iconColor,
316 fontSize: iconSize,
317 fontFamily: icon.fontFamily,
318 fontWeight: fontWeight,
319 package: icon.fontPackage,
320 fontFamilyFallback: icon.fontFamilyFallback,
321 shadows: iconShadows,
322 height:
323 1.0, // Makes sure the font's body is vertically centered within the iconSize x iconSize square.
324 leadingDistribution: TextLeadingDistribution.even,
325 foreground: foreground,
326 );
327
328 Widget iconWidget = RichText(
329 overflow: TextOverflow.visible, // Never clip.
330 textDirection: textDirection, // Since we already fetched it for the assert...
331 text: TextSpan(text: String.fromCharCode(icon.codePoint), style: fontStyle),
332 );
333
334 if (icon.matchTextDirection) {
335 switch (textDirection) {
336 case TextDirection.rtl:
337 iconWidget = Transform(
338 transform: Matrix4.identity()..scaleByDouble(-1.0, 1.0, 1.0, 1),
339 alignment: Alignment.center,
340 transformHitTests: false,
341 child: iconWidget,
342 );
343 case TextDirection.ltr:
344 break;
345 }
346 }
347
348 return Semantics(
349 label: semanticLabel,
350 child: ExcludeSemantics(
351 child: SizedBox(
352 width: iconSize,
353 height: iconSize,
354 child: Center(child: iconWidget),
355 ),
356 ),
357 );
358 }
359
360 @override
361 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
362 super.debugFillProperties(properties);
363 properties.add(IconDataProperty('icon', icon, ifNull: '<empty>', showName: false));
364 properties.add(DoubleProperty('size', size, defaultValue: null));
365 properties.add(DoubleProperty('fill', fill, defaultValue: null));
366 properties.add(DoubleProperty('weight', weight, defaultValue: null));
367 properties.add(DoubleProperty('grade', grade, defaultValue: null));
368 properties.add(DoubleProperty('opticalSize', opticalSize, defaultValue: null));
369 properties.add(ColorProperty('color', color, defaultValue: null));
370 properties.add(IterableProperty<Shadow>('shadows', shadows, defaultValue: null));
371 properties.add(StringProperty('semanticLabel', semanticLabel, defaultValue: null));
372 properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
373 properties.add(
374 DiagnosticsProperty<bool>('applyTextScaling', applyTextScaling, defaultValue: null),
375 );
376 }
377}
378

Provided by KDAB

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