checkbox_theme.dart 8.85 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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 'material_state.dart';
import 'theme.dart';
import 'theme_data.dart';

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

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
/// Defines default property values for descendant [Checkbox] widgets.
///
/// Descendant widgets obtain the current [CheckboxThemeData] object using
/// `CheckboxTheme.of(context)`. Instances of [CheckboxThemeData] can be
/// customized with [CheckboxThemeData.copyWith].
///
/// Typically a [CheckboxThemeData] is specified as part of the overall [Theme]
/// with [ThemeData.checkboxTheme].
///
/// All [CheckboxThemeData] properties are `null` by default. When null, the
/// [Checkbox] will use the values from [ThemeData] if they exist, otherwise it
/// will provide its own defaults based on the overall [Theme]'s colorScheme.
/// See the individual [Checkbox] properties for details.
///
/// See also:
///
///  * [ThemeData], which describes the overall theme information for the
///    application.
@immutable
class CheckboxThemeData with Diagnosticable {
  /// Creates a theme that can be used for [ThemeData.checkboxTheme].
  const CheckboxThemeData({
    this.mouseCursor,
    this.fillColor,
    this.checkColor,
    this.overlayColor,
    this.splashRadius,
    this.materialTapTargetSize,
    this.visualDensity,
47 48
    this.shape,
    this.side,
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  });

  /// {@macro flutter.material.checkbox.mouseCursor}
  ///
  /// If specified, overrides the default value of [Checkbox.mouseCursor].
  final MaterialStateProperty<MouseCursor?>? mouseCursor;

  /// {@macro flutter.material.checkbox.fillColor}
  ///
  /// If specified, overrides the default value of [Checkbox.fillColor].
  final MaterialStateProperty<Color?>? fillColor;

  /// {@macro flutter.material.checkbox.checkColor}
  ///
  /// Resolves in the following states:
  ///  * [MaterialState.selected].
  ///  * [MaterialState.hovered].
  ///  * [MaterialState.focused].
  ///  * [MaterialState.disabled].
  ///
  /// If specified, overrides the default value of [Checkbox.checkColor].
  final MaterialStateProperty<Color?>? checkColor;

72
  /// {@macro flutter.material.checkbox.overlayColor}
73
  ///
74
  /// If specified, overrides the default value of [Checkbox.overlayColor].
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
  final MaterialStateProperty<Color?>? overlayColor;

  /// {@macro flutter.material.checkbox.splashRadius}
  ///
  /// If specified, overrides the default value of [Checkbox.splashRadius].
  final double? splashRadius;

  /// {@macro flutter.material.checkbox.materialTapTargetSize}
  ///
  /// If specified, overrides the default value of
  /// [Checkbox.materialTapTargetSize].
  final MaterialTapTargetSize? materialTapTargetSize;

  /// {@macro flutter.material.checkbox.visualDensity}
  ///
  /// If specified, overrides the default value of [Checkbox.visualDensity].
  final VisualDensity? visualDensity;

93 94 95 96 97 98 99 100 101 102
  /// {@macro flutter.material.checkbox.shape}
  ///
  /// If specified, overrides the default value of [Checkbox.shape].
  final OutlinedBorder? shape;

  /// {@macro flutter.material.checkbox.side}
  ///
  /// If specified, overrides the default value of [Checkbox.side].
  final BorderSide? side;

103 104 105 106 107 108 109 110 111 112
  /// Creates a copy of this object but with the given fields replaced with the
  /// new values.
  CheckboxThemeData copyWith({
    MaterialStateProperty<MouseCursor?>? mouseCursor,
    MaterialStateProperty<Color?>? fillColor,
    MaterialStateProperty<Color?>? checkColor,
    MaterialStateProperty<Color?>? overlayColor,
    double? splashRadius,
    MaterialTapTargetSize? materialTapTargetSize,
    VisualDensity? visualDensity,
113 114
    OutlinedBorder? shape,
    BorderSide? side,
115 116 117 118 119 120 121 122 123
  }) {
    return CheckboxThemeData(
      mouseCursor: mouseCursor ?? this.mouseCursor,
      fillColor: fillColor ?? this.fillColor,
      checkColor: checkColor ?? this.checkColor,
      overlayColor: overlayColor ?? this.overlayColor,
      splashRadius: splashRadius ?? this.splashRadius,
      materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
      visualDensity: visualDensity ?? this.visualDensity,
124 125
      shape: shape ?? this.shape,
      side: side ?? this.side,
126 127 128 129 130 131 132
    );
  }

