// 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' as ui show lerpDouble; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'framework.dart' show BuildContext; /// Defines the size, font variations, color, opacity, and shadows of icons. /// /// Used by [IconTheme] to control those properties in a widget subtree. /// /// To obtain the current icon theme, use [IconTheme.of]. To convert an icon /// theme to a version with all the fields filled in, use /// [IconThemeData.fallback]. @immutable class IconThemeData with Diagnosticable { /// Creates an icon theme data. /// /// The opacity applies to both explicit and default icon colors. The value /// is clamped between 0.0 and 1.0. const IconThemeData({ this.size, this.fill, this.weight, this.grade, this.opticalSize, this.color, double? opacity, this.shadows, }) : _opacity = opacity, assert(fill == null || (0.0 <= fill && fill <= 1.0)), assert(weight == null || (0.0 < weight)), assert(opticalSize == null || (0.0 < opticalSize)); /// Creates an icon theme with some reasonable default values. /// /// The [size] is 24.0, [fill] is 0.0, [weight] is 400.0, [grade] is 0.0, /// opticalSize is 48.0, [color] is black, and [opacity] is 1.0. const IconThemeData.fallback() : size = 24.0, fill = 0.0, weight = 400.0, grade = 0.0, opticalSize = 48.0, color = const Color(0xFF000000), _opacity = 1.0, shadows = null; /// Creates a copy of this icon theme but with the given fields replaced with /// the new values. IconThemeData copyWith({ double? size, double? fill, double? weight, double? grade, double? opticalSize, Color? color, double? opacity, List<Shadow>? shadows, }) { return IconThemeData( size: size ?? this.size, fill: fill ?? this.fill, weight: weight ?? this.weight, grade: grade ?? this.grade, opticalSize: opticalSize ?? this.opticalSize, color: color ?? this.color, opacity: opacity ?? this.opacity, shadows: shadows ?? this.shadows, ); } /// Returns a new icon theme that matches this icon theme but with some values /// replaced by the non-null parameters of the given icon theme. If the given /// icon theme is null, simply returns this icon theme. IconThemeData merge(IconThemeData? other) { if (other == null) { return this; } return copyWith( size: other.size, fill: other.fill, weight: other.weight, grade: other.grade, opticalSize: other.opticalSize, color: other.color, opacity: other.opacity, shadows: other.shadows, ); } /// Called by [IconTheme.of] to convert this instance to an [IconThemeData] /// that fits the given [BuildContext]. /// /// This method gives the ambient [IconThemeData] a chance to update itself, /// after it's been retrieved by [IconTheme.of], and before being returned as /// the final result. For instance, [CupertinoIconThemeData] overrides this method /// to resolve [color], in case [color] is a [CupertinoDynamicColor] and needs /// to be resolved against the given [BuildContext] before it can be used as a /// regular [Color]. /// /// The default implementation returns this [IconThemeData] as-is. /// /// See also: /// /// * [CupertinoIconThemeData.resolve] an implementation that resolves /// the color of [CupertinoIconThemeData] before returning. IconThemeData resolve(BuildContext context) => this; /// Whether all the properties (except shadows) of this object are non-null. bool get isConcrete => size != null && fill != null && weight != null && grade != null && opticalSize != null && color != null && opacity != null; /// The default for [Icon.size]. /// /// Falls back to 24.0. final double? size; /// The default for [Icon.fill]. /// /// Falls back to 0.0. final double? fill; /// The default for [Icon.weight]. /// /// Falls back to 400.0. final double? weight; /// The default for [Icon.grade]. /// /// Falls back to 0.0. final double? grade; /// The default for [Icon.opticalSize]. /// /// Falls back to 48.0. final double? opticalSize; /// The default for [Icon.color]. /// /// In material apps, if there is a [Theme] without any [IconTheme]s /// specified, icon colors default to white if [ThemeData.brightness] is dark /// and black if [ThemeData.brightness] is light. /// /// Otherwise, falls back to black. final Color? color; /// An opacity to apply to both explicit and default icon colors. /// /// Falls back to 1.0. double? get opacity => _opacity == null ? null : clampDouble(_opacity!, 0.0, 1.0); final double? _opacity; /// The default for [Icon.shadows]. final List<Shadow>? shadows; /// Linearly interpolate between two icon theme data objects. /// /// {@macro dart.ui.shadow.lerp} static IconThemeData lerp(IconThemeData? a, IconThemeData? b, double t) { assert(t != null); return IconThemeData( size: ui.lerpDouble(a?.size, b?.size, t), fill: ui.lerpDouble(a?.fill, b?.fill, t), weight: ui.lerpDouble(a?.weight, b?.weight, t), grade: ui.lerpDouble(a?.grade, b?.grade, t), opticalSize: ui.lerpDouble(a?.opticalSize, b?.opticalSize, t), color: Color.lerp(a?.color, b?.color, t), opacity: ui.lerpDouble(a?.opacity, b?.opacity, t), shadows: Shadow.lerpList(a?.shadows, b?.shadows, t), ); } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is IconThemeData && other.size == size && other.fill == fill && other.weight == weight && other.grade == grade && other.opticalSize == opticalSize && other.color == color && other.opacity == opacity && listEquals(other.shadows, shadows); } @override int get hashCode => Object.hash( size, fill, weight, grade, opticalSize, color, opacity, shadows == null ? null : Object.hashAll(shadows!), ); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); 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(DoubleProperty('opacity', opacity, defaultValue: null)); properties.add(IterableProperty<Shadow>('shadows', shadows, defaultValue: null)); } }