navigation_rail_theme.dart 11.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
// 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' show lerpDouble;

import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

import 'navigation_rail.dart';
import 'theme.dart';

14 15 16
// Examples can assume:
// late BuildContext context;

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/// Defines default property values for descendant [NavigationRail]
/// widgets.
///
/// Descendant widgets obtain the current [NavigationRailThemeData] object
/// using `NavigationRailTheme.of(context)`. Instances of
/// [NavigationRailThemeData] can be customized with
/// [NavigationRailThemeData.copyWith].
///
/// Typically a [NavigationRailThemeData] is specified as part of the
/// overall [Theme] with [ThemeData.navigationRailTheme].
///
/// All [NavigationRailThemeData] properties are `null` by default.
/// When null, the [NavigationRail] will use the values from [ThemeData]
/// if they exist, otherwise it will provide its own defaults based on the
/// overall [Theme]'s textTheme and colorScheme. See the individual
/// [NavigationRail] properties for details.
///
/// See also:
///
///  * [ThemeData], which describes the overall theme information for the
///    application.
38
@immutable
39 40 41 42 43 44 45 46 47 48 49
class NavigationRailThemeData with Diagnosticable {
  /// Creates a theme that can be used for [ThemeData.navigationRailTheme].
  const NavigationRailThemeData({
    this.backgroundColor,
    this.elevation,
    this.unselectedLabelTextStyle,
    this.selectedLabelTextStyle,
    this.unselectedIconTheme,
    this.selectedIconTheme,
    this.groupAlignment,
    this.labelType,
50 51
    this.useIndicator,
    this.indicatorColor,
52
    this.indicatorShape,
53 54
    this.minWidth,
    this.minExtendedWidth,
55 56 57
  });

  /// Color to be used for the [NavigationRail]'s background.
58
  final Color? backgroundColor;
59 60

  /// The z-coordinate to be used for the [NavigationRail]'s elevation.
61
  final double? elevation;
62 63 64

  /// The style to merge with the default text style for
  /// [NavigationRailDestination] labels, when the destination is not selected.
65
  final TextStyle? unselectedLabelTextStyle;
66 67 68

  /// The style to merge with the default text style for
  /// [NavigationRailDestination] labels, when the destination is selected.
69
  final TextStyle? selectedLabelTextStyle;
70 71 72

  /// The theme to merge with the default icon theme for
  /// [NavigationRailDestination] icons, when the destination is not selected.
73
  final IconThemeData? unselectedIconTheme;
74 75 76

  /// The theme to merge with the default icon theme for
  /// [NavigationRailDestination] icons, when the destination is selected.
77
  final IconThemeData? selectedIconTheme;
78 79 80

  /// The alignment for the [NavigationRailDestination]s as they are positioned
  /// within the [NavigationRail].
81
  final double? groupAlignment;
82 83 84

  /// The type that defines the layout and behavior of the labels in the
  /// [NavigationRail].
85
  final NavigationRailLabelType? labelType;
86

87 88 89 90 91 92 93 94
  /// Whether or not the selected [NavigationRailDestination] should include a
  /// [NavigationIndicator].
  final bool? useIndicator;

  /// Overrides the default value of [NavigationRail]'s selection indicator color,
  /// when [useIndicator] is true.
  final Color? indicatorColor;

95 96 97
  /// Overrides the default shape of the [NavigationRail]'s selection indicator.
  final ShapeBorder? indicatorShape;

98 99 100 101 102 103 104 105
  /// Overrides the default value of [NavigationRail]'s minimum width when it
  /// is not extended.
  final double? minWidth;

