123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900 |
- // 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/material.dart';
- import 'package:flyinsonolite/infrastructure/scale.dart';
- // Examples can assume:
- // bool _throwShotAway = false;
- // late StateSetter setState;
- /// A Material Design checkbox.
- ///
- /// The checkbox itself does not maintain any state. Instead, when the state of
- /// the checkbox changes, the widget calls the [onChanged] callback. Most
- /// widgets that use a checkbox will listen for the [onChanged] callback and
- /// rebuild the checkbox with a new [value] to update the visual appearance of
- /// the checkbox.
- ///
- /// The checkbox can optionally display three values - true, false, and null -
- /// if [tristate] is true. When [value] is null a dash is displayed. By default
- /// [tristate] is false and the checkbox's [value] must be true or false.
- ///
- /// Requires one of its ancestors to be a [Material] widget.
- ///
- /// {@tool dartpad}
- /// This example shows how you can override the default theme of
- /// of a [CustomCheckbox] with a [MaterialStateProperty].
- /// In this example, the checkbox's color will be `Colors.blue` when the [CustomCheckbox]
- /// is being pressed, hovered, or focused. Otherwise, the checkbox's color will
- /// be `Colors.red`.
- ///
- /// ** See code in examples/api/lib/material/checkbox/checkbox.0.dart **
- /// {@end-tool}
- ///
- /// See also:
- ///
- /// * [CheckboxListTile], which combines this widget with a [ListTile] so that
- /// you can give the checkbox a label.
- /// * [Switch], a widget with semantics similar to [CustomCheckbox].
- /// * [Radio], for selecting among a set of explicit values.
- /// * [Slider], for selecting a value in a range.
- /// * <https://material.io/design/components/selection-controls.html#checkboxes>
- /// * <https://material.io/design/components/lists.html#types>
- class CustomCheckbox extends StatefulWidget {
- /// Creates a Material Design checkbox.
- ///
- /// The checkbox itself does not maintain any state. Instead, when the state of
- /// the checkbox changes, the widget calls the [onChanged] callback. Most
- /// widgets that use a checkbox will listen for the [onChanged] callback and
- /// rebuild the checkbox with a new [value] to update the visual appearance of
- /// the checkbox.
- ///
- /// The following arguments are required:
- ///
- /// * [value], which determines whether the checkbox is checked. The [value]
- /// can only be null if [tristate] is true.
- /// * [onChanged], which is called when the value of the checkbox should
- /// change. It can be set to null to disable the checkbox.
- ///
- /// The values of [tristate] and [autofocus] must not be null.
- const CustomCheckbox({
- super.key,
- required this.value,
- this.tristate = false,
- required this.onChanged,
- this.mouseCursor,
- this.activeColor,
- this.fillColor,
- this.checkColor,
- this.focusColor,
- this.hoverColor,
- this.overlayColor,
- this.splashRadius,
- this.materialTapTargetSize,
- this.visualDensity,
- this.focusNode,
- this.autofocus = false,
- this.shape,
- this.side,
- this.isError = false,
- }) : assert(tristate != null),
- assert(tristate || value != null),
- assert(autofocus != null);
- /// Whether this checkbox is checked.
- ///
- /// When [tristate] is true, a value of null corresponds to the mixed state.
- /// When [tristate] is false, this value must not be null.
- final bool? value;
- /// Called when the value of the checkbox should change.
- ///
- /// The checkbox passes the new value to the callback but does not actually
- /// change state until the parent widget rebuilds the checkbox with the new
- /// value.
- ///
- /// If this callback is null, the checkbox will be displayed as disabled
- /// and will not respond to input gestures.
- ///
- /// When the checkbox is tapped, if [tristate] is false (the default) then
- /// the [onChanged] callback will be applied to `!value`. If [tristate] is
- /// true this callback cycle from false to true to null.
- ///
- /// The callback provided to [onChanged] should update the state of the parent
- /// [StatefulWidget] using the [State.setState] method, so that the parent
- /// gets rebuilt; for example:
- ///
- /// ```dart
- /// Checkbox(
- /// value: _throwShotAway,
- /// onChanged: (bool? newValue) {
- /// setState(() {
- /// _throwShotAway = newValue!;
- /// });
- /// },
- /// )
- /// ```
- final ValueChanged<bool?>? onChanged;
- /// {@template flutter.material.checkbox.mouseCursor}
- /// The cursor for a mouse pointer when it enters or is hovering over the
- /// widget.
- ///
- /// If [mouseCursor] is a [MaterialStateProperty<MouseCursor>],
- /// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
- ///
- /// * [MaterialState.selected].
- /// * [MaterialState.hovered].
- /// * [MaterialState.focused].
- /// * [MaterialState.disabled].
- /// {@endtemplate}
- ///
- /// When [value] is null and [tristate] is true, [MaterialState.selected] is
- /// included as a state.
- ///
- /// If null, then the value of [CheckboxThemeData.mouseCursor] is used. If
- /// that is also null, then [MaterialStateMouseCursor.clickable] is used.
- ///
- /// See also:
- ///
- /// * [MaterialStateMouseCursor], a [MouseCursor] that implements
- /// `MaterialStateProperty` which is used in APIs that need to accept
- /// either a [MouseCursor] or a [MaterialStateProperty<MouseCursor>].
- final MouseCursor? mouseCursor;
- /// The color to use when this checkbox is checked.
- ///
- /// Defaults to [ColorScheme.secondary].
- ///
- /// If [fillColor] returns a non-null color in the [MaterialState.selected]
- /// state, it will be used instead of this color.
- final Color? activeColor;
- /// {@template flutter.material.checkbox.fillColor}
- /// The color that fills the checkbox, in all [MaterialState]s.
- ///
- /// Resolves in the following states:
- /// * [MaterialState.selected].
- /// * [MaterialState.hovered].
- /// * [MaterialState.focused].
- /// * [MaterialState.disabled].
- ///
- /// {@tool snippet}
- /// This example resolves the [fillColor] based on the current [MaterialState]
- /// of the [CustomCheckbox], providing a different [Color] when it is
- /// [MaterialState.disabled].
- ///
- /// ```dart
- /// Checkbox(
- /// value: true,
- /// onChanged: (_){},
- /// fillColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
- /// if (states.contains(MaterialState.disabled)) {
- /// return Colors.orange.withOpacity(.32);
- /// }
- /// return Colors.orange;
- /// })
- /// )
- /// ```
- /// {@end-tool}
- /// {@endtemplate}
- ///
- /// If null, then the value of [activeColor] is used in the selected
- /// state. If that is also null, the value of [CheckboxThemeData.fillColor]
- /// is used. If that is also null, then [ThemeData.disabledColor] is used in
- /// the disabled state, [ColorScheme.secondary] is used in the
- /// selected state, and [ThemeData.unselectedWidgetColor] is used in the
- /// default state.
- final MaterialStateProperty<Color?>? fillColor;
- /// {@template flutter.material.checkbox.checkColor}
- /// The color to use for the check icon when this checkbox is checked.
- /// {@endtemplate}
- ///
- /// If null, then the value of [CheckboxThemeData.checkColor] is used. If
- /// that is also null, then Color(0xFFFFFFFF) is used.
- final Color? checkColor;
- /// If true the checkbox's [value] can be true, false, or null.
- ///
- /// [CustomCheckbox] displays a dash when its value is null.
- ///
- /// When a tri-state checkbox ([tristate] is true) is tapped, its [onChanged]
- /// callback will be applied to true if the current value is false, to null if
- /// value is true, and to false if value is null (i.e. it cycles through false
- /// => true => null => false when tapped).
- ///
- /// If tristate is false (the default), [value] must not be null.
- final bool tristate;
- /// {@template flutter.material.checkbox.materialTapTargetSize}
- /// Configures the minimum size of the tap target.
- /// {@endtemplate}
- ///
- /// If null, then the value of [CheckboxThemeData.materialTapTargetSize] is
- /// used. If that is also null, then the value of
- /// [ThemeData.materialTapTargetSize] is used.
- ///
- /// See also:
- ///
- /// * [MaterialTapTargetSize], for a description of how this affects tap targets.
- final MaterialTapTargetSize? materialTapTargetSize;
- /// {@template flutter.material.checkbox.visualDensity}
- /// Defines how compact the checkbox's layout will be.
- /// {@endtemplate}
- ///
- /// {@macro flutter.material.themedata.visualDensity}
- ///
- /// If null, then the value of [CheckboxThemeData.visualDensity] is used. If
- /// that is also null, then the value of [ThemeData.visualDensity] is used.
- ///
- /// See also:
- ///
- /// * [ThemeData.visualDensity], which specifies the [visualDensity] for all
- /// widgets within a [Theme].
- final VisualDensity? visualDensity;
- /// The color for the checkbox's [Material] when it has the input focus.
- ///
- /// If [overlayColor] returns a non-null color in the [MaterialState.focused]
- /// state, it will be used instead.
- ///
- /// If null, then the value of [CheckboxThemeData.overlayColor] is used in the
- /// focused state. If that is also null, then the value of
- /// [ThemeData.focusColor] is used.
- final Color? focusColor;
- /// The color for the checkbox's [Material] when a pointer is hovering over it.
- ///
- /// If [overlayColor] returns a non-null color in the [MaterialState.hovered]
- /// state, it will be used instead.
- ///
- /// If null, then the value of [CheckboxThemeData.overlayColor] is used in the
- /// hovered state. If that is also null, then the value of
- /// [ThemeData.hoverColor] is used.
- final Color? hoverColor;
- /// {@template flutter.material.checkbox.overlayColor}
- /// The color for the checkbox's [Material].
- ///
- /// Resolves in the following states:
- /// * [MaterialState.pressed].
- /// * [MaterialState.selected].
- /// * [MaterialState.hovered].
- /// * [MaterialState.focused].
- /// {@endtemplate}
- ///
- /// If null, then the value of [activeColor] with alpha
- /// [kRadialReactionAlpha], [focusColor] and [hoverColor] is used in the
- /// pressed, focused and hovered state. If that is also null,
- /// the value of [CheckboxThemeData.overlayColor] is used. If that is
- /// also null, then the value of [ColorScheme.secondary] with alpha
- /// [kRadialReactionAlpha], [ThemeData.focusColor] and [ThemeData.hoverColor]
- /// is used in the pressed, focused and hovered state.
- final MaterialStateProperty<Color?>? overlayColor;
- /// {@template flutter.material.checkbox.splashRadius}
- /// The splash radius of the circular [Material] ink response.
- /// {@endtemplate}
- ///
- /// If null, then the value of [CheckboxThemeData.splashRadius] is used. If
- /// that is also null, then [kRadialReactionRadius] is used.
- final double? splashRadius;
- /// {@macro flutter.widgets.Focus.focusNode}
- final FocusNode? focusNode;
- /// {@macro flutter.widgets.Focus.autofocus}
- final bool autofocus;
- /// {@template flutter.material.checkbox.shape}
- /// The shape of the checkbox's [Material].
- /// {@endtemplate}
- ///
- /// If this property is null then [CheckboxThemeData.shape] of [ThemeData.checkboxTheme]
- /// is used. If that's null then the shape will be a [RoundedRectangleBorder]
- /// with a circular corner radius of 1.0.
- final OutlinedBorder? shape;
- /// {@template flutter.material.checkbox.side}
- /// The color and width of the checkbox's border.
- ///
- /// This property can be a [MaterialStateBorderSide] that can
- /// specify different border color and widths depending on the
- /// checkbox's state.
- ///
- /// Resolves in the following states:
- /// * [MaterialState.pressed].
- /// * [MaterialState.selected].
- /// * [MaterialState.hovered].
- /// * [MaterialState.focused].
- /// * [MaterialState.disabled].
- ///
- /// If this property is not a [MaterialStateBorderSide] and it is
- /// non-null, then it is only rendered when the checkbox's value is
- /// false. The difference in interpretation is for backwards
- /// compatibility.
- /// {@endtemplate}
- ///
- /// If this property is null, then [CheckboxThemeData.side] of
- /// [ThemeData.checkboxTheme] is used. If that is also null, then the side
- /// will be width 2.
- final BorderSide? side;
- /// True if this checkbox wants to show an error state.
- ///
- /// The checkbox will have different default container color and check color when
- /// this is true. This is only used when [ThemeData.useMaterial3] is set to true.
- ///
- /// Must not be null. Defaults to false.
- final bool isError;
- @override
- State<CustomCheckbox> createState() => _CustomCheckboxState();
- }
- class _CustomCheckboxState extends State<CustomCheckbox>
- with TickerProviderStateMixin, ToggleableStateMixin {
- final _CheckboxPainter _painter = _CheckboxPainter();
- bool? _previousValue;
- @override
- void initState() {
- super.initState();
- _previousValue = widget.value;
- }
- @override
- void didUpdateWidget(CustomCheckbox oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.value != widget.value) {
- _previousValue = oldWidget.value;
- animateToValue();
- }
- }
- @override
- void dispose() {
- _painter.dispose();
- super.dispose();
- }
- @override
- ValueChanged<bool?>? get onChanged => widget.onChanged;
- @override
- bool get tristate => widget.tristate;
- @override
- bool? get value => widget.value;
- MaterialStateProperty<Color?> get _widgetFillColor {
- return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return null;
- }
- if (states.contains(MaterialState.selected)) {
- return widget.activeColor;
- }
- return null;
- });
- }
- BorderSide? _resolveSide(BorderSide? side) {
- if (side is MaterialStateBorderSide) {
- return MaterialStateProperty.resolveAs<BorderSide?>(side, states);
- }
- if (!states.contains(MaterialState.selected)) {
- return side;
- }
- return null;
- }
- @override
- Widget build(BuildContext context) {
- assert(debugCheckHasMaterial(context));
- final CheckboxThemeData checkboxTheme = CheckboxTheme.of(context);
- final CheckboxThemeData defaults = Theme.of(context).useMaterial3
- ? _CheckboxDefaultsM3(context)
- : _CheckboxDefaultsM2(context);
- final MaterialTapTargetSize effectiveMaterialTapTargetSize =
- widget.materialTapTargetSize ??
- checkboxTheme.materialTapTargetSize ??
- defaults.materialTapTargetSize!;
- final VisualDensity effectiveVisualDensity = widget.visualDensity ??
- checkboxTheme.visualDensity ??
- defaults.visualDensity!;
- Size sizeBeforeScale;
- switch (effectiveMaterialTapTargetSize) {
- case MaterialTapTargetSize.padded:
- sizeBeforeScale =
- Size(kMinInteractiveDimension.s, kMinInteractiveDimension.s);
- break;
- case MaterialTapTargetSize.shrinkWrap:
- sizeBeforeScale = const Size(
- kMinInteractiveDimension - 8.0, kMinInteractiveDimension - 8.0);
- break;
- }
- Size size = Size(
- (sizeBeforeScale.width + effectiveVisualDensity.baseSizeAdjustment.dx)
- .s,
- (sizeBeforeScale.height + effectiveVisualDensity.baseSizeAdjustment.dy)
- .s);
- final MaterialStateProperty<MouseCursor> effectiveMouseCursor =
- MaterialStateProperty.resolveWith<MouseCursor>(
- (Set<MaterialState> states) {
- return MaterialStateProperty.resolveAs<MouseCursor?>(
- widget.mouseCursor, states) ??
- checkboxTheme.mouseCursor?.resolve(states) ??
- MaterialStateMouseCursor.clickable.resolve(states);
- });
- // Colors need to be resolved in selected and non selected states separately
- // so that they can be lerped between.
- final Set<MaterialState> errorState = states..add(MaterialState.error);
- final Set<MaterialState> activeStates =
- widget.isError ? (errorState..add(MaterialState.selected)) : states
- ..add(MaterialState.selected);
- final Set<MaterialState> inactiveStates =
- widget.isError ? (errorState..remove(MaterialState.selected)) : states
- ..remove(MaterialState.selected);
- final Color? activeColor = widget.fillColor?.resolve(activeStates) ??
- _widgetFillColor.resolve(activeStates) ??
- checkboxTheme.fillColor?.resolve(activeStates);
- final Color effectiveActiveColor =
- activeColor ?? defaults.fillColor!.resolve(activeStates)!;
- final Color? inactiveColor = widget.fillColor?.resolve(inactiveStates) ??
- _widgetFillColor.resolve(inactiveStates) ??
- checkboxTheme.fillColor?.resolve(inactiveStates);
- final Color effectiveInactiveColor =
- inactiveColor ?? defaults.fillColor!.resolve(inactiveStates)!;
- final Set<MaterialState> focusedStates =
- widget.isError ? (errorState..add(MaterialState.focused)) : states
- ..add(MaterialState.focused);
- Color effectiveFocusOverlayColor =
- widget.overlayColor?.resolve(focusedStates) ??
- widget.focusColor ??
- checkboxTheme.overlayColor?.resolve(focusedStates) ??
- defaults.overlayColor!.resolve(focusedStates)!;
- final Set<MaterialState> hoveredStates =
- widget.isError ? (errorState..add(MaterialState.hovered)) : states
- ..add(MaterialState.hovered);
- Color effectiveHoverOverlayColor =
- widget.overlayColor?.resolve(hoveredStates) ??
- widget.hoverColor ??
- checkboxTheme.overlayColor?.resolve(hoveredStates) ??
- defaults.overlayColor!.resolve(hoveredStates)!;
- final Set<MaterialState> activePressedStates = activeStates
- ..add(MaterialState.pressed);
- final Color effectiveActivePressedOverlayColor =
- widget.overlayColor?.resolve(activePressedStates) ??
- checkboxTheme.overlayColor?.resolve(activePressedStates) ??
- activeColor?.withAlpha(kRadialReactionAlpha) ??
- defaults.overlayColor!.resolve(activePressedStates)!;
- final Set<MaterialState> inactivePressedStates = inactiveStates
- ..add(MaterialState.pressed);
- final Color effectiveInactivePressedOverlayColor =
- widget.overlayColor?.resolve(inactivePressedStates) ??
- checkboxTheme.overlayColor?.resolve(inactivePressedStates) ??
- inactiveColor?.withAlpha(kRadialReactionAlpha) ??
- defaults.overlayColor!.resolve(inactivePressedStates)!;
- if (downPosition != null) {
- effectiveHoverOverlayColor = states.contains(MaterialState.selected)
- ? effectiveActivePressedOverlayColor
- : effectiveInactivePressedOverlayColor;
- effectiveFocusOverlayColor = states.contains(MaterialState.selected)
- ? effectiveActivePressedOverlayColor
- : effectiveInactivePressedOverlayColor;
- }
- final Set<MaterialState> checkStates =
- widget.isError ? (states..add(MaterialState.error)) : states;
- final Color effectiveCheckColor = widget.checkColor ??
- checkboxTheme.checkColor?.resolve(checkStates) ??
- defaults.checkColor!.resolve(checkStates)!;
- final double effectiveSplashRadius = widget.splashRadius ??
- checkboxTheme.splashRadius ??
- defaults.splashRadius!.s;
- return Semantics(
- checked: widget.value ?? false,
- mixed: widget.tristate ? widget.value == null : null,
- child: buildToggleable(
- mouseCursor: effectiveMouseCursor,
- focusNode: widget.focusNode,
- autofocus: widget.autofocus,
- size: size,
- painter: _painter
- ..position = position
- ..reaction = reaction
- ..reactionFocusFade = reactionFocusFade
- ..reactionHoverFade = reactionHoverFade
- ..inactiveReactionColor = effectiveInactivePressedOverlayColor
- ..reactionColor = effectiveActivePressedOverlayColor
- ..hoverColor = effectiveHoverOverlayColor
- ..focusColor = effectiveFocusOverlayColor
- ..splashRadius = effectiveSplashRadius
- ..downPosition = downPosition
- ..isFocused = states.contains(MaterialState.focused)
- ..isHovered = states.contains(MaterialState.hovered)
- ..activeColor = effectiveActiveColor
- ..inactiveColor = effectiveInactiveColor
- ..checkColor = effectiveCheckColor
- ..value = value
- ..previousValue = _previousValue
- ..shape = widget.shape ??
- checkboxTheme.shape ??
- RoundedRectangleBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(1.s),
- ),
- )
- ..side =
- _resolveSide(widget.side) ?? _resolveSide(checkboxTheme.side),
- ),
- );
- }
- }
- class _CheckboxPainter extends ToggleablePainter {
- double get _kEdgeSize => 18.s;
- double get _kStrokeWidth => 2.s;
-
- Color get checkColor => _checkColor!;
- Color? _checkColor;
- set checkColor(Color value) {
- if (_checkColor == value) {
- return;
- }
- _checkColor = value;
- notifyListeners();
- }
- bool? get value => _value;
- bool? _value;
- set value(bool? value) {
- if (_value == value) {
- return;
- }
- _value = value;
- notifyListeners();
- }
- bool? get previousValue => _previousValue;
- bool? _previousValue;
- set previousValue(bool? value) {
- if (_previousValue == value) {
- return;
- }
- _previousValue = value;
- notifyListeners();
- }
- OutlinedBorder get shape => _shape!;
- OutlinedBorder? _shape;
- set shape(OutlinedBorder value) {
- if (_shape == value) {
- return;
- }
- _shape = value;
- notifyListeners();
- }
- BorderSide? get side => _side;
- BorderSide? _side;
- set side(BorderSide? value) {
- if (_side == value) {
- return;
- }
- _side = value;
- notifyListeners();
- }
- // The square outer bounds of the checkbox at t, with the specified origin.
- // At t == 0.0, the outer rect's size is _kEdgeSize (Checkbox.width)
- // At t == 0.5, .. is _kEdgeSize - _kStrokeWidth
- // At t == 1.0, .. is _kEdgeSize
- Rect _outerRectAt(Offset origin, double t) {
- final double inset = 1.0 - (t - 0.5).abs() * 2.0;
- final double size = _kEdgeSize - inset * _kStrokeWidth;
- final Rect rect =
- Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
- return rect;
- }
- // The checkbox's border color if value == false, or its fill color when
- // value == true or null.
- Color _colorAt(double t) {
- // As t goes from 0.0 to 0.25, animate from the inactiveColor to activeColor.
- return t >= 0.25
- ? activeColor
- : Color.lerp(inactiveColor, activeColor, t * 4.0)!;
- }
- // White stroke used to paint the check and dash.
- Paint _createStrokePaint() {
- return Paint()
- ..color = checkColor
- ..style = PaintingStyle.stroke
- ..strokeWidth = _kStrokeWidth;
- }
- void _drawBox(
- Canvas canvas, Rect outer, Paint paint, BorderSide? side, bool fill) {
- if (fill) {
- canvas.drawPath(shape.getOuterPath(outer), paint);
- }
- if (side != null) {
- shape.copyWith(side: side).paint(canvas, outer);
- }
- }
- void _drawCheck(Canvas canvas, Offset origin, double t, Paint paint) {
- assert(t >= 0.0 && t <= 1.0);
- // As t goes from 0.0 to 1.0, animate the two check mark strokes from the
- // short side to the long side.
- final Path path = Path();
- Offset start = Offset(_kEdgeSize * 0.15, _kEdgeSize * 0.45);
- Offset mid = Offset(_kEdgeSize * 0.4, _kEdgeSize * 0.7);
- Offset end = Offset(_kEdgeSize * 0.85, _kEdgeSize * 0.25);
- if (t < 0.5) {
- final double strokeT = t * 2.0;
- final Offset drawMid = Offset.lerp(start, mid, strokeT)!;
- path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
- path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
- } else {
- final double strokeT = (t - 0.5) * 2.0;
- final Offset drawEnd = Offset.lerp(mid, end, strokeT)!;
- path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
- path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
- path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
- }
- canvas.drawPath(path, paint);
- }
- void _drawDash(Canvas canvas, Offset origin, double t, Paint paint) {
- assert(t >= 0.0 && t <= 1.0);
- // As t goes from 0.0 to 1.0, animate the horizontal line from the
- // mid point outwards.
- Offset start = Offset(_kEdgeSize * 0.2, _kEdgeSize * 0.5);
- Offset mid = Offset(_kEdgeSize * 0.5, _kEdgeSize * 0.5);
- Offset end = Offset(_kEdgeSize * 0.8, _kEdgeSize * 0.5);
- final Offset drawStart = Offset.lerp(start, mid, 1.0 - t)!;
- final Offset drawEnd = Offset.lerp(mid, end, t)!;
- canvas.drawLine(origin + drawStart, origin + drawEnd, paint);
- }
- @override
- void paint(Canvas canvas, Size size) {
- paintRadialReaction(canvas: canvas, origin: size.center(Offset.zero));
- final Paint strokePaint = _createStrokePaint();
- final Offset origin = size / 2.0 - Size.square(_kEdgeSize) / 2.0 as Offset;
- final AnimationStatus status = position.status;
- final double tNormalized =
- status == AnimationStatus.forward || status == AnimationStatus.completed
- ? position.value
- : 1.0 - position.value;
- // Four cases: false to null, false to true, null to false, true to false
- if (previousValue == false || value == false) {
- final double t = value == false ? 1.0 - tNormalized : tNormalized;
- final Rect outer = _outerRectAt(origin, t);
- final Paint paint = Paint()..color = _colorAt(t);
- if (t <= 0.5) {
- final BorderSide border =
- side ?? BorderSide(width: 2.s, color: paint.color);
- _drawBox(canvas, outer, paint, border, false); // only paint the border
- } else {
- _drawBox(canvas, outer, paint, side, true);
- final double tShrink = (t - 0.5) * 2.0;
- if (previousValue == null || value == null) {
- _drawDash(canvas, origin, tShrink, strokePaint);
- } else {
- _drawCheck(canvas, origin, tShrink, strokePaint);
- }
- }
- } else {
- // Two cases: null to true, true to null
- final Rect outer = _outerRectAt(origin, 1.0);
- final Paint paint = Paint()..color = _colorAt(1.0);
- _drawBox(canvas, outer, paint, side, true);
- if (tNormalized <= 0.5) {
- final double tShrink = 1.0 - tNormalized * 2.0;
- if (previousValue ?? false) {
- _drawCheck(canvas, origin, tShrink, strokePaint);
- } else {
- _drawDash(canvas, origin, tShrink, strokePaint);
- }
- } else {
- final double tExpand = (tNormalized - 0.5) * 2.0;
- if (value ?? false) {
- _drawCheck(canvas, origin, tExpand, strokePaint);
- } else {
- _drawDash(canvas, origin, tExpand, strokePaint);
- }
- }
- }
- }
- }
- // Hand coded defaults based on Material Design 2.
- class _CheckboxDefaultsM2 extends CheckboxThemeData {
- _CheckboxDefaultsM2(BuildContext context)
- : _theme = Theme.of(context),
- _colors = Theme.of(context).colorScheme;
- final ThemeData _theme;
- final ColorScheme _colors;
- @override
- MaterialStateProperty<Color> get fillColor {
- return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return _theme.disabledColor;
- }
- if (states.contains(MaterialState.selected)) {
- return _colors.secondary;
- }
- return _theme.unselectedWidgetColor;
- });
- }
- @override
- MaterialStateProperty<Color> get checkColor {
- return MaterialStateProperty.all<Color>(const Color(0xFFFFFFFF));
- }
- @override
- MaterialStateProperty<Color?> get overlayColor {
- return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.pressed)) {
- return fillColor.resolve(states).withAlpha(kRadialReactionAlpha);
- }
- if (states.contains(MaterialState.hovered)) {
- return _theme.hoverColor;
- }
- if (states.contains(MaterialState.focused)) {
- return _theme.focusColor;
- }
- return Colors.transparent;
- });
- }
- @override
- double get splashRadius => kRadialReactionRadius;
- @override
- MaterialTapTargetSize get materialTapTargetSize =>
- _theme.materialTapTargetSize;
- @override
- VisualDensity get visualDensity => _theme.visualDensity;
- }
- // BEGIN GENERATED TOKEN PROPERTIES - Checkbox
- // Do not edit by hand. The code between the "BEGIN GENERATED" and
- // "END GENERATED" comments are generated from data in the Material
- // Design token database by the script:
- // dev/tools/gen_defaults/bin/gen_defaults.dart.
- // Token database version: v0_132
- class _CheckboxDefaultsM3 extends CheckboxThemeData {
- _CheckboxDefaultsM3(BuildContext context)
- : _theme = Theme.of(context),
- _colors = Theme.of(context).colorScheme;
- final ThemeData _theme;
- final ColorScheme _colors;
- @override
- MaterialStateProperty<Color> get fillColor {
- return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return _colors.onSurface.withOpacity(0.38);
- }
- if (states.contains(MaterialState.error)) {
- return _colors.error;
- }
- if (states.contains(MaterialState.selected)) {
- return _colors.primary;
- }
- if (states.contains(MaterialState.pressed)) {
- return _colors.onSurface;
- }
- if (states.contains(MaterialState.hovered)) {
- return _colors.onSurface;
- }
- if (states.contains(MaterialState.focused)) {
- return _colors.onSurface;
- }
- return _colors.onSurfaceVariant;
- });
- }
- @override
- MaterialStateProperty<Color> get checkColor {
- return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- if (states.contains(MaterialState.selected)) {
- return _colors.surface;
- }
- return Colors
- .transparent; // No icons available when the checkbox is unselected.
- }
- if (states.contains(MaterialState.selected)) {
- if (states.contains(MaterialState.error)) {
- return _colors.onError;
- }
- return _colors.onPrimary;
- }
- return Colors
- .transparent; // No icons available when the checkbox is unselected.
- });
- }
- @override
- MaterialStateProperty<Color> get overlayColor {
- return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.error)) {
- if (states.contains(MaterialState.pressed)) {
- return _colors.error.withOpacity(0.12);
- }
- if (states.contains(MaterialState.hovered)) {
- return _colors.error.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return _colors.error.withOpacity(0.12);
- }
- }
- if (states.contains(MaterialState.selected)) {
- if (states.contains(MaterialState.pressed)) {
- return _colors.onSurface.withOpacity(0.12);
- }
- if (states.contains(MaterialState.hovered)) {
- return _colors.primary.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return _colors.primary.withOpacity(0.12);
- }
- return Colors.transparent;
- }
- if (states.contains(MaterialState.pressed)) {
- return _colors.primary.withOpacity(0.12);
- }
- if (states.contains(MaterialState.hovered)) {
- return _colors.onSurface.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return _colors.onSurface.withOpacity(0.12);
- }
- return Colors.transparent;
- });
- }
- @override
- double get splashRadius => 20.s;
- @override
- MaterialTapTargetSize get materialTapTargetSize =>
- _theme.materialTapTargetSize;
- @override
- VisualDensity get visualDensity => _theme.visualDensity;
- }
- // END GENERATED TOKEN PROPERTIES - Checkbox
|