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
Migrated Switch
to Material 3
#110095
Migrated Switch
to Material 3
#110095
Conversation
a128d1e
to
6be39fe
Compare
Any updates? |
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.
@QuncCccccc sorry this took so long to review. There is a lot here!
Looks great. Nice work. I have a question about the defaults vs theme data below.
In a future PR we should add an example or two showing how to use the new thumb image and icons, as well as update the demo app.
/// onChanged: (_) => true, | ||
/// thumbImage: MaterialStateProperty.resolveWith<ImageProvider>((Set<MaterialState> states) { | ||
/// if (states.contains(MaterialState.disabled)) { | ||
/// return MemoryImage(Uint8List.fromList(<int>[1, 2])); |
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.
For this example, perhaps just have disabledImage
and normalImage
variables for the images so that we don't have to complicate the example with the details of MemoryImage.
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.
Small question: do you really want thumbImage
? Can't I use Icon(Icons.check)
to do the check? And should there be only a single thumbImage
? In the Material specs they use x for unselected, check for selected.
@@ -1078,3 +1322,288 @@ class _SwitchPainter extends ToggleablePainter { | |||
super.dispose(); | |||
} | |||
} | |||
|
|||
class _SwitchDefaults { |
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.
So is the reason for this class because we have a bunch of values that need to change between M2 and M3, but they aren't in the SwitchThemeData? If so, perhaps we should just add them to the ThemeData so that apps can customize them if they want. That would be consistent with the way we handle other components. Although there is a lot here, that maybe we don't want to bloat the ThemeData with.
If we don't want to make them part of the ThemeData, then it might be cleaner to just have a _SwitchConfig
interface and have a hand coded _SwitchConfigM2
and the generated _SwitchConfigM3
that implement it. That will make it easier to ditch the M2 implementation when we switch over to M3 completely.
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.
Thanks a lot for the suggestions! Just created a separate config class for both M2 and M3. Please let me know if there're any problems:)
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 haven't finished reviewing this yet
if (states.contains(MaterialState.pressed)) { | ||
return ${componentColor('md.comp.switch.unselected.pressed.state-layer')}; | ||
} | ||
return 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.
Should this be Colors.transparent? How do we expect overlayColor=null to be interpreted in this case?
CC @darrenaustin - Maybe this is another case where we want null to mean "no overlay" instead of "no preference".
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.
When the switch is not in any of the states(hovered, focused, and pressed), should we just give a null value for the state layer?
BuildContext context; | ||
final ColorScheme _colors; | ||
|
||
static const double iconSize = ${tokens['md.comp.switch.unselected.icon.size']}; |
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.
Good to see all of these configuration parameters being derived from the token db.
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 looks great (good to see all of the tests). I realize that there's still feedback from Darren that needs to be resolved.
/// The optional icon on the thumb of this switch when the switch is on. | ||
/// | ||
/// This property can be null. | ||
final IconData? activeIcon; |
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.
There are more Switch color and thumb image configuration parameters than we need, given MaterialStateProperty. In a future PR and design doc, I think we should either migrate developers to a new switch widget, one that only has MaterialStateProperty parameters, or a new version of Switch that deprecates all of the excess baggage.
@@ -441,11 +498,11 @@ class Switch extends StatelessWidget { | |||
height: size.height, | |||
alignment: Alignment.center, | |||
child: CupertinoSwitch( |
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.
Similar to my comment about Switch's icon and color properties: in the future, perhaps we can avoid delegating to a Cupertino library widget, and just configure the Material Switch widget instead.
6d40c0b
to
f720896
Compare
@bernaferrari (per #110095 (comment)) I agree that just using "thumb" instead of "thumbImage" would make the API more concise, using thumbImage has two advantages:
|
Yeah, but for me the "image" part is kind of confusing as it is not really an image when it is something else. An icon is supposedly different from an image. So I think something like thumbForeground could make more sense so people know anything can go there. |
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.
Looks good. I a have a couple of small comments and a question about thumbImage
below.
@@ -1217,7 +1217,7 @@ class ThemeData with Diagnosticable { | |||
/// * Typography: `typography` (see table above) | |||
/// | |||
/// ### Components | |||
/// * Common buttons: [ElevatedButton], [FilledButton], [OutlinedButton], [TextButton] | |||
/// * Common buttons: [ElevatedButton], [FilledButton], [OutlinedButton], [TextButton], [IconButton] |
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.
Ah we missed this one. Nice catch.
if (thumbWidget is Image) { | ||
effectiveActiveThumbImage = (widget.thumbImage?.resolve(activeStates) as Image?)?.image; | ||
effectiveInactiveThumbImage = (widget.thumbImage?.resolve(inactiveStates) as Image?)?.image; | ||
} | ||
final Widget? thumbThemeWidget = switchTheme.thumbImage?.resolve(states); | ||
effectiveActiveThumbImage ??= _widgetThumbImage.resolve(activeStates); | ||
effectiveInactiveThumbImage ??= _widgetThumbImage.resolve(inactiveStates); | ||
if (thumbThemeWidget is Image) { | ||
effectiveActiveThumbImage ??= (switchTheme.thumbImage?.resolve(activeStates) as Image?)?.image; | ||
effectiveInactiveThumbImage ??= (switchTheme.thumbImage?.resolve(inactiveStates) as Image?)?.image; | ||
} | ||
|
||
Icon? effectiveActiveIcon; | ||
Icon? effectiveInactiveIcon; | ||
if (theme.useMaterial3) { | ||
if (thumbWidget is Icon) { | ||
effectiveActiveIcon = widget.thumbImage?.resolve(activeStates) as Icon?; | ||
effectiveInactiveIcon = widget.thumbImage?.resolve(inactiveStates) as Icon?; | ||
} | ||
if (thumbThemeWidget is Icon) { | ||
effectiveActiveIcon ??= switchTheme.thumbImage?.resolve(activeStates) as Icon?; | ||
effectiveInactiveIcon ??= switchTheme.thumbImage?.resolve(inactiveStates) as Icon?; | ||
} | ||
} |
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 is thumbImage
used if it is not an Icon
or Image
? What if it was just a label or a container with a shape and color? Will it be rendered on the thumb of the switch?
Or do we only support image and icon widgets for thumbImage
? If so we would at least need to document that restriction.
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.
Yeah, the thumbImage
currently only supports Image
and Icon
. Investigating how we can paint a widget directly. Thanks for the suggestion!
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 is the challenge, because it uses a BoxDecoration and pre-renders/caches it. I don't know how to improve that.
_cachedThumbPainter = _createDefaultThumbDecoration(thumbColor, thumbImage, thumbErrorListener).createBoxPainter(_handleDecorationChanged);
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.
Yes, this is tricky. But I agree that the restriction that only allows 2 types here is a little odd. To support the optional icon feature, I changed the thumbImage
to the thumbIcon
, and just used the type of MaterialStateProperty<Icon?>?
instead of MaterialStateProperty<Widget?>?
. Eventually, we will refactor this part of the code and allow a widget painting on the thumb of Switch.
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.
Perfect!
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.
LGTM
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.
Part of: #91605
Updated the
Switch
widget with support for Material Design 3.In order to use the
Switch
with the new Material 3 defaults, turn on theuseMaterial3
flag in theThemeData
:Fixes: #103536
Pre-launch Checklist
///
).