bottomNavigationBar static method

BottomNavigationBarThemeData bottomNavigationBar({
  1. required ColorScheme colorScheme,
  2. TextStyle? labelTextStyle,
  3. double? selectedLabelSize,
  4. double? unselectedLabelSize,
  5. SchemeColor? selectedLabelSchemeColor,
  6. SchemeColor? unselectedLabelSchemeColor,
  7. bool? mutedUnselectedLabel,
  8. double? selectedIconSize,
  9. double? unselectedIconSize,
  10. SchemeColor? selectedIconSchemeColor,
  11. SchemeColor? unselectedIconSchemeColor,
  12. bool? mutedUnselectedIcon,
  13. SchemeColor? backgroundSchemeColor,
  14. double? opacity,
  15. double? elevation,
  16. bool? showSelectedLabels,
  17. bool? showUnselectedLabels,
  18. BottomNavigationBarType? type,
  19. BottomNavigationBarLandscapeLayout? landscapeLayout,
  20. int unselectedAlphaBlend = kUnselectedBackgroundPrimaryAlphaBlend,
  21. int unselectedAlpha = kUnselectedAlphaBlend,
  22. bool useFlutterDefaults = false,
})

An opinionated BottomNavigationBarThemeData with custom elevation.

This sub-theme uses a style that prefers single use config parameters over the ones that combines many styling options into TextStyle and icon sub-theme properties. This is simpler to use when you want to modify a single property like size and rest is fine. This of course comes at the expense that the sub-theme instead has more properties.

Its elevation defaults to kBottomNavigationBarElevation = 0.

The bottom navigation bar uses opinionated colors choices from the passed colorScheme to style the bottom navigation bar.

Background opacity can be set.

Implementation

