-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Added ColorScheme, updated ThemeData, ButtonTheme, material buttons #22013
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
Conversation
ed04506 to
6bc1dda
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.
In general, I'm a little skeptical of string based APIs. I can definitely see the value in essentially having a tag/selector like value for determining theme values though. Is there an advantage over using an enumeration - besides visibility?
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 agree. Enums have some serious disadvantages which put me off finding a better solution (than Strings):
- They can't be extended. The "type" of subclasses of MaterialButton will be limited to the enum values we define.
- They're difficult for us to extend. Adding an enum value later will break code that switches on the value and doesn't include a default clause.
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.
Some general concerns:
-
are we doing too much work here? For example, getTextTheme is called 5 or 6 times in various parts of the buttonTheme helper methods.
-
are the tags useful or a bit of a hack?
Consider an alternative design, where instead of having tags we have n different functions which take a MaterialButton and return some RawMaterialButton or other widget. This might be worse in someways, but I think it connects the tag directly to the produced theme value. It also still allows us to avoid computing theme values we don't need while avoiding repetitive calculations.
Might not matter
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 don't think we're actually doing too much work, although it's true that some small computations are done repeatedly.
The current design, with its "type" property, is a compromise. It avoids the API explosion that would come from having a complete set of methods for RaisedButton, OutlineButton, FlatButton (and perhaps IconButton and FloatingActionButton in the future).
b5fc167 to
eebb5c8
Compare
|
@willlarche Currently the ColorScheme colors don't have doc strings. I'd appreciate it if you'd suggest some. Currently |
24e24d1 to
9cbaefb
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.
As long as button is exactly a FlatButton, I think it is faster to check the runtimeType than use an is check. Since the latter has to verify the full type hierarchy.
You could also pass the type as method generic:
double getElevation<T extends materialButton>(T button) {
if (T == FlatButton) { ... }
}
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 guess the above wouldn't work if you wanted to delegate to MaterialButton, and it might not be obvious but inference could mess up users here:
class MyButton extends MaterialButton {
Widget build(_) => RawMaterialButton(foo: theme.getFoo(this)) // <- T is inferred as MyButton instead of MaterialButton
}
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 someone wants to write a button that's configured like a FlatButton but builds a little differently, they could subclass FlatButton and override its build method. FlatButtonWithIcon is essentially that.
The generic approach would be a little cheaper, and it would obviate creating classes like FlatButtonWithIcon. It's similar to an earlier approach I'd taken, where a value that represented the button's type was passed along as a MaterialButton property. It makes using the API a bit clunkier, although it also clarifies the meaning of each getFoo(button) method: getElevation(myFlatButton) is a little clearer that we want the FlatButton elevation.
I don't think this would be practical problem with the type parameter approach:
class MyButton extends MaterialButton {
Widget build(_) => RawMaterialButton(foo: theme.getFoo(this)) // <- T is inferred as MyButton instead of MaterialButton
}
Only because ButtonThemeData doesn't discriminate based on is MaterialButton. The same scenario with a subclass of a type that is part of the rules, say FlatButton, would have to use theme.getFoo<FlatButton>(), to get the "right" answer. I don't know if that qualifies as a footgun or not.
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 think the advantage of the is checks is that they will just work. If it's slow we can always revisit
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.
Since this is almost a one-liner I would use ?? instead.
|
@ajsecord can you resolve the questions in #22013 (comment) ? |
|
I can work with design to get descriptions of each of these colors. Don't block on me, I'll send a PR with the results. I agree about |
|
@HansMuller if you create an issue for the doc strings and assign me we'll have a better chance of landing them. |
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 have an idea to both reduce the number of is checks for the isIcon case and allow users to correctly extend an IconTheme.
Define an empty mixin called MaterialIconButton, or similar.
/// Something something indicates that we use icon theme.s
mixin MaterialIconButton {}
Then make FlatButtonWithIcon and friends mix this in instead of being public class/private constructor.
_FlatButtonWithIcon extends FlatButton with MaterialIconButton { ... }
You can then simplify the isIcon check to a single is:
bool _isIconButton(MaterialButton button) => button is MaterialIconButton
And users can mixin this class to make custom subclasses take the icon config as a bonus.
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 idea. I added MaterialButtonWithIconMixin and made the FooButtonWithIcon classes private.
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.
Nit: here and else where it should be a lowercase null-the-value instead of uppcase Null-the-type
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.
OK, done.
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 clarity, I would place the "If" first and drop the otherwise.
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 tried various permutations of this doc. It needs to convey the fact that the first non-null production is returned (I used "otherwise"). I agree that it's a little clunky as presented, and I wish the derivation of fillColor wasn't so complicated, but I haven't been able to improve it.
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 concerns me a bit, we won't break tests that look for OutlineButton will we?
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 point. The tricky Finder wasn't really needed, I've removed it.
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 think we should just leave this as part of the regular theme. While it needs to exist for backwards compatibility, ideally we don't want new apps to find it easy to set to anything besides padded.
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.
It's already a property of the button classes and it needs to be funneled through ButtonTheme so that buttons can (just) depend on ButtonThemeData and not on the overall Theme.
I've made the ButtonThemeData.materialTapTargetSize property private. It's initialized by the constructor, which will usually be called by ThemeData, and returned by getMaterialTapTargetSize() as before.
It seemed like a good idea to similarly not expose the new (color) ButtonThemeData color parameters as properties, since they're available via the getFoo(button) methods. I've made them private as well.
9cbaefb to
2090b3d
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.
I believe you can use the new mixin syntax now: mixin MaterialButtonWithIconMixin { } right @yjbanov ?
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 syntax appears to work.
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 analyzer and the linter still produce warnings on some valid code, but if you are not triggering those warnings, you should be OK.
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.
Is there a way we could detect a new material theme and switch to the constrained layout behavior?
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.
New themes can specify constrained layout behavior.
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.
Nit: here and next method, extra space
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.
Fixed
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.
Seems like this doc comment got duplicated from above
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.
Oops, copy and pasteo.
jonahwilliams
left a comment
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 modulo nits and documentation for color scheme (now or later..)
2090b3d to
57e6e48
Compare
| mixin MaterialButtonWithIconMixin { | ||
| // This class is intended to be used as a mixin, and should not be | ||
| // extended directly. | ||
| factory MaterialButtonWithIconMixin._() => 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.
You can remove the factory constructor trick, since it isn't possible to instantiate a mixin
|
This PR broke a devicelab test because not all public members are documented: https://flutter-dashboard.appspot.com/api/get-log?ownerKey=ahNzfmZsdXR0ZXItZGFzaGJvYXJkclgLEglDaGVja2xpc3QiOGZsdXR0ZXIvZmx1dHRlci9lZWEzNDY1YWU3NWFjNTQ0NWM1NDQ5MGM1NDIwNDcwMzJmOGY4MzE0DAsSBFRhc2sYgICAgICAhAgM |
|
Fixed the dartdoc problems: #22592 |
|
Can we rename the onFoo colors (e.g. onSecondary) to something that doesn't look like a callback per the style guide naming convention? |
|
Those are the official names from the guidelines so probably more confusing
to change them than keep them.
…On Mon, Oct 8, 2018 at 19:59 Ian Hickson ***@***.***> wrote:
Can we rename the onFoo colors (e.g. onSecondary) to something that
doesn't look like a callback per the style guide naming convention?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#22013 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABNm5RXIZ6OjtJa13AoN9e78MJJ-2UDgks5ui-bxgaJpZM4WvM96>
.
|
|
The onFoo naming convention is described in the MD spec: https://material.io/design/color/the-color-system.html, search for: "On" colors (including the double quotes). It's hard to say how confusing the spec'd names would actually be, given the clash with our conventions. We could call them secondaryContrast (but that sounds like an aspect of the secondary color) or if we were seeking new naming lows "constrastsWithSecondary" or "legibleWhenPaintedOnSecondary". Maybe secondaryForeground? |
…uttons (flutter#22013)" This reverts commit eea3465.
For G3 Roll: * Revert "MaterialButton must honor its minWidth and height parameters (#22919)" This reverts commit a023323. * Revert "Update uses of ButtonTheme.bar: pass along the current Theme's colorScheme (#22827)" This reverts commit 655bf6a. * Revert "ButtonTheme.of().colorScheme defers to Theme (#22880)" This reverts commit a590940. * Revert "Bring TextTheme into alignment with the current Material spec (#22330)" This reverts commit 8bfb4b3. * Revert "Added ColorScheme, updated ThemeData, ButtonTheme, material buttons (#22013)" This reverts commit eea3465. * Manual adjustments to fix reversion issues.

The goal of this PR is to introduce an overall material "color scheme" and to make it possible to redefine how a component's visual properties bind to the color scheme and to the existing material Theme.
This PR addresses the goal, but only for RaisedButton, FlatButton, OutlineButton. Hopefully it defines a pattern that can be extended to other components.
The new ColorScheme class is essentially a struct with 13 Material Design colors. ThemeData and ButtonThemeData now have-a ColorScheme. In theory all of the colors in all of the components are defined by these colors. In practice, for the sake backwards compatibility, the Theme's buttonColor, disabledColor, highlightColor, and splashColor are also used if specified.
With one exception (the button text style) all of the buttons' visual properties are computed by ButtonThemeData. In all cases widget constructor parameters override the theme values.
All three button classes now inherit from MaterialButton. The
.iconfactory constructors, likeRaisedButton.icon()create a new subclass, likeRaisedButtonWithIcon, so that ButtonTheme can always compute colors based on the "type" of its MaterialButton.All three button classes just configure an internal RawMaterialButton (as before). RawMaterialButtons do not depend on any theme.
The following new ButtonThemeData methods are used to configure the RawMaterialButton:
This is RaisedButton's build method (it's typical).
One consequence of moving this code from the button classes to ButtonTheme has been to eliminate some duplicate code.
When Theme is an InheritedModel, buttons will only depend on its 'ButtonTheme' aspect.
This PR introduces some (hopefully small) visual incompatibilities.
Color(0x66BCBCBC).Color(0x66C8C8C8).There's also one significant concession to backwards compatibility:
ButtonTheme.buttonColor. This value is only used as the fill color for RaisedButtons. Its old default value,Colors.grey[300]is used instead of the button theme'sColorScheme.primarycolor. Apps can avoid the concession by configuring their Theme's buttonTheme.buttonColor with the primary color (the Flutter Gallery does this).Still TBD (although probably not in this PR):
ThemeData.copyWith() doesn't neccessarily do what you want. If its ColorScheme or one of the colors that ButtonTheme depends on is changed, then it will be necessary to rebuild the ButtonTheme. But
copyWithis intended to be a simple field copying method. So we'll need a.applycopying method, as with TextTheme.