  /// Overrides the default value of [NavigationRail]'s minimum width when it
  /// is extended.
  final double? minExtendedWidth;

106 107 108
  /// Creates a copy of this object with the given fields replaced with the
  /// new values.
  NavigationRailThemeData copyWith({
109 110 111 112 113 114 115 116
    Color? backgroundColor,
    double? elevation,
    TextStyle? unselectedLabelTextStyle,
    TextStyle? selectedLabelTextStyle,
    IconThemeData? unselectedIconTheme,
    IconThemeData? selectedIconTheme,
    double? groupAlignment,
    NavigationRailLabelType? labelType,
117 118
    bool? useIndicator,
    Color? indicatorColor,
119
    ShapeBorder? indicatorShape,
120 121
    double? minWidth,
    double? minExtendedWidth,
122 123 124 125 126 127 128 129 130 131
  }) {
    return NavigationRailThemeData(
      backgroundColor: backgroundColor ?? this.backgroundColor,
      elevation: elevation ?? this.elevation,
      unselectedLabelTextStyle: unselectedLabelTextStyle ?? this.unselectedLabelTextStyle,
      selectedLabelTextStyle: selectedLabelTextStyle ?? this.selectedLabelTextStyle,
      unselectedIconTheme: unselectedIconTheme ?? this.unselectedIconTheme,
      selectedIconTheme: selectedIconTheme ?? this.selectedIconTheme,
      groupAlignment: groupAlignment ?? this.groupAlignment,
      labelType: labelType ?? this.labelType,
132 133
      useIndicator: useIndicator ?? this.useIndicator,
      indicatorColor: indicatorColor ?? this.indicatorColor,
134
      indicatorShape: indicatorShape ?? this.indicatorShape,
135 136
      minWidth: minWidth ?? this.minWidth,
      minExtendedWidth: minExtendedWidth ?? this.minExtendedWidth,
137 138 139 140 141 142 143 144
    );
  }

  /// Linearly interpolate between two navigation rail themes.
  ///
  /// If both arguments are null then null is returned.
  ///
  /// {@macro dart.ui.shadow.lerp}
145
  static NavigationRailThemeData? lerp(NavigationRailThemeData? a, NavigationRailThemeData? b, double t) {
146 147
    if (identical(a, b)) {
      return a;
148
    }
149 150 151 152 153
    return NavigationRailThemeData(
      backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
      elevation: lerpDouble(a?.elevation, b?.elevation, t),
      unselectedLabelTextStyle: TextStyle.lerp(a?.unselectedLabelTextStyle, b?.unselectedLabelTextStyle, t),
      selectedLabelTextStyle: TextStyle.lerp(a?.selectedLabelTextStyle, b?.selectedLabelTextStyle, t),
154 155 156 157
      unselectedIconTheme: a?.unselectedIconTheme == null && b?.unselectedIconTheme == null
        ? null : IconThemeData.lerp(a?.unselectedIconTheme, b?.unselectedIconTheme, t),
      selectedIconTheme: a?.selectedIconTheme == null && b?.selectedIconTheme == null
        ? null : IconThemeData.lerp(a?.selectedIconTheme, b?.selectedIconTheme, t),
158 159
      groupAlignment: lerpDouble(a?.groupAlignment, b?.groupAlignment, t),
      labelType: t < 0.5 ? a?.labelType : b?.labelType,
160 161
      useIndicator: t < 0.5 ? a?.useIndicator : b?.useIndicator,
      indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
162
      indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t),
163 164 165
      minWidth: lerpDouble(a?.minWidth, b?.minWidth, t),
      minExtendedWidth: lerpDouble(a?.minExtendedWidth, b?.minExtendedWidth, t),

166 167 168 169
    );
  }

  @override
170 171 172 173 174 175 176 177 178 179 180
  int get hashCode => Object.hash(
    backgroundColor,
    elevation,
    unselectedLabelTextStyle,
    selectedLabelTextStyle,
    unselectedIconTheme,
    selectedIconTheme,
    groupAlignment,
    labelType,
    useIndicator,
    indicatorColor,
181
    indicatorShape,
182 183
    minWidth,
    minExtendedWidth,
184
  );
