// 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/widgets.dart'; import 'bottom_navigation_bar.dart'; import 'material_state.dart'; import 'theme.dart'; // Examples can assume: // late BuildContext context; /// Defines default property values for descendant [BottomNavigationBar] /// widgets. /// /// Descendant widgets obtain the current [BottomNavigationBarThemeData] object /// using `BottomNavigationBarTheme.of(context)`. Instances of /// [BottomNavigationBarThemeData] can be customized with /// [BottomNavigationBarThemeData.copyWith]. /// /// Typically a [BottomNavigationBarThemeData] is specified as part of the /// overall [Theme] with [ThemeData.bottomNavigationBarTheme]. /// /// All [BottomNavigationBarThemeData] properties are `null` by default. When /// null, the [BottomNavigationBar]'s build method provides defaults. /// /// See also: /// /// * [ThemeData], which describes the overall theme information for the /// application. @immutable class BottomNavigationBarThemeData with Diagnosticable { /// Creates a theme that can be used for [ThemeData.bottomNavigationBarTheme]. const BottomNavigationBarThemeData({ this.backgroundColor, this.elevation, this.selectedIconTheme, this.unselectedIconTheme, this.selectedItemColor, this.unselectedItemColor, this.selectedLabelStyle, this.unselectedLabelStyle, this.showSelectedLabels, this.showUnselectedLabels, this.type, this.enableFeedback, this.landscapeLayout, this.mouseCursor, }); /// The color of the [BottomNavigationBar] itself. /// /// See [BottomNavigationBar.backgroundColor]. final Color? backgroundColor; /// The z-coordinate of the [BottomNavigationBar]. /// /// See [BottomNavigationBar.elevation]. final double? elevation; /// The size, opacity, and color of the icon in the currently selected /// [BottomNavigationBarItem.icon]. /// /// If [BottomNavigationBar.selectedIconTheme] is non-null on the widget, /// the whole [IconThemeData] from the widget will be used over this /// [selectedIconTheme]. /// /// See [BottomNavigationBar.selectedIconTheme]. final IconThemeData? selectedIconTheme; /// The size, opacity, and color of the icon in the currently unselected /// [BottomNavigationBarItem.icon]s. /// /// If [BottomNavigationBar.unselectedIconTheme] is non-null on the widget, /// the whole [IconThemeData] from the widget will be used over this /// [unselectedIconTheme]. /// /// See [BottomNavigationBar.unselectedIconTheme]. final IconThemeData? unselectedIconTheme; /// The color of the selected [BottomNavigationBarItem.icon] and /// [BottomNavigationBarItem.label]. /// /// See [BottomNavigationBar.selectedItemColor]. final Color? selectedItemColor; /// The color of the unselected [BottomNavigationBarItem.icon] and /// [BottomNavigationBarItem.label]s. /// /// See [BottomNavigationBar.unselectedItemColor]. final Color? unselectedItemColor; /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are /// selected. /// /// See [BottomNavigationBar.selectedLabelStyle]. final TextStyle? selectedLabelStyle; /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not /// selected. /// /// See [BottomNavigationBar.unselectedLabelStyle]. final TextStyle? unselectedLabelStyle; /// Whether the labels are shown for the selected [BottomNavigationBarItem]. /// /// See [BottomNavigationBar.showSelectedLabels]. final bool? showSelectedLabels; /// Whether the labels are shown for the unselected [BottomNavigationBarItem]s. /// /// See [BottomNavigationBar.showUnselectedLabels]. final bool? showUnselectedLabels; /// Defines the layout and behavior of a [BottomNavigationBar]. /// /// See [BottomNavigationBar.type]. final BottomNavigationBarType? type; /// If specified, defines the feedback property for [BottomNavigationBar]. /// /// If [BottomNavigationBar.enableFeedback] is provided, [enableFeedback] is ignored. final bool? enableFeedback; /// If non-null, overrides the [BottomNavigationBar.landscapeLayout] property. final BottomNavigationBarLandscapeLayout? landscapeLayout; /// If specified, overrides the default value of [BottomNavigationBar.mouseCursor]. final MaterialStateProperty<MouseCursor?>? mouseCursor; /// Creates a copy of this object but with the given fields replaced with the /// new values. BottomNavigationBarThemeData copyWith({ Color? backgroundColor, double? elevation, IconThemeData? selectedIconTheme, IconThemeData? unselectedIconTheme, Color? selectedItemColor, Color? unselectedItemColor, TextStyle? selectedLabelStyle, TextStyle? unselectedLabelStyle, bool? showSelectedLabels, bool? showUnselectedLabels, BottomNavigationBarType? type, bool? enableFeedback, BottomNavigationBarLandscapeLayout? landscapeLayout, MaterialStateProperty<MouseCursor?>? mouseCursor, }) { return BottomNavigationBarThemeData( backgroundColor: backgroundColor ?? this.backgroundColor, elevation: elevation ?? this.elevation, selectedIconTheme: selectedIconTheme ?? this.selectedIconTheme, unselectedIconTheme: unselectedIconTheme ?? this.unselectedIconTheme, selectedItemColor: selectedItemColor ?? this.selectedItemColor, unselectedItemColor: unselectedItemColor ?? this.unselectedItemColor, selectedLabelStyle: selectedLabelStyle ?? this.selectedLabelStyle, unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle, showSelectedLabels: showSelectedLabels ?? this.showSelectedLabels, showUnselectedLabels: showUnselectedLabels ?? this.showUnselectedLabels, type: type ?? this.type, enableFeedback: enableFeedback ?? this.enableFeedback, landscapeLayout: landscapeLayout ?? this.landscapeLayout, mouseCursor: mouseCursor ?? this.mouseCursor, ); } /// Linearly interpolate between two [BottomNavigationBarThemeData]. /// /// {@macro dart.ui.shadow.lerp} static BottomNavigationBarThemeData lerp(BottomNavigationBarThemeData? a, BottomNavigationBarThemeData? b, double t) { if (identical(a, b) && a != null) { return a; } return BottomNavigationBarThemeData( backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), elevation: lerpDouble(a?.elevation, b?.elevation, t), selectedIconTheme: IconThemeData.lerp(a?.selectedIconTheme, b?.selectedIconTheme, t), unselectedIconTheme: IconThemeData.lerp(a?.unselectedIconTheme, b?.unselectedIconTheme, t), selectedItemColor: Color.lerp(a?.selectedItemColor, b?.selectedItemColor, t), unselectedItemColor: Color.lerp(a?.unselectedItemColor, b?.unselectedItemColor, t), selectedLabelStyle: TextStyle.lerp(a?.selectedLabelStyle, b?.selectedLabelStyle, t), unselectedLabelStyle: TextStyle.lerp(a?.unselectedLabelStyle, b?.unselectedLabelStyle, t), showSelectedLabels: t < 0.5 ? a?.showSelectedLabels : b?.showSelectedLabels, showUnselectedLabels: t < 0.5 ? a?.showUnselectedLabels : b?.showUnselectedLabels, type: t < 0.5 ? a?.type : b?.type, enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback, landscapeLayout: t < 0.5 ? a?.landscapeLayout : b?.landscapeLayout, mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor, ); } @override int get hashCode => Object.hash( backgroundColor, elevation, selectedIconTheme, unselectedIconTheme, selectedItemColor, unselectedItemColor, selectedLabelStyle, unselectedLabelStyle, showSelectedLabels, showUnselectedLabels, type, enableFeedback, landscapeLayout, mouseCursor, ); @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is BottomNavigationBarThemeData && other.backgroundColor == backgroundColor && other.elevation == elevation && other.selectedIconTheme == selectedIconTheme && other.unselectedIconTheme == unselectedIconTheme && other.selectedItemColor == selectedItemColor && other.unselectedItemColor == unselectedItemColor && other.selectedLabelStyle == selectedLabelStyle && other.unselectedLabelStyle == unselectedLabelStyle && other.showSelectedLabels == showSelectedLabels && other.showUnselectedLabels == showUnselectedLabels && other.type == type && other.enableFeedback == enableFeedback && other.landscapeLayout == landscapeLayout && other.mouseCursor == mouseCursor; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null)); properties.add(DoubleProperty('elevation', elevation, defaultValue: null)); properties.add(DiagnosticsProperty<IconThemeData>('selectedIconTheme', selectedIconTheme, defaultValue: null)); properties.add(DiagnosticsProperty<IconThemeData>('unselectedIconTheme', unselectedIconTheme, defaultValue: null)); properties.add(ColorProperty('selectedItemColor', selectedItemColor, defaultValue: null)); properties.add(ColorProperty('unselectedItemColor', unselectedItemColor, defaultValue: null)); properties.add(DiagnosticsProperty<TextStyle>('selectedLabelStyle', selectedLabelStyle, defaultValue: null)); properties.add(DiagnosticsProperty<TextStyle>('unselectedLabelStyle', unselectedLabelStyle, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('showSelectedLabels', showSelectedLabels, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('showUnselectedLabels', showUnselectedLabels, defaultValue: null)); properties.add(DiagnosticsProperty<BottomNavigationBarType>('type', type, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null)); properties.add(DiagnosticsProperty<BottomNavigationBarLandscapeLayout>('landscapeLayout', landscapeLayout, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null)); } } /// Applies a bottom navigation bar theme to descendant [BottomNavigationBar] /// widgets. /// /// Descendant widgets obtain the current theme's [BottomNavigationBarTheme] /// object using [BottomNavigationBarTheme.of]. When a widget uses /// [BottomNavigationBarTheme.of], it is automatically rebuilt if the theme /// later changes. /// /// A bottom navigation theme can be specified as part of the overall Material /// theme using [ThemeData.bottomNavigationBarTheme]. /// /// See also: /// /// * [BottomNavigationBarThemeData], which describes the actual configuration /// of a bottom navigation bar theme. class BottomNavigationBarTheme extends InheritedWidget { /// Constructs a bottom navigation bar theme that configures all descendant /// [BottomNavigationBar] widgets. const BottomNavigationBarTheme({ super.key, required this.data, required super.child, }); /// The properties used for all descendant [BottomNavigationBar] widgets. final BottomNavigationBarThemeData data; /// Returns the configuration [data] from the closest /// [BottomNavigationBarTheme] ancestor. If there is no ancestor, it returns /// [ThemeData.bottomNavigationBarTheme]. Applications can assume that the /// returned value will not be null. /// /// Typical usage is as follows: /// /// ```dart /// BottomNavigationBarThemeData theme = BottomNavigationBarTheme.of(context); /// ``` static BottomNavigationBarThemeData of(BuildContext context) { final BottomNavigationBarTheme? bottomNavTheme = context.dependOnInheritedWidgetOfExactType<BottomNavigationBarTheme>(); return bottomNavTheme?.data ?? Theme.of(context).bottomNavigationBarTheme; } @override bool updateShouldNotify(BottomNavigationBarTheme oldWidget) => data != oldWidget.data; }