  /// Linearly interpolate between two [CheckboxThemeData]s.
  ///
  /// {@macro dart.ui.shadow.lerp}
  static CheckboxThemeData lerp(CheckboxThemeData? a, CheckboxThemeData? b, double t) {
133 134 135
    if (identical(a, b) && a != null) {
      return a;
    }
136 137
    return CheckboxThemeData(
      mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
138 139 140
      fillColor: MaterialStateProperty.lerp<Color?>(a?.fillColor, b?.fillColor, t, Color.lerp),
      checkColor: MaterialStateProperty.lerp<Color?>(a?.checkColor, b?.checkColor, t, Color.lerp),
      overlayColor: MaterialStateProperty.lerp<Color?>(a?.overlayColor, b?.overlayColor, t, Color.lerp),
141 142 143
      splashRadius: lerpDouble(a?.splashRadius, b?.splashRadius, t),
      materialTapTargetSize: t < 0.5 ? a?.materialTapTargetSize : b?.materialTapTargetSize,
      visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity,
144 145
      shape: ShapeBorder.lerp(a?.shape, b?.shape, t) as OutlinedBorder?,
      side: _lerpSides(a?.side, b?.side, t),
146 147 148 149
    );
  }

  @override
150 151 152 153 154 155 156 157 158 159 160
  int get hashCode => Object.hash(
    mouseCursor,
    fillColor,
    checkColor,
    overlayColor,
    splashRadius,
    materialTapTargetSize,
    visualDensity,
    shape,
    side,
  );
161 162 163

  @override
  bool operator ==(Object other) {
164
    if (identical(this, other)) {
165
      return true;
166 167
    }
    if (other.runtimeType != runtimeType) {
168
      return false;
169
    }
170 171 172 173 174 175 176
    return other is CheckboxThemeData
      && other.mouseCursor == mouseCursor
      && other.fillColor == fillColor
      && other.checkColor == checkColor
      && other.overlayColor == overlayColor
      && other.splashRadius == splashRadius
      && other.materialTapTargetSize == materialTapTargetSize
177 178 179
      && other.visualDensity == visualDensity
      && other.shape == shape
      && other.side == side;
180 181 182 183 184 185 186 187 188 189 190 191
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('fillColor', fillColor, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('checkColor', checkColor, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('overlayColor', overlayColor, defaultValue: null));
    properties.add(DoubleProperty('splashRadius', splashRadius, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
    properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null));
192 193
    properties.add(DiagnosticsProperty<OutlinedBorder>('shape', shape, defaultValue: null));
    properties.add(DiagnosticsProperty<BorderSide>('side', side, defaultValue: null));
194 195
  }

196 197
  // Special case because BorderSide.lerp() doesn't support null arguments
  static BorderSide? _lerpSides(BorderSide? a, BorderSide? b, double t) {
198
    if (a == null || b == null) {
199
      return null;
200
    }
201 202 203
    if (identical(a, b)) {
      return a;
    }
204
    return BorderSide.lerp(a, b, t);
205
  }
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
}

/// Applies a checkbox theme to descendant [Checkbox] widgets.
///
/// Descendant widgets obtain the current theme's [CheckboxTheme] object using
/// [CheckboxTheme.of]. When a widget uses [CheckboxTheme.of], it is
/// automatically rebuilt if the theme later changes.
///
/// A checkbox theme can be specified as part of the overall Material theme
/// using [ThemeData.checkboxTheme].
///
/// See also:
///
///  * [CheckboxThemeData], which describes the actual configuration of a
///  checkbox theme.
class CheckboxTheme extends InheritedWidget {
  /// Constructs a checkbox theme that configures all descendant [Checkbox]
  /// widgets.
  const CheckboxTheme({
225
    super.key,
226
    required this.data,
227 228
    required super.child,
  });
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

  /// The properties used for all descendant [Checkbox] widgets.
  final CheckboxThemeData data;

  /// Returns the configuration [data] from the closest [CheckboxTheme]
  /// ancestor. If there is no ancestor, it returns [ThemeData.checkboxTheme].
  ///
  /// Typical usage is as follows:
  ///
  /// ```dart
  /// CheckboxThemeData theme = CheckboxTheme.of(context);
  /// ```
  static CheckboxThemeData of(BuildContext context) {
    final CheckboxTheme? checkboxTheme = context.dependOnInheritedWidgetOfExactType<CheckboxTheme>();
    return checkboxTheme?.data ?? Theme.of(context).checkboxTheme;
  }

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