185 186 187

  @override
  bool operator ==(Object other) {
188
    if (identical(this, other)) {
189
      return true;
190 191
    }
    if (other.runtimeType != runtimeType) {
192
      return false;
193
    }
194 195 196 197 198 199 200 201
    return other is NavigationRailThemeData
        && other.backgroundColor == backgroundColor
        && other.elevation == elevation
        && other.unselectedLabelTextStyle == unselectedLabelTextStyle
        && other.selectedLabelTextStyle == selectedLabelTextStyle
        && other.unselectedIconTheme == unselectedIconTheme
        && other.selectedIconTheme == selectedIconTheme
        && other.groupAlignment == groupAlignment
202 203
        && other.labelType == labelType
        && other.useIndicator == useIndicator
204
        && other.indicatorColor == indicatorColor
205
        && other.indicatorShape == indicatorShape
206 207
        && other.minWidth == minWidth
        && other.minExtendedWidth == minExtendedWidth;
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    const NavigationRailThemeData defaultData = NavigationRailThemeData();

    properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor));
    properties.add(DoubleProperty('elevation', elevation, defaultValue: defaultData.elevation));
    properties.add(DiagnosticsProperty<TextStyle>('unselectedLabelTextStyle', unselectedLabelTextStyle, defaultValue: defaultData.unselectedLabelTextStyle));
    properties.add(DiagnosticsProperty<TextStyle>('selectedLabelTextStyle', selectedLabelTextStyle, defaultValue: defaultData.selectedLabelTextStyle));
    properties.add(DiagnosticsProperty<IconThemeData>('unselectedIconTheme', unselectedIconTheme, defaultValue: defaultData.unselectedIconTheme));
    properties.add(DiagnosticsProperty<IconThemeData>('selectedIconTheme', selectedIconTheme, defaultValue: defaultData.selectedIconTheme));
    properties.add(DoubleProperty('groupAlignment', groupAlignment, defaultValue: defaultData.groupAlignment));
    properties.add(DiagnosticsProperty<NavigationRailLabelType>('labelType', labelType, defaultValue: defaultData.labelType));
223 224
    properties.add(DiagnosticsProperty<bool>('useIndicator', useIndicator, defaultValue: defaultData.useIndicator));
    properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: defaultData.indicatorColor));
225
    properties.add(DiagnosticsProperty<ShapeBorder>('indicatorShape', indicatorShape, defaultValue: null));
226 227
    properties.add(DoubleProperty('minWidth', minWidth, defaultValue: defaultData.minWidth));
    properties.add(DoubleProperty('minExtendedWidth', minExtendedWidth, defaultValue: defaultData.minExtendedWidth));
228 229 230 231 232 233 234 235 236 237 238 239 240 241
  }
}

/// An inherited widget that defines visual properties for [NavigationRail]s and
/// [NavigationRailDestination]s in this widget's subtree.
///
/// Values specified here are used for [NavigationRail] properties that are not
/// given an explicit non-null value.
class NavigationRailTheme extends InheritedTheme {
  /// Creates a navigation rail theme that controls the
  /// [NavigationRailThemeData] properties for a [NavigationRail].
  ///
  /// The data argument must not be null.
  const NavigationRailTheme({
242
    super.key,
243
    required this.data,
244
    required super.child,
245
  });
246 247 248 249 250 251 252 253 254 255 256 257 258 259

  /// Specifies the background color, elevation, label text style, icon theme,
  /// group alignment, and label type and border values for descendant
  /// [NavigationRail] widgets.
  final NavigationRailThemeData data;

  /// The closest instance of this class that encloses the given context.
  ///
  /// If there is no enclosing [NavigationRailTheme] widget, then
  /// [ThemeData.navigationRailTheme] is used.
  ///
  /// Typical usage is as follows:
  ///
  /// ```dart
260
  /// NavigationRailThemeData theme = NavigationRailTheme.of(context);
261 262
  /// ```
  static NavigationRailThemeData of(BuildContext context) {
263
    final NavigationRailTheme? navigationRailTheme = context.dependOnInheritedWidgetOfExactType<NavigationRailTheme>();
264
    return navigationRailTheme?.data ?? Theme.of(context).navigationRailTheme;
265 266 267 268
  }

  @override
  Widget wrap(BuildContext context, Widget child) {
269
    return NavigationRailTheme(data: data, child: child);
270 271 272 273 274
  }

  @override
  bool updateShouldNotify(NavigationRailTheme oldWidget) => data != oldWidget.data;
}