New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create cupertino checkbox #122244
Create cupertino checkbox #122244
Conversation
/// Note: in the Apple ecosystem, checkboxes are encouraged to only be used in | ||
/// macOS, not iOS. If a multi-selection component is needed, iOS encourages the | ||
/// developer to use Switches, or [CupertinoSwitch] instead, or find a creative | ||
/// custom solution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not confident on the wording here. I would like to warn developers of Apple's guidance, without discouraging them from doing what they would like to do.
FYI for iOS it's in the HIG but only implicitly (e.g. in tables https://developer.apple.com/design/human-interface-guidelines/components/layout-and-organization/lists-and-tables/#platform-considerations) |
@GroovinChip might be the best person to give feedback on this, he made the macOS checkbox on Flutter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just made an initial pass. Mostly it looks pretty good.
/// Note: in the Apple ecosystem, checkboxes are encouraged to only be used in | ||
/// macOS, not iOS. If a multi-selection component is needed, iOS encourages the | ||
/// developer to use Switches, or [CupertinoSwitch] instead, or find a creative | ||
/// custom solution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this?
/// Note: in the Apple ecosystem, checkboxes are encouraged to only be used in | |
/// macOS, not iOS. If a multi-selection component is needed, iOS encourages the | |
/// developer to use Switches, or [CupertinoSwitch] instead, or find a creative | |
/// custom solution. | |
/// In the Apple Human Interface Guidelines (HIG), checkboxes are encouraged for use on macOS, but not on iOS. | |
/// If a multi-selection component is needed on iOS, the HIG encourages the developer to use switches ([CupertinoSwitch] in Flutter) | |
/// instead, or to find a creative custom solution. |
Does it actively discourage checkboxes on iOS? If not, then I'd just say that "but is silent about their use on iOS".
And probably link the appropriate HIG guidelines. Also note that (lol!) our style guide discourages empty prose like "Note" "Note that", etc.
/// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it assert? If so, you should mention that it asserts in debug mode.
/// | ||
/// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// true this callback cycle from false to true to null. | |
/// true this callback cycle from false to true to null and back to false again. |
/// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also mention if it asserts in debug mode.
|
||
/// The color and width of the checkbox's border. | ||
/// | ||
/// If this property is null, then the side will be width 1. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If this property is null, then the side will be width 1. | |
/// If this property is null, then the side defaults to a width of 1. |
} | ||
} | ||
|
||
const double _kEdgeSize = CupertinoCheckbox.width; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't these use locations for this use CupertinoCheckbox.width
directly? It is also a const, and would show where it comes from.
|
||
void _drawCheck(Canvas canvas, Offset origin, Paint paint) { | ||
final Path path = Path(); | ||
const Offset start = Offset(_kEdgeSize * 0.25, _kEdgeSize * 0.52); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These numbers in the offsets should be converted to constants to make it easier to read/understand them. Or you could comment on how the values are derived right here in a comment if this is the only instance of the number.
import 'package:flutter/rendering.dart'; | ||
import 'package:flutter/widgets.dart'; | ||
|
||
/// A mixin for [StatefulWidget]s that implement ios-themed toggleable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// A mixin for [StatefulWidget]s that implement ios-themed toggleable | |
/// A mixin for [StatefulWidget]s that implement iOS-themed toggleable |
/// A mixin for [StatefulWidget]s that implement ios-themed toggleable | ||
/// controls (e.g.[Checkbox]es). | ||
/// | ||
/// The mixin implements the logic for toggling the control (e.g. when tapped). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The mixin implements the logic for toggling the control (e.g. when tapped). | |
/// The mixin implements the logic for toggling the control when tapped. |
/// the [buildToggleable]. [State] objects using this mixin should call that | ||
/// method from their [build] method. | ||
/// | ||
/// This mixin is used to implement the cupertino components for [Checkbox] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// This mixin is used to implement the cupertino components for [Checkbox] | |
/// This mixin is used to implement the Cupertino components for [Checkbox] |
|
||
@override | ||
Widget build(BuildContext context) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove extra blank line
focusNode: widget.focusNode, | ||
autofocus: widget.autofocus, | ||
onFocusChange: onFocusChange, | ||
size: const Size(48.0, 48.0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adaptive widgets don't need to be the same size, and I think it's probably better to have it conform to the standard interactive size for Cupertino, since that should fit better with other Cupertino widgets. It's not much different anyhow.
b501bdf
to
9beffb6
Compare
/// The color used if the checkbox is inactive. By default, | ||
/// [CupertinoColors.inactiveGray] is used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The color used if the checkbox is inactive. By default, | |
/// [CupertinoColors.inactiveGray] is used. | |
/// The color used if the checkbox is inactive. | |
/// | |
/// By default, [CupertinoColors.inactiveGray] is used. |
The first paragraph is used as a short description in the docs, and should only be one sentence long.
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#dartdoc-specific-requirements
|
||
void _drawCheck(Canvas canvas, Offset origin, Paint paint) { | ||
final Path path = Path(); | ||
// The ratios for the offsets below where found from looking at the checkbox |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The ratios for the offsets below where found from looking at the checkbox | |
// The ratios for the offsets below were found from looking at the checkbox |
import 'package:flutter/rendering.dart'; | ||
import 'package:flutter/widgets.dart'; | ||
|
||
/// A mixin for [StatefulWidget]s that implement iOS-themed toggleable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// A mixin for [StatefulWidget]s that implement iOS-themed toggleable | |
/// A mixin for [StatefulWidget]s that implements iOS-themed toggleable |
import 'package:flutter/widgets.dart'; | ||
|
||
/// A mixin for [StatefulWidget]s that implement iOS-themed toggleable | ||
/// controls (e.g.[Checkbox]es). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// controls (e.g.[Checkbox]es). | |
/// controls (e.g. [CupertinoCheckbox]es). |
Or describe how this class relates to the Material Checkbox
.
/// A mixin for [StatefulWidget]s that implement iOS-themed toggleable | ||
/// controls (e.g.[Checkbox]es). | ||
/// | ||
/// The mixin implements the logic for toggling the control when tapped. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The mixin implements the logic for toggling the control when tapped. | |
/// This mixin implements the logic for toggling the control when tapped. |
|
||
/// False if this control is "inactive" (not checked, off, or unselected). | ||
/// | ||
/// If value is true then the control "active" (checked, on, or selected). If |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If value is true then the control "active" (checked, on, or selected). If | |
/// If [value] is true then the control "active" (checked, on, or selected). If |
|
||
@override | ||
void initState() { | ||
super.initState(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this entire function, since it's only calling super.
/// a Toggleable. | ||
/// | ||
/// Subclasses must implement the [paint] method to draw the actual visuals of | ||
/// the Toggleable. In their [paint] method subclasses may call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// the Toggleable. In their [paint] method subclasses may call | |
/// the Toggleable. In their [paint] method, subclasses may call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to remove this sentence entirely, because I missed that I had deleted the method this is referring too.
/// If null, then the value of [CupertinoColors.white] is used. | ||
final Color? checkColor; | ||
|
||
/// If true the checkbox's [value] can be true, false, or null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If true the checkbox's [value] can be true, false, or null. | |
/// If true, the checkbox's [value] can be true, false, or null. |
/// 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] is asseted to not be null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If tristate is false (the default), [value] is asseted to not be null. | |
/// If tristate is false (the default), [value] must not be null, and [onChanged] will only toggle between true and false. |
9beffb6
to
0810582
Compare
@gspencergoog this is ready for review again 👍 |
3916a7b
to
9a5eec2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// * [Checkbox], the Material Design equivalent. | ||
/// * [CupertinoSwitch], a widget with semantics similar to [CupertinoCheckbox]. | ||
/// * [CupertinoSlider], for selecting a value in a range. | ||
/// * <https://developer.apple.com/design/human-interface-guidelines/components/selection-and-input/toggles/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe describe what this is a link to? (or make it into an actual Markdown link to the descriptive text?)
/// * [CupertinoSlider], for selecting a value in a range. | ||
/// * <https://developer.apple.com/design/human-interface-guidelines/components/selection-and-input/toggles/> | ||
class CupertinoCheckbox extends StatefulWidget { | ||
/// Creates a macOS styled checkbox. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Creates a macOS styled checkbox. | |
/// Creates a macOS-style checkbox. |
Just a tiny nit: when I read this the first time, I read it as a "styled checkbox" for macOS.
|
||
/// The color and width of the checkbox's border. | ||
/// | ||
/// If this property is null, then the side defaults to a width of 1. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If this property is null, then the side defaults to a width of 1. | |
/// If this property is null, then the side defaults to a one pixel wide | |
/// black, solid border. |
@override | ||
void paint(Canvas canvas, Size size) { | ||
final Paint strokePaint = _createStrokePaint(); | ||
final Offset origin = size / 2.0 - const Size.square(CupertinoCheckbox.width) / 2.0 as Offset; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too bad we have to cast here (but we do). Someday we should add a asOffset
to Size
and a asSize
to Offset
.
9a5eec2
to
f084121
Compare
f084121
to
6ccacb8
Compare
Addresses: #61789.
Adds a macOS/iOS styled checkbox widget to Cupertino. Works with keyboard focus, and keyboard interactions. The goal is to make this widget adaptable with the Material Checkbox.
Notably SwiftUI does not provide checkboxes for iOS development, only macOS, so our comparison tool could not be used. So we have to compare with desktop checkboxes and the checkboxes in the Human Interface Guidelines.
Some screenshots of the checkbox in the macOS settings, and from the Human Interface Guidelines:
Here is the CupertinoCheckbox in action, both with mouse and keyboard interactions:
Screen.Recording.2023-03-08.at.3.06.53.PM.mov
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.