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';
import 'package:flutter/widgets.dart';
import 'button.dart';
import 'color_scheme.dart';
import 'constants.dart';
import 'debug.dart';
import 'material_state.dart';
import 'theme.dart';
import 'theme_data.dart';
import 'toggle_buttons_theme.dart';
......@@ -284,6 +286,13 @@ class ToggleButtons extends StatelessWidget {
/// ToggleButtonTheme.of(context).fillColor is used. If
/// [ToggleButtonsThemeData.fillColor] is also null, then
/// 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;
/// The color to use for filling the button when the button has input focus.
......@@ -654,7 +663,7 @@ class ToggleButtons extends StatelessWidget {
color: color,
selectedColor: selectedColor,
disabledColor: disabledColor,
fillColor: fillColor ?? toggleButtonsTheme.fillColor,
fillColor: fillColor,
focusColor: focusColor ?? toggleButtonsTheme.focusColor,
highlightColor: highlightColor ?? toggleButtonsTheme.highlightColor,
hoverColor: hoverColor ?? toggleButtonsTheme.hoverColor,
......@@ -852,23 +861,39 @@ class _ToggleButton extends StatelessWidget {
/// The button's label, which is usually an [Icon] or a [Text] widget.
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
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
final Color currentColor;
final Color currentFillColor;
Color? currentFocusColor;
Color? currentHoverColor;
Color? currentSplashColor;
final ThemeData theme = Theme.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) {
currentColor = selectedColor
?? toggleButtonsTheme.selectedColor
?? theme.colorScheme.primary;
currentFillColor = fillColor
?? theme.colorScheme.primary.withOpacity(0.12);
currentFocusColor = focusColor
?? toggleButtonsTheme.focusColor
?? theme.colorScheme.primary.withOpacity(0.12);
......@@ -882,7 +907,6 @@ class _ToggleButton extends StatelessWidget {
currentColor = color
?? toggleButtonsTheme.color
?? theme.colorScheme.onSurface.withOpacity(0.87);
currentFillColor = theme.colorScheme.surface.withOpacity(0.0);
currentFocusColor = focusColor
?? toggleButtonsTheme.focusColor
?? theme.colorScheme.onSurface.withOpacity(0.12);
......@@ -896,7 +920,6 @@ class _ToggleButton extends StatelessWidget {
currentColor = disabledColor
?? toggleButtonsTheme.disabledColor
?? theme.colorScheme.onSurface.withOpacity(0.38);
currentFillColor = theme.colorScheme.surface.withOpacity(0.0);
}
final TextStyle currentTextStyle = textStyle ?? toggleButtonsTheme.textStyle ?? theme.textTheme.bodyText2!;
......@@ -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 {
const _SelectToggleButton({
Key? key,
......
......@@ -665,6 +665,126 @@ void main() {
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 {
final ThemeData theme = ThemeData();
final FocusNode focusNode = FocusNode();
......
......@@ -363,6 +363,74 @@ void main() {
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 {
const Color splashColor = Color(0xff4caf50);
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