segmented_button_theme.dart 5.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
// 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 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'button_style.dart';
import 'theme.dart';

// Examples can assume:
// late BuildContext context;

/// Overrides the default values of visual properties for descendant
/// [SegmentedButton] widgets.
///
/// Descendant widgets obtain the current [SegmentedButtonThemeData] object with
/// [SegmentedButtonTheme.of]. Instances of [SegmentedButtonTheme] can
/// be customized with [SegmentedButtonThemeData.copyWith].
///
/// Typically a [SegmentedButtonTheme] is specified as part of the overall
/// [Theme] with [ThemeData.segmentedButtonTheme].
///
/// All [SegmentedButtonThemeData] properties are null by default. When null,
/// the [SegmentedButton] computes its own default values, typically based on
/// the overall theme's [ThemeData.colorScheme], [ThemeData.textTheme], and
/// [ThemeData.iconTheme].
@immutable
class SegmentedButtonThemeData with Diagnosticable {
  /// Creates a [SegmentedButtonThemeData] that can be used to override default properties
  /// in a [SegmentedButtonTheme] widget.
  const SegmentedButtonThemeData({
    this.style,
    this.selectedIcon,
  });

  /// Overrides the [SegmentedButton]'s default style.
  ///
  /// Non-null properties or non-null resolved [MaterialStateProperty]
  /// values override the default values used by [SegmentedButton].
  ///
  /// If [style] is null, then this theme doesn't override anything.
  final ButtonStyle? style;

  /// Override for [SegmentedButton.selectedIcon] property.
  ///
  /// If non-null, then [selectedIcon] will be used instead of default
  /// value for [SegmentedButton.selectedIcon].
  final Widget? selectedIcon;

  /// Creates a copy of this object with the given fields replaced with the
  /// new values.
  SegmentedButtonThemeData copyWith({
    ButtonStyle? style,
    Widget? selectedIcon,
  }) {
    return SegmentedButtonThemeData(
      style: style ?? this.style,
      selectedIcon: selectedIcon ?? this.selectedIcon,
    );
  }

  /// Linearly interpolates between two segmented button themes.
  static SegmentedButtonThemeData lerp(SegmentedButtonThemeData? a, SegmentedButtonThemeData? b, double t) {
    return SegmentedButtonThemeData(
      style: ButtonStyle.lerp(a?.style, b?.style, t),
      selectedIcon: t < 0.5 ? a?.selectedIcon : b?.selectedIcon,
    );
  }

  @override
  int get hashCode => Object.hash(
    style,
    selectedIcon,
  );

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is SegmentedButtonThemeData
        && other.style == style
        && other.selectedIcon == selectedIcon;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<ButtonStyle>('style', style, defaultValue: null));
    properties.add(DiagnosticsProperty<Widget>('selectedIcon', selectedIcon, defaultValue: null));
  }
}

/// An inherited widget that defines the visual properties for
/// [SegmentedButton]s in this widget's subtree.
///
/// Values specified here are used for [SegmentedButton] properties that are not
/// given an explicit non-null value.
class SegmentedButtonTheme extends InheritedTheme {
  /// Creates a [SegmentedButtonTheme] that controls visual parameters for
  /// descendent [SegmentedButton]s.
  const SegmentedButtonTheme({
    super.key,
    required this.data,
    required super.child,
  }) : assert(data != null);

  /// Specifies the visual properties used by descendant [SegmentedButton]
  /// widgets.
  final SegmentedButtonThemeData data;

  /// The [data] from the closest instance of this class that encloses the given
  /// context.
  ///
  /// If there is no [SegmentedButtonTheme] in scope, this will return
  /// [ThemeData.segmentedButtonTheme] from the ambient [Theme].
  ///
  /// Typical usage is as follows:
  ///
  /// ```dart
  /// SegmentedButtonThemeData theme = SegmentedButtonTheme.of(context);
  /// ```
  ///
  /// See also:
  ///
  ///  * [maybeOf], which returns null if it doesn't find a
  ///    [SegmentedButtonTheme] ancestor.
  static SegmentedButtonThemeData of(BuildContext context) {
    return maybeOf(context) ?? Theme.of(context).segmentedButtonTheme;
  }

  /// The data from the closest instance of this class that encloses the given
  /// context, if any.
  ///
  /// Use this function if you want to allow situations where no
  /// [SegmentedButtonTheme] is in scope. Prefer using [SegmentedButtonTheme.of]
  /// in situations where a [SegmentedButtonThemeData] is expected to be
  /// non-null.
  ///
  /// If there is no [SegmentedButtonTheme] in scope, then this function will
  /// return null.
  ///
  /// Typical usage is as follows:
  ///
  /// ```dart
  /// SegmentedButtonThemeData? theme = SegmentedButtonTheme.maybeOf(context);
  /// if (theme == null) {
  ///   // Do something else instead.
  /// }
  /// ```
  ///
  /// See also:
  ///
  ///  * [of], which will return [ThemeData.segmentedButtonTheme] if it doesn't
  ///    find a [SegmentedButtonTheme] ancestor, instead of returning null.
  static SegmentedButtonThemeData? maybeOf(BuildContext context) {
    assert(context != null);
    return context.dependOnInheritedWidgetOfExactType<SegmentedButtonTheme>()?.data;
  }

  @override
  Widget wrap(BuildContext context, Widget child) {
    return SegmentedButtonTheme(data: data, child: child);
  }

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