static BottomNavigationBarThemeData bottomNavigationBar({
  /// Typically you would pass the same [ColorScheme] that is also used in
  /// your [ThemeData] definition.
  required final ColorScheme colorScheme,

  /// Optional text style for the [BottomNavigationBar] labels.
  ///
  /// If [useFlutterDefaults] is false, the text style
  /// [FlexColorScheme.m3TextTheme.bodyMedium]
  /// will be used as base style for the text style.
  ///
  /// If [useFlutterDefaults] is true, null will be passed to
  /// [FlexSubThemes.bottomNavigationBar] and along to theme creation, if all
  /// labeling modifying properties (size and scheme color) are also null, it
  /// will then be passed along as null, allowing it to remain undefined
  /// and widget default behavior sets the default. If label size or scheme
  /// is defined, a default TextStyle() will be created, if
  /// [bottomNavigationBarLabelTextStyle] is undefined, that gets th size and
  /// color applied.
  ///
  /// The size and colors defined in any of the text size and color properties
  /// are applied as overrides on the text style.
  final TextStyle? labelTextStyle,

  /// The size of the text label on selected [BottomNavigationBar] item.
  ///
  /// If defined, it overrides the font size on effective label TextStyle
  /// on selected item, 14 is used as fallback if needed.
  final double? selectedLabelSize,

  /// The size of the text label on unselected [BottomNavigationBar] items.
  ///
  /// If defined, it overrides the font size on effective label TextStyle
  /// on unselected items. Defaults to [selectedLabelSize] - 2, but min 8.
  /// Smaller than 8dp is not legible on most screens.
  ///
  /// [BottomNavigationBar] uses this -2dp smaller font on the unselected
  /// label as default, since it is so based on Material 2 spec. By assigning
  /// same value as to selectedLabelSize, you can make them the same size.
  final double? unselectedLabelSize,

  /// Select which color from the theme's [ColorScheme] to use as base for
  /// the [BottomNavigationBar]'s selected label text color.
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  ///
  /// Defaults to [SchemeColor.primary].
  ///
  /// If [useFlutterDefaults] is true, and this property and all other
  /// label modifying properties are undefined, including the text style,
  /// the effective color will be [ColorScheme.primary] in light theme and
  /// [ColorScheme.dark] theme mode.
  final SchemeColor? selectedLabelSchemeColor,

  /// Select which color from the passed in [ColorScheme] to use for
  /// the [BottomNavigationBar]'s unselected items text color.
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  ///
  /// If null, defaults to using [Theme]'s [ColorScheme.onSurface] via the
  /// [NavigationRail]'s default un-themed behavior.
  ///
  /// FlexColorScheme will use [SchemeColor.onSurface] via [FlexSubThemesData]
  /// property default.
  final SchemeColor? unselectedLabelSchemeColor,

  /// If true, the unselected labels in the [BottomNavigationBar] use a
  /// more muted color version of the color defined by
  /// [unselectedLabelSchemeColor].
  ///
  /// If null, defaults to true.
  final bool? mutedUnselectedLabel,

  /// The size of the icon on selected [BottomNavigationBar] item.
  ///
  /// If undefined, it defaults to 24.
  final double? selectedIconSize,

  /// The size of the icons on unselected [BottomNavigationBar] items.
  ///
  /// If undefined, defaults to [selectedIconSize].
  final double? unselectedIconSize,

  /// Select which color from the theme's [ColorScheme] to use as base for
  /// the [BottomNavigationBar]'s selected item icon color.
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  ///
  /// If undefined, defaults to [SchemeColor.primary].
  ///
  /// If [useFlutterDefaults] is true, and this property and all other icon
  /// modifying properties are undefined, the effective color will be
  /// [ColorScheme.dark] in dark theme mode.
  final SchemeColor? selectedIconSchemeColor,

  /// Select which color from the passed in [ColorScheme] to use as base for
  /// the [BottomNavigationBar]'s unselected items icon color.
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  ///
  /// Defaults to [SchemeColor.onSurface], and adds an alpha blend and
  /// opacity, if [bottomNavigationBarMutedUnselectedLabel] is true.
  ///
  /// If [useFlutterDefaults] is true, and this property and all other
  /// icon modifying properties are undefined,
  /// the effective color will be [ThemeData.unselectedWidgetColor]
  /// which is [Colors.black54] in light mode and [Colors.white70] in dark.
  final SchemeColor? unselectedIconSchemeColor,

  /// If true, the unselected icon in the [BottomNavigationBar] use a more
  /// muted color version of the color defined by
  /// [bottomNavigationBarUnselectedIconSchemeColor].
  /// The muting is unselected color with
  /// blendAlpha(unselected color, [kUnselectedBackgroundPrimaryAlphaBlend])
  /// and withAlpha([kUnselectedAlphaBlend]).
  ///
  /// If undefined, defaults to true.
  final bool? mutedUnselectedIcon,

  /// Select which color from the theme's [ColorScheme] to use as background
  /// color for the [BottomNavigationBar].
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  ///
  /// If undefined, defaults to [SchemeColor.background].
  ///
  /// If [useFlutterDefaults] true, and this property is undefined,
  /// the effective background color will also be [ColorScheme.background].
  ///
  /// FlexColorScheme sets background defaults of [BottomNavigationBar],
  /// [NavigationBar] and [BottomNavigationBar] to [SchemeColor.background]
  /// when it is using component sub-themes.
  /// Flutter SDK uses different colors on all three widgets. Our opinion is
  /// that they should all default to using the same [ColorScheme] based
  /// color. FlexColorScheme uses the background color as this default.
  final SchemeColor? backgroundSchemeColor,

  /// BottomNavigationBar background opacity.
  ///
  /// The opacity value is only applied when a none null value is selected
  /// in [backgroundSchemeColor], it cannot be applied to the default
  /// Flutter SDK background color available when backgroundSchemeColor is
  /// null.
  ///
  /// If undefined, defaults to 1, fully opaque.
  final double? opacity,

  /// [BottomNavigationBar] container elevation.
  ///
  /// If not defined, defaults to [kBottomNavigationBarElevation] = 3.
  final double? elevation,

  /// Whether the labels are shown for the selected
  /// [BottomNavigationBarItem].
  final bool? showSelectedLabels,

  /// Whether the labels are shown for the unselected
  /// [BottomNavigationBarItem]s.
  final bool? showUnselectedLabels,

  /// Defines the layout and behavior of a [BottomNavigationBar].
  ///
  /// With [BottomNavigationBarType.fixed] the items have fixed width.
  /// With [BottomNavigationBarType.shifting], the location and size of the
  /// items animate and labels fade in when they are tapped.
  ///
  /// If undefined, defaults to Flutter SDK default. Where
  /// If type is provided, it is returned. Next, if the bottom navigation bar
  /// theme provides a type, it is used. Finally, the default behavior will be
  /// [BottomNavigationBarType.fixed] for 3 or fewer items, and
  /// [BottomNavigationBarType.shifting] is used for 4+ items.
  final BottomNavigationBarType? type,

  /// The arrangement of the bar's [items] when the enclosing
  /// [MediaQueryData.orientation] is [Orientation.landscape].
  ///
  /// The following alternatives are supported:
  ///
  /// * [BottomNavigationBarLandscapeLayout.spread] - the items are
  ///   evenly spaced and spread out across the available width. Each
  ///   item's label and icon are arranged in a column.
  /// * [BottomNavigationBarLandscapeLayout.centered] - the items are
  ///   evenly spaced in a row but only consume as much width as they
  ///   would in portrait orientation. The row of items is centered within
  ///   the available width. Each item's label and icon are arranged
  ///   in a column.
  /// * [BottomNavigationBarLandscapeLayout.linear] - the items are
  ///   evenly spaced and each item's icon and label are lined up in a
  ///   row instead of a column.
  ///
  /// Defaults to [BottomNavigationBarLandscapeLayout.spread] via
  /// Widget's default un-themed behavior.
  final BottomNavigationBarLandscapeLayout? landscapeLayout,

  /// The icon color alpha blend value for unselected items, used on icon when
  /// [mutedUnselectedIcon] is true and on label when
  /// [mutedUnselectedLabel] is true.
  ///
  /// Defaults to [kUnselectedBackgroundPrimaryAlphaBlend], which is
  /// 0x66 = 102 = 40%.
  ///
  /// This setting is not exposed via [FlexSubThemesData], but can be if
  /// needed later.
  final int unselectedAlphaBlend = kUnselectedBackgroundPrimaryAlphaBlend,

  /// The icon alpha value for unselected item, used on icon when
  /// [mutedUnselectedIcon] is true and on label when
  /// [mutedUnselectedLabel] is true.
  ///
  /// Defaults to [kUnselectedAlphaBlend], which is
  /// 0xA5 = 165 = 65%
  ///
  /// This setting is not exposed via [FlexSubThemesData], but can be if
  /// needed later.
  final int unselectedAlpha = kUnselectedAlphaBlend,

  /// Set to true to use Flutter SDK defaults for [BottomNavigationBar]
  /// theme when its color, size and text style properties are undefined,
  /// instead of using [FlexColorScheme]'s own defaults.
  ///
  /// Recommend keeping it **false** for a more color harmonized component
  /// theme starting point. This flag may be helpful if you want to create
  /// custom sub-themes starting from less opinionated settings.
  ///
  /// When all required properties are undefined and flag is false or true,
  /// the effective default styles for undefined inputs become:
  ///
  /// ```
  ///                    FCS defaults   Flutter defaults
  /// useFlutterDefaults false          true
  /// - background       background     background
  /// - selected icon    primary        light: theme primary, dark: secondary
  /// - Selected label   primary        light: theme primary, dark: secondary
  /// - unselected icon  onSurface      light: black54, dark: white70
  /// - unSelected label onSurface      light: black54, dark: white70
  /// ```
  /// FCS further applies both an alpha blend and slight opacity to
  /// unselected icon and unselected label, but only if
  /// [bottomNavigationBarMutedUnselectedIcon] and
  /// [bottomNavigationBarMutedUnselectedLabel] are true respectively,
  /// this also applies to undefined color inputs.
  ///
  /// When muted unselected options are true, the difference to Flutter
  /// default for unselected items is subtle, FCS has a bit more contrast.
  final bool useFlutterDefaults = false,
}) {
  // Determine if we can even use default text styles, only when all are null,
  // can we fall back to Flutter SDK default.
  final bool useDefaultTextStyle = labelTextStyle == null &&
      selectedLabelSize == null &&
      unselectedLabelSize == null &&
      selectedLabelSchemeColor == null &&
      unselectedLabelSchemeColor == null &&
      useFlutterDefaults;

  // Determine if we can even use default icon styles, only when all are null,
  // can we fall back to Flutter SDK default.
  final bool useDefaultIconTheme = selectedIconSize == null &&
      unselectedIconSize == null &&
      selectedIconSchemeColor == null &&
      unselectedIconSchemeColor == null &&
      useFlutterDefaults;

  // Get text color, defaults to primary in light and to secondary in dark.
  final Color labelColor = schemeColor(
      selectedLabelSchemeColor ??
          (colorScheme.brightness == Brightness.dark && useDefaultTextStyle
              ? SchemeColor.secondary
              : SchemeColor.primary),
      colorScheme);

  // Get unselected label color, defaults to onSurface.
  final Color unselectedLabelColor = schemeColor(
      unselectedLabelSchemeColor ?? SchemeColor.onSurface, colorScheme);

  // Get selected text style, defaults to TextStyle(), we can use it since
  // size and color are applied to is separately.
  final TextStyle textStyle = labelTextStyle ?? const TextStyle();

  // Get effective text sizes.
  final double labelSize = selectedLabelSize ?? textStyle.fontSize ?? 14;
  // If not specified, unselected is label size, use 2dp smaller than
  // selected, but always at least 8dp.
  final double effectiveUnselectedLabelSize =
      unselectedLabelSize ?? math.max(labelSize - 2, 8);

  // Get icon color, defaults to primary in light, and secondary in dark.
  final Color iconColor = schemeColor(
      selectedIconSchemeColor ??
          (useDefaultIconTheme && colorScheme.brightness == Brightness.dark
              ? SchemeColor.secondary
              : SchemeColor.primary),
      colorScheme);

  // Get unselected icon color, defaults to onSurface.
  final Color unselectedIconColor = schemeColor(
      unselectedIconSchemeColor ?? SchemeColor.onSurface, colorScheme);

  // Get effective icons sizes.
  final double iconSize = selectedIconSize ?? 24;
  final double effectiveUnselectedIconSize = unselectedIconSize ?? iconSize;

  // Background color, when using normal default, falls back to background.
  final Color backgroundColor = schemeColor(
          backgroundSchemeColor ?? SchemeColor.background, colorScheme)
      .withOpacity(opacity ?? 1.0);

  return BottomNavigationBarThemeData(
    backgroundColor: backgroundSchemeColor == null
        ? useFlutterDefaults
            ? null
            : backgroundColor
        : backgroundColor,
    elevation: useFlutterDefaults && elevation == null
        ? null
        : elevation ?? kBottomNavigationBarElevation,
    unselectedIconTheme: useDefaultIconTheme
        ? null
        : IconThemeData(
            size: effectiveUnselectedIconSize,
            opacity: 1,
            color: (mutedUnselectedIcon ?? true)
                ? unselectedIconColor
                    .blendAlpha(unselectedIconColor, unselectedAlphaBlend)
                    .withAlpha(unselectedAlpha)
                : unselectedIconColor,
          ),
    selectedIconTheme: useDefaultIconTheme
        ? null
        : IconThemeData(
            size: iconSize,
            opacity: 1,
            color: iconColor,
          ),
    selectedItemColor: useDefaultTextStyle ? null : labelColor,
    unselectedItemColor: useDefaultTextStyle
        ? null
        : (mutedUnselectedLabel ?? true)
            ? unselectedLabelColor
                .blendAlpha(unselectedLabelColor, unselectedAlphaBlend)
                .withAlpha(unselectedAlpha)
            : unselectedLabelColor,
    unselectedLabelStyle: useDefaultTextStyle
        ? null
        : textStyle.copyWith(
            fontSize: effectiveUnselectedLabelSize,
            color: (mutedUnselectedLabel ?? true)
                ? unselectedLabelColor
                    .blendAlpha(unselectedLabelColor, unselectedAlphaBlend)
                    .withAlpha(unselectedAlpha)
                : unselectedLabelColor,
          ),
    selectedLabelStyle: useDefaultTextStyle
        ? null
        : textStyle.copyWith(
            fontSize: labelSize,
            color: labelColor,
          ),
    showSelectedLabels: showSelectedLabels,
    showUnselectedLabels: showUnselectedLabels,
    type: type,
    landscapeLayout: landscapeLayout,
  );
}