| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:ui'; |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/rendering.dart'; |
| |
| import 'basic.dart'; |
| import 'debug.dart'; |
| import 'framework.dart'; |
| import 'icon_data.dart'; |
| import 'icon_theme.dart'; |
| import 'icon_theme_data.dart'; |
| |
| /// A graphical icon widget drawn with a glyph from a font described in |
| /// an [IconData] such as material's predefined [IconData]s in [Icons]. |
| /// |
| /// Icons are not interactive. For an interactive icon, consider material's |
| /// [IconButton]. |
| /// |
| /// There must be an ambient [Directionality] widget when using [Icon]. |
| /// Typically this is introduced automatically by the [WidgetsApp] or |
| /// [MaterialApp]. |
| /// |
| /// This widget assumes that the rendered icon is squared. Non-squared icons may |
| /// render incorrectly. |
| /// |
| /// {@tool snippet} |
| /// |
| /// This example shows how to create a [Row] of [Icon]s in different colors and |
| /// sizes. The first [Icon] uses a [semanticLabel] to announce in accessibility |
| /// modes like TalkBack and VoiceOver. |
| /// |
| /// ![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) |
| /// |
| /// ```dart |
| /// Row( |
| /// mainAxisAlignment: MainAxisAlignment.spaceAround, |
| /// children: const <Widget>[ |
| /// Icon( |
| /// Icons.favorite, |
| /// color: Colors.pink, |
| /// size: 24.0, |
| /// semanticLabel: 'Text to announce in accessibility modes', |
| /// ), |
| /// Icon( |
| /// Icons.audiotrack, |
| /// color: Colors.green, |
| /// size: 30.0, |
| /// ), |
| /// Icon( |
| /// Icons.beach_access, |
| /// color: Colors.blue, |
| /// size: 36.0, |
| /// ), |
| /// ], |
| /// ) |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// See also: |
| /// |
| /// * [IconButton], for interactive icons. |
| /// * [Icons], for the list of available Material Icons for use with this class. |
| /// * [IconTheme], which provides ambient configuration for icons. |
| /// * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s. |
| class Icon extends StatelessWidget { |
| /// Creates an icon. |
| const Icon( |
| this.icon, { |
| super.key, |
| this.size, |
| this.fill, |
| this.weight, |
| this.grade, |
| this.opticalSize, |
| this.color, |
| this.shadows, |
| this.semanticLabel, |
| this.textDirection, |
| }) : assert(fill == null || (0.0 <= fill && fill <= 1.0)), |
| assert(weight == null || (0.0 < weight)), |
| assert(opticalSize == null || (0.0 < opticalSize)); |
| |
| /// The icon to display. The available icons are described in [Icons]. |
| /// |
| /// The icon can be null, in which case the widget will render as an empty |
| /// space of the specified [size]. |
| final IconData? icon; |
| |
| /// The size of the icon in logical pixels. |
| /// |
| /// Icons occupy a square with width and height equal to size. |
| /// |
| /// Defaults to the nearest [IconTheme]'s [IconThemeData.size]. |
| /// |
| /// If this [Icon] is being placed inside an [IconButton], then use |
| /// [IconButton.iconSize] instead, so that the [IconButton] can make the splash |
| /// area the appropriate size as well. The [IconButton] uses an [IconTheme] to |
| /// pass down the size to the [Icon]. |
| final double? size; |
| |
| /// The fill for drawing the icon. |
| /// |
| /// Requires the underlying icon font to support the `FILL` [FontVariation] |
| /// axis, otherwise has no effect. Variable font filenames often indicate |
| /// the supported axes. Must be between 0.0 (unfilled) and 1.0 (filled), |
| /// inclusive. |
| /// |
| /// Can be used to convey a state transition for animation or interaction. |
| /// |
| /// Defaults to nearest [IconTheme]'s [IconThemeData.fill]. |
| /// |
| /// See also: |
| /// * [weight], for controlling stroke weight. |
| /// * [grade], for controlling stroke weight in a more granular way. |
| /// * [opticalSize], for controlling optical size. |
| final double? fill; |
| |
| /// The stroke weight for drawing the icon. |
| /// |
| /// Requires the underlying icon font to support the `wght` [FontVariation] |
| /// axis, otherwise has no effect. Variable font filenames often indicate |
| /// the supported axes. Must be greater than 0. |
| /// |
| /// Defaults to nearest [IconTheme]'s [IconThemeData.weight]. |
| /// |
| /// See also: |
| /// * [fill], for controlling fill. |
| /// * [grade], for controlling stroke weight in a more granular way. |
| /// * [opticalSize], for controlling optical size. |
| /// * https://fonts.google.com/knowledge/glossary/weight_axis |
| final double? weight; |
| |
| /// The grade (granular stroke weight) for drawing the icon. |
| /// |
| /// Requires the underlying icon font to support the `GRAD` [FontVariation] |
| /// axis, otherwise has no effect. Variable font filenames often indicate |
| /// the supported axes. Can be negative. |
| /// |
| /// Grade and [weight] both affect a symbol's stroke weight (thickness), but |
| /// grade has a smaller impact on the size of the symbol. |
| /// |
| /// Grade is also available in some text fonts. One can match grade levels |
| /// between text and symbols for a harmonious visual effect. For example, if |
| /// the text font has a -25 grade value, the symbols can match it with a |
| /// suitable value, say -25. |
| /// |
| /// Defaults to nearest [IconTheme]'s [IconThemeData.grade]. |
| /// |
| /// See also: |
| /// * [fill], for controlling fill. |
| /// * [weight], for controlling stroke weight in a less granular way. |
| /// * [opticalSize], for controlling optical size. |
| /// * https://fonts.google.com/knowledge/glossary/grade_axis |
| final double? grade; |
| |
| /// The optical size for drawing the icon. |
| /// |
| /// Requires the underlying icon font to support the `opsz` [FontVariation] |
| /// axis, otherwise has no effect. Variable font filenames often indicate |
| /// the supported axes. Must be greater than 0. |
| /// |
| /// For an icon to look the same at different sizes, the stroke weight |
| /// (thickness) must change as the icon size scales. Optical size offers a way |
| /// to automatically adjust the stroke weight as icon size changes. |
| /// |
| /// Defaults to nearest [IconTheme]'s [IconThemeData.opticalSize]. |
| /// |
| /// See also: |
| /// * [fill], for controlling fill. |
| /// * [weight], for controlling stroke weight. |
| /// * [grade], for controlling stroke weight in a more granular way. |
| /// * https://fonts.google.com/knowledge/glossary/optical_size_axis |
| final double? opticalSize; |
| |
| /// The color to use when drawing the icon. |
| /// |
| /// Defaults to the nearest [IconTheme]'s [IconThemeData.color]. |
| /// |
| /// The color (whether specified explicitly here or obtained from the |
| /// [IconTheme]) will be further adjusted by the nearest [IconTheme]'s |
| /// [IconThemeData.opacity]. |
| /// |
| /// {@tool snippet} |
| /// Typically, a Material Design color will be used, as follows: |
| /// |
| /// ```dart |
| /// Icon( |
| /// Icons.widgets, |
| /// color: Colors.blue.shade400, |
| /// ) |
| /// ``` |
| /// {@end-tool} |
| final Color? color; |
| |
| /// A list of [Shadow]s that will be painted underneath the icon. |
| /// |
| /// Multiple shadows are supported to replicate lighting from multiple light |
| /// sources. |
| /// |
| /// Shadows must be in the same order for [Icon] to be considered as |
| /// equivalent as order produces differing transparency. |
| /// |
| /// Defaults to the nearest [IconTheme]'s [IconThemeData.shadows]. |
| final List<Shadow>? shadows; |
| |
| /// Semantic label for the icon. |
| /// |
| /// Announced in accessibility modes (e.g TalkBack/VoiceOver). |
| /// This label does not show in the UI. |
| /// |
| /// * [SemanticsProperties.label], which is set to [semanticLabel] in the |
| /// underlying [Semantics] widget. |
| final String? semanticLabel; |
| |
| /// The text direction to use for rendering the icon. |
| /// |
| /// If this is null, the ambient [Directionality] is used instead. |
| /// |
| /// Some icons follow the reading direction. For example, "back" buttons point |
| /// left in left-to-right environments and right in right-to-left |
| /// environments. Such icons have their [IconData.matchTextDirection] field |
| /// set to true, and the [Icon] widget uses the [textDirection] to determine |
| /// the orientation in which to draw the icon. |
| /// |
| /// This property has no effect if the [icon]'s [IconData.matchTextDirection] |
| /// field is false, but for consistency a text direction value must always be |
| /// specified, either directly using this property or using [Directionality]. |
| final TextDirection? textDirection; |
| |
| @override |
| Widget build(BuildContext context) { |
| assert(this.textDirection != null || debugCheckHasDirectionality(context)); |
| final TextDirection textDirection = this.textDirection ?? Directionality.of(context); |
| |
| final IconThemeData iconTheme = IconTheme.of(context); |
| |
| final double? iconSize = size ?? iconTheme.size; |
| |
| final double? iconFill = fill ?? iconTheme.fill; |
| |
| final double? iconWeight = weight ?? iconTheme.weight; |
| |
| final double? iconGrade = grade ?? iconTheme.grade; |
| |
| final double? iconOpticalSize = opticalSize ?? iconTheme.opticalSize; |
| |
| final List<Shadow>? iconShadows = shadows ?? iconTheme.shadows; |
| |
| if (icon == null) { |
| return Semantics( |
| label: semanticLabel, |
| child: SizedBox(width: iconSize, height: iconSize), |
| ); |
| } |
| |
| final double iconOpacity = iconTheme.opacity ?? 1.0; |
| Color iconColor = color ?? iconTheme.color!; |
| if (iconOpacity != 1.0) { |
| iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity); |
| } |
| |
| Widget iconWidget = RichText( |
| overflow: TextOverflow.visible, // Never clip. |
| textDirection: textDirection, // Since we already fetched it for the assert... |
| text: TextSpan( |
| text: String.fromCharCode(icon!.codePoint), |
| style: TextStyle( |
| fontVariations: <FontVariation>[ |
| if (iconFill != null) FontVariation('FILL', iconFill), |
| if (iconWeight != null) FontVariation('wght', iconWeight), |
| if (iconGrade != null) FontVariation('GRAD', iconGrade), |
| if (iconOpticalSize != null) FontVariation('opsz', iconOpticalSize), |
| ], |
| inherit: false, |
| color: iconColor, |
| fontSize: iconSize, |
| fontFamily: icon!.fontFamily, |
| package: icon!.fontPackage, |
| shadows: iconShadows, |
| ), |
| ), |
| ); |
| |
| if (icon!.matchTextDirection) { |
| switch (textDirection) { |
| case TextDirection.rtl: |
| iconWidget = Transform( |
| transform: Matrix4.identity()..scale(-1.0, 1.0, 1.0), |
| alignment: Alignment.center, |
| transformHitTests: false, |
| child: iconWidget, |
| ); |
| break; |
| case TextDirection.ltr: |
| break; |
| } |
| } |
| |
| return Semantics( |
| label: semanticLabel, |
| child: ExcludeSemantics( |
| child: SizedBox( |
| width: iconSize, |
| height: iconSize, |
| child: Center( |
| child: iconWidget, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| properties.add(IconDataProperty('icon', icon, ifNull: '<empty>', showName: false)); |
| properties.add(DoubleProperty('size', size, defaultValue: null)); |
| properties.add(DoubleProperty('fill', fill, defaultValue: null)); |
| properties.add(DoubleProperty('weight', weight, defaultValue: null)); |
| properties.add(DoubleProperty('grade', grade, defaultValue: null)); |
| properties.add(DoubleProperty('opticalSize', opticalSize, defaultValue: null)); |
| properties.add(ColorProperty('color', color, defaultValue: null)); |
| properties.add(IterableProperty<Shadow>('shadows', shadows, defaultValue: null)); |
| properties.add(StringProperty('semanticLabel', semanticLabel, defaultValue: null)); |
| properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null)); |
| } |
| } |