Unverified Commit 74489206 authored by Anas35's avatar Anas35 Committed by GitHub

Extend Toggle Button's fill color with MaterialState (#82026)

parent f76b2926
...@@ -9,8 +9,10 @@ import 'package:flutter/rendering.dart'; ...@@ -9,8 +9,10 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button.dart'; import 'button.dart';
import 'color_scheme.dart';
import 'constants.dart'; import 'constants.dart';
import 'debug.dart'; import 'debug.dart';
import 'material_state.dart';
import 'theme.dart'; import 'theme.dart';
import 'theme_data.dart'; import 'theme_data.dart';
import 'toggle_buttons_theme.dart'; import 'toggle_buttons_theme.dart';
...@@ -284,6 +286,13 @@ class ToggleButtons extends StatelessWidget { ...@@ -284,6 +286,13 @@ class ToggleButtons extends StatelessWidget {
/// ToggleButtonTheme.of(context).fillColor is used. If /// ToggleButtonTheme.of(context).fillColor is used. If
/// [ToggleButtonsThemeData.fillColor] is also null, then /// [ToggleButtonsThemeData.fillColor] is also null, then
/// the fill color is null. /// the fill color is null.
///
/// If fillColor is a [MaterialStateProperty<Color>], then [MaterialStateProperty.resolve]
/// is used for the following [MaterialState]s:
///
/// * [MaterialState.disabled]
/// * [MaterialState.selected]
///
final Color? fillColor; final Color? fillColor;
/// The color to use for filling the button when the button has input focus. /// The color to use for filling the button when the button has input focus.
...@@ -654,7 +663,7 @@ class ToggleButtons extends StatelessWidget { ...@@ -654,7 +663,7 @@ class ToggleButtons extends StatelessWidget {
color: color, color: color,
selectedColor: selectedColor, selectedColor: selectedColor,
disabledColor: disabledColor, disabledColor: disabledColor,
fillColor: fillColor ?? toggleButtonsTheme.fillColor, fillColor: fillColor,
focusColor: focusColor ?? toggleButtonsTheme.focusColor, focusColor: focusColor ?? toggleButtonsTheme.focusColor,
highlightColor: highlightColor ?? toggleButtonsTheme.highlightColor, highlightColor: highlightColor ?? toggleButtonsTheme.highlightColor,
hoverColor: hoverColor ?? toggleButtonsTheme.hoverColor, hoverColor: hoverColor ?? toggleButtonsTheme.hoverColor,
...@@ -852,23 +861,39 @@ class _ToggleButton extends StatelessWidget { ...@@ -852,23 +861,39 @@ class _ToggleButton extends StatelessWidget {
/// The button's label, which is usually an [Icon] or a [Text] widget. /// The button's label, which is usually an [Icon] or a [Text] widget.
final Widget child; final Widget child;
Color _resolveColor(Set<MaterialState> states, MaterialStateProperty<Color?> widgetColor,
MaterialStateProperty<Color?> themeColor, MaterialStateProperty<Color> defaultColor) {
return widgetColor.resolve(states)
?? themeColor.resolve(states)
?? defaultColor.resolve(states);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
final Color currentColor; final Color currentColor;
final Color currentFillColor;
Color? currentFocusColor; Color? currentFocusColor;
Color? currentHoverColor; Color? currentHoverColor;
Color? currentSplashColor; Color? currentSplashColor;
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final ToggleButtonsThemeData toggleButtonsTheme = ToggleButtonsTheme.of(context); final ToggleButtonsThemeData toggleButtonsTheme = ToggleButtonsTheme.of(context);
final Set<MaterialState> states = <MaterialState>{
if (selected && onPressed != null) MaterialState.selected,
if (onPressed == null) MaterialState.disabled,
};
final Color currentFillColor = _resolveColor(
states,
_ResolveFillColor(fillColor),
_ResolveFillColor(toggleButtonsTheme.fillColor),
_DefaultFillColor(theme.colorScheme),
);
if (onPressed != null && selected) { if (onPressed != null && selected) {
currentColor = selectedColor currentColor = selectedColor
?? toggleButtonsTheme.selectedColor ?? toggleButtonsTheme.selectedColor
?? theme.colorScheme.primary; ?? theme.colorScheme.primary;
currentFillColor = fillColor
?? theme.colorScheme.primary.withOpacity(0.12);
currentFocusColor = focusColor currentFocusColor = focusColor
?? toggleButtonsTheme.focusColor ?? toggleButtonsTheme.focusColor
?? theme.colorScheme.primary.withOpacity(0.12); ?? theme.colorScheme.primary.withOpacity(0.12);
...@@ -882,7 +907,6 @@ class _ToggleButton extends StatelessWidget { ...@@ -882,7 +907,6 @@ class _ToggleButton extends StatelessWidget {
currentColor = color currentColor = color
?? toggleButtonsTheme.color ?? toggleButtonsTheme.color
?? theme.colorScheme.onSurface.withOpacity(0.87); ?? theme.colorScheme.onSurface.withOpacity(0.87);
currentFillColor = theme.colorScheme.surface.withOpacity(0.0);
currentFocusColor = focusColor currentFocusColor = focusColor
?? toggleButtonsTheme.focusColor ?? toggleButtonsTheme.focusColor
?? theme.colorScheme.onSurface.withOpacity(0.12); ?? theme.colorScheme.onSurface.withOpacity(0.12);
...@@ -896,7 +920,6 @@ class _ToggleButton extends StatelessWidget { ...@@ -896,7 +920,6 @@ class _ToggleButton extends StatelessWidget {
currentColor = disabledColor currentColor = disabledColor
?? toggleButtonsTheme.disabledColor ?? toggleButtonsTheme.disabledColor
?? theme.colorScheme.onSurface.withOpacity(0.38); ?? theme.colorScheme.onSurface.withOpacity(0.38);
currentFillColor = theme.colorScheme.surface.withOpacity(0.0);
} }
final TextStyle currentTextStyle = textStyle ?? toggleButtonsTheme.textStyle ?? theme.textTheme.bodyText2!; final TextStyle currentTextStyle = textStyle ?? toggleButtonsTheme.textStyle ?? theme.textTheme.bodyText2!;
...@@ -951,6 +974,36 @@ class _ToggleButton extends StatelessWidget { ...@@ -951,6 +974,36 @@ class _ToggleButton extends StatelessWidget {
} }
} }
@immutable
class _ResolveFillColor extends MaterialStateProperty<Color?> with Diagnosticable {
_ResolveFillColor(this.primary);
final Color? primary;
@override
Color? resolve(Set<MaterialState> states) {
if (primary is MaterialStateProperty<Color>) {
return MaterialStateProperty.resolveAs<Color?>(primary, states);
}
return states.contains(MaterialState.selected) ? primary : null;
}
}
@immutable
class _DefaultFillColor extends MaterialStateProperty<Color> with Diagnosticable {
_DefaultFillColor(this.colorScheme);
final ColorScheme colorScheme;
@override
Color resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return colorScheme.primary.withOpacity(0.12);
}
return colorScheme.surface.withOpacity(0.0);
}
}
class _SelectToggleButton extends SingleChildRenderObjectWidget { class _SelectToggleButton extends SingleChildRenderObjectWidget {
const _SelectToggleButton({ const _SelectToggleButton({
Key? key, Key? key,
......
...@@ -665,6 +665,126 @@ void main() { ...@@ -665,6 +665,126 @@ void main() {
expect(material.type, MaterialType.button); expect(material.type, MaterialType.button);
}); });
testWidgets('Custom button fillColor - Non MaterialState', (WidgetTester tester) async {
Material buttonColor(String text) {
return tester.widget<Material>(
find.descendant(
of: find.byType(RawMaterialButton),
matching: find.widgetWithText(Material, text),
),
);
}
final ThemeData theme = ThemeData();
const Color selectedFillColor = Colors.yellow;
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
fillColor: selectedFillColor,
isSelected: const <bool>[false, true],
onPressed: (int index) {},
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
);
await tester.pumpAndSettle();
expect(buttonColor('First child').color, theme.colorScheme.surface.withOpacity(0.0));
expect(buttonColor('Second child').color, selectedFillColor);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
fillColor: selectedFillColor,
isSelected: const <bool>[false, true],
onPressed: null,
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
);
await tester.pumpAndSettle();
expect(buttonColor('First child').color, theme.colorScheme.surface.withOpacity(0.0));
expect(buttonColor('Second child').color, theme.colorScheme.surface.withOpacity(0.0));
});
testWidgets('Custom button fillColor - MaterialState', (WidgetTester tester) async {
Material buttonColor(String text) {
return tester.widget<Material>(
find.descendant(
of: find.byType(RawMaterialButton),
matching: find.widgetWithText(Material, text),
),
);
}
const Color selectedFillColor = Colors.orange;
const Color defaultFillColor = Colors.blue;
Color getFillColor(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return selectedFillColor;
}
return defaultFillColor;
}
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
fillColor: MaterialStateColor.resolveWith(getFillColor),
isSelected: const <bool>[false, true],
onPressed: (int index) {},
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
);
await tester.pumpAndSettle();
expect(buttonColor('First child').color, defaultFillColor);
expect(buttonColor('Second child').color, selectedFillColor);
// disabled
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
fillColor: MaterialStateColor.resolveWith(getFillColor),
isSelected: const <bool>[false, true],
onPressed: null,
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
);
await tester.pumpAndSettle();
expect(buttonColor('First child').color, defaultFillColor);
expect(buttonColor('Second child').color, defaultFillColor);
});
testWidgets('Default InkWell colors - unselected', (WidgetTester tester) async { testWidgets('Default InkWell colors - unselected', (WidgetTester tester) async {
final ThemeData theme = ThemeData(); final ThemeData theme = ThemeData();
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
......
...@@ -363,6 +363,74 @@ void main() { ...@@ -363,6 +363,74 @@ void main() {
expect(material.type, MaterialType.button); expect(material.type, MaterialType.button);
}); });
testWidgets('Custom Theme button fillColor in different states', (WidgetTester tester) async {
Material buttonColor(String text) {
return tester.widget<Material>(
find.descendant(
of: find.byType(RawMaterialButton),
matching: find.widgetWithText(Material, text),
),
);
}
const Color enabledFillColor = Colors.green;
const Color selectedFillColor = Colors.blue;
const Color disabledFillColor = Colors.yellow;
Color getColor(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return selectedFillColor;
} else if (states.contains(MaterialState.disabled)) {
return disabledFillColor;
}
return enabledFillColor;
}
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: ToggleButtonsThemeData(fillColor: MaterialStateColor.resolveWith(getColor)),
child: ToggleButtons(
isSelected: const <bool>[true, false],
onPressed: (int index) {},
children: const <Widget> [
Text('First child'),
Text('Second child'),
],
),
),
),
),
);
await tester.pumpAndSettle();
expect(buttonColor('First child').color, selectedFillColor);
expect(buttonColor('Second child').color, enabledFillColor);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: ToggleButtonsThemeData(fillColor: MaterialStateColor.resolveWith(getColor)),
child: ToggleButtons(
isSelected: const <bool>[true, false],
onPressed: null,
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
),
);
expect(buttonColor('First child').color, disabledFillColor);
expect(buttonColor('Second child').color, disabledFillColor);
});
testWidgets('Theme InkWell colors', (WidgetTester tester) async { testWidgets('Theme InkWell colors', (WidgetTester tester) async {
const Color splashColor = Color(0xff4caf50); const Color splashColor = Color(0xff4caf50);
const Color highlightColor = Color(0xffcddc39); const Color highlightColor = Color(0xffcddc39);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment