diff --git a/packages/flutter/lib/src/material/button.dart b/packages/flutter/lib/src/material/button.dart index 1cc1452e36..27828fedab 100644 --- a/packages/flutter/lib/src/material/button.dart +++ b/packages/flutter/lib/src/material/button.dart @@ -130,6 +130,8 @@ class MaterialButton extends StatefulWidget { this.textTheme, this.textColor, this.color, + this.highlightColor, + this.splashColor, this.elevation, this.highlightElevation, this.minWidth, @@ -152,9 +154,40 @@ class MaterialButton extends StatefulWidget { /// The color to use for this button's text. final Color textColor; - /// The color of the button, as printed on the [Material]. + /// The primary color of the button, as printed on the [Material], while it + /// is in its default (unpressed, enabled) state. + /// + /// Defaults to null, meaning that the color is automatically derived from the [Theme]. + /// + /// Typically, a material design color will be used, as follows: + /// + /// ```dart + /// new MaterialButton( + /// color: Colors.blue[500], + /// onPressed: _handleTap, + /// child: new Text('DEMO'), + /// ), + /// ``` final Color color; + /// The primary color of the button when the button is in the down (pressed) state. + /// The splash is represented as a circular overlay that appears above the + /// [highlightColor] overlay. The splash overlay has a center point that matches + /// the hit point of the user touch event. The splash overlay will expand to + /// fill the button area if the touch is held for long enough time. If the splash + /// color has transparency then the highlight and button color will show through. + /// + /// Defaults to the splash color from the [Theme]. + final Color splashColor; + + /// The secondary color of the button when the button is in the down (pressed) + /// state. The higlight color is represented as a solid color that is overlaid over the + /// button color (if any). If the highlight color has transparency, the button color + /// will show through. The highlight fades in quickly as the button is held down. + /// + /// Defaults to the highlight color from the [Theme]. + final Color highlightColor; + /// The z-coordinate at which to place this button. /// /// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12, 16, 24 @@ -250,8 +283,9 @@ class _MaterialButtonState extends State { @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); + final ThemeData theme = Theme.of(context); final Color textColor = _textColor; - final TextStyle style = Theme.of(context).textTheme.button.copyWith(color: textColor); + final TextStyle style = theme.textTheme.button.copyWith(color: textColor); final ButtonTheme buttonTheme = ButtonTheme.of(context); final double height = config.height ?? buttonTheme.height; final int elevation = (_highlight ? config.highlightElevation : config.elevation) ?? 0; @@ -261,6 +295,8 @@ class _MaterialButtonState extends State { color: textColor ), child: new InkWell( + highlightColor: config.highlightColor ?? theme.highlightColor, + splashColor: config.splashColor ?? theme.splashColor, onTap: config.onPressed, onHighlightChanged: _handleHighlightChanged, child: new Container( diff --git a/packages/flutter/lib/src/material/flat_button.dart b/packages/flutter/lib/src/material/flat_button.dart index 60bc721e2c..57604a2604 100644 --- a/packages/flutter/lib/src/material/flat_button.dart +++ b/packages/flutter/lib/src/material/flat_button.dart @@ -53,6 +53,8 @@ class FlatButton extends StatelessWidget { this.textColor, this.disabledTextColor, this.color, + this.highlightColor, + this.splashColor, this.disabledColor, this.textTheme, this.colorBrightness, @@ -76,8 +78,10 @@ class FlatButton extends StatelessWidget { /// Defaults to a color derived from the [Theme]. final Color disabledTextColor; - /// The color of the button, as printed on the [Material]. Defaults to null, - /// meaning that the color is automatically derived from the [Theme]. + /// The primary color of the button, as printed on the [Material], while it + /// is in its default (unpressed, enabled) state. + /// + /// Defaults to null, meaning that the color is automatically derived from the [Theme]. /// /// Typically, a material design color will be used, as follows: /// @@ -90,6 +94,24 @@ class FlatButton extends StatelessWidget { /// ``` final Color color; + /// The primary color of the button when the button is in the down (pressed) state. + /// The splash is represented as a circular overlay that appears above the + /// [highlightColor] overlay. The splash overlay has a center point that matches + /// the hit point of the user touch event. The splash overlay will expand to + /// fill the button area if the touch is held for long enough time. If the splash + /// color has transparency then the highlight and button color will show through. + /// + /// Defaults to the splash color from the [Theme]. + final Color splashColor; + + /// The secondary color of the button when the button is in the down (pressed) + /// state. The higlight color is represented as a solid color that is overlaid over the + /// button color (if any). If the highlight color has transparency, the button color + /// will show through. The highlight fades in quickly as the button is held down. + /// + /// Defaults to the highlight color from the [Theme]. + final Color highlightColor; + /// The color of the button when the button is disabled. Buttons are disabled /// by default. To enable a button, set its [onPressed] property to a non-null /// value. @@ -120,6 +142,8 @@ class FlatButton extends StatelessWidget { onPressed: onPressed, textColor: enabled ? textColor : disabledTextColor, color: enabled ? color : disabledColor, + highlightColor: highlightColor ?? Theme.of(context).highlightColor, + splashColor: splashColor ?? Theme.of(context).splashColor, textTheme: textTheme, colorBrightness: colorBrightness, child: child diff --git a/packages/flutter/lib/src/material/ink_splash.dart b/packages/flutter/lib/src/material/ink_splash.dart index 39a8907441..a0bdd4d4f6 100644 --- a/packages/flutter/lib/src/material/ink_splash.dart +++ b/packages/flutter/lib/src/material/ink_splash.dart @@ -107,7 +107,6 @@ class InkSplash extends InkFeature { } final Point _position; - final Color _color; final double _targetRadius; final RectCallback _clipCallback; final bool _repositionToReferenceBox; @@ -118,6 +117,17 @@ class InkSplash extends InkFeature { Animation _alpha; AnimationController _alphaController; + /// The color of the splash. + Color get color => _color; + Color _color; + set color(Color value) { + if (value == _color) + return; + _color = value; + controller.markNeedsPaint(); + } + + /// The user input is confirmed. /// /// Causes the reaction to propagate faster across the material. diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart index 845f45929c..2a00ff932b 100644 --- a/packages/flutter/lib/src/material/ink_well.dart +++ b/packages/flutter/lib/src/material/ink_well.dart @@ -218,7 +218,9 @@ class _InkResponseState extends State { @override Widget build(BuildContext context) { assert(config.debugCheckContext(context)); - _lastHighlight?.color = Theme.of(context).highlightColor; + final ThemeData themeData = Theme.of(context); + _lastHighlight?.color = config.highlightColor ?? themeData.highlightColor; + _currentSplash?.color = config.splashColor ?? themeData.splashColor; final bool enabled = config.onTap != null || config.onDoubleTap != null || config.onLongPress != null; return new GestureDetector( onTapDown: enabled ? _handleTapDown : null, diff --git a/packages/flutter/lib/src/material/raised_button.dart b/packages/flutter/lib/src/material/raised_button.dart index bd0ac0cdc3..8650e05b2e 100644 --- a/packages/flutter/lib/src/material/raised_button.dart +++ b/packages/flutter/lib/src/material/raised_button.dart @@ -40,6 +40,8 @@ class RaisedButton extends StatelessWidget { Key key, @required this.onPressed, this.color, + this.highlightColor, + this.splashColor, this.disabledColor, this.elevation: 2, this.highlightElevation: 8, @@ -53,8 +55,12 @@ class RaisedButton extends StatelessWidget { /// If this is set to null, the button will be disabled. final VoidCallback onPressed; - /// The color of the button, as printed on the [Material]. Defaults to null, - /// meaning that the color is automatically derived from the [Theme]. + /// The primary color of the button, as printed on the [Material], while it + /// is in its default (unpressed, enabled) state. + /// + /// Defaults to null, meaning that the color is automatically derived from the [Theme]. + /// + /// Typically, a material design color will be used, as follows: /// /// ```dart /// new RaisedButton( @@ -65,6 +71,25 @@ class RaisedButton extends StatelessWidget { /// ``` final Color color; + /// The primary color of the button when the button is in the down (pressed) state. + /// The splash is represented as a circular overlay that appears above the + /// [highlightColor] overlay. The splash overlay has a center point that matches + /// the hit point of the user touch event. The splash overlay will expand to + /// fill the button area if the touch is held for long enough time. If the splash + /// color has transparency then the highlight and button color will show through. + /// + /// Defaults to the splash color from the [Theme]. + final Color splashColor; + + /// The secondary color of the button when the button is in the down (pressed) + /// state. The higlight color is represented as a solid color that is overlaid over the + /// button color (if any). If the highlight color has transparency, the button color + /// will show through. The highlight fades in quickly as the button is held down. + /// + /// Defaults to the highlight color from the [Theme]. + final Color highlightColor; + + /// The color of the button when the button is disabled. Buttons are disabled /// by default. To enable a button, set its [onPressed] property to a non-null /// value. @@ -129,6 +154,8 @@ class RaisedButton extends StatelessWidget { return new MaterialButton( onPressed: onPressed, color: _getColor(context), + highlightColor: highlightColor ?? Theme.of(context).highlightColor, + splashColor: splashColor ?? Theme.of(context).splashColor, elevation: enabled ? elevation : disabledElevation, highlightElevation: enabled ? highlightElevation : disabledElevation, colorBrightness: colorBrightness, diff --git a/packages/flutter/test/widgets/buttons_test.dart b/packages/flutter/test/widgets/buttons_test.dart index d5e355c8d3..4126a94b0a 100644 --- a/packages/flutter/test/widgets/buttons_test.dart +++ b/packages/flutter/test/widgets/buttons_test.dart @@ -40,4 +40,103 @@ void main() { semantics.dispose(); }); + + + Widget _buttonWidget({ + Key buttonKey, + Key materialKey, + Color color, Color + highlightColor, + Color splashColor, + double minWidth = 150.0, + double height = 60.0, + bool useTheme = false + }) { + + final Key definedMaterialKey = materialKey ?? new UniqueKey(); + final Key definedButtonKey = buttonKey ?? new UniqueKey(); + + Widget buttonWidget = new Material( + key: definedMaterialKey, + child: new Center( + child: new MaterialButton( + key: definedButtonKey, + color: color, + highlightColor: !useTheme ? highlightColor : null, + splashColor: !useTheme ? splashColor : null, + minWidth: minWidth, + height: height, + onPressed: () { }, + ), + ), + ); + if (useTheme) { + final ThemeData themeData = new ThemeData( + accentColor: color, + highlightColor: highlightColor, + splashColor: splashColor, + ); + buttonWidget = new Theme( + data: themeData, + child: buttonWidget, + ); + } + return buttonWidget; + } + + testWidgets('Does button highlight + splash colors work if set directly', (WidgetTester tester) async { + final Color buttonColor = new Color(0xFFFFFF00); + final Color highlightColor = new Color(0xDD0000FF); + final Color splashColor = new Color(0xAA0000FF); + + final Key materialKey = new UniqueKey(); + final Key buttonKey = new UniqueKey(); + + await tester.pumpWidget( + _buttonWidget( + materialKey: materialKey, + buttonKey: buttonKey, + color: buttonColor, + highlightColor: highlightColor, + splashColor: splashColor, + ), + ); + + final Point center = tester.getCenter(find.byKey(buttonKey)); + final TestGesture gesture = await tester.startGesture(center); + await tester.pump(new Duration(milliseconds: 200)); + + // TODO(ianh) - the object returned by renderObject does not contain splash or highlights (??) + + await gesture.up(); + }); + + testWidgets('Does button highlight color work if set via theme', (WidgetTester tester) async { + final Color buttonColor = new Color(0xFFFFFF00); + final Color highlightColor = new Color(0xDD0000FF); + final Color splashColor = new Color(0xAA0000FF); + + final Key materialKey = new UniqueKey(); + final Key buttonKey = new UniqueKey(); + + await tester.pumpWidget( + _buttonWidget( + useTheme: true, // use a theme wrapper + materialKey: materialKey, + buttonKey: buttonKey, + color: buttonColor, + highlightColor: highlightColor, + splashColor: splashColor, + ), + ); + + final Point center = tester.getCenter(find.byKey(buttonKey)); + final TestGesture gesture = await tester.startGesture(center); + await tester.pump(new Duration(milliseconds: 200)); + + // TODO(ianh) - the object returned by renderObject does not contain splash or highlights (??) + + await gesture.up(); + }); + }