Unverified Commit c14ca6d3 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Migrate common buttons to Material 3 (#100794)

parent 1755819c
......@@ -17,6 +17,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:gen_defaults/button_template.dart';
import 'package:gen_defaults/card_template.dart';
import 'package:gen_defaults/dialog_template.dart';
import 'package:gen_defaults/fab_template.dart';
......@@ -77,6 +78,9 @@ Future<void> main(List<String> args) async {
tokens['colorsLight'] = _readTokenFile('color_light.json');
tokens['colorsDark'] = _readTokenFile('color_dark.json');
ButtonTemplate('md.comp.elevated-button', '$materialLib/elevated_button.dart', tokens).updateFile();
ButtonTemplate('md.comp.outlined-button', '$materialLib/outlined_button.dart', tokens).updateFile();
ButtonTemplate('md.comp.text-button', '$materialLib/text_button.dart', tokens).updateFile();
CardTemplate('$materialLib/card.dart', tokens).updateFile();
DialogTemplate('$materialLib/dialog.dart', tokens).updateFile();
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'template.dart';
class ButtonTemplate extends TokenTemplate {
const ButtonTemplate(this.tokenGroup, String fileName, Map<String, dynamic> tokens)
: super(fileName, tokens,
colorSchemePrefix: '_colors.',
);
final String tokenGroup;
String _backgroundColor() {
if (tokens.containsKey('$tokenGroup.container.color')) {
return '''
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return ${componentColor('$tokenGroup.disabled.container')};
return ${componentColor('$tokenGroup.container')};
})''';
}
return '''
ButtonStyleButton.allOrNull<Color>(Colors.transparent)''';
}
String _elevation() {
if (tokens.containsKey('$tokenGroup.container.elevation')) {
return '''
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return ${elevation("$tokenGroup.disabled.container")};
if (states.contains(MaterialState.hovered))
return ${elevation("$tokenGroup.hover.container")};
if (states.contains(MaterialState.focused))
return ${elevation("$tokenGroup.focus.container")};
if (states.contains(MaterialState.pressed))
return ${elevation("$tokenGroup.pressed.container")};
return ${elevation("$tokenGroup.container")};
})''';
}
return '''
ButtonStyleButton.allOrNull<double>(0.0)''';
}
@override
String generate() => '''
// Generated version ${tokens["version"]}
class _TokenDefaultsM3 extends ButtonStyle {
_TokenDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<TextStyle?> get textStyle =>
MaterialStateProperty.all<TextStyle?>(${textStyle("$tokenGroup.label-text")});
@override
MaterialStateProperty<Color?>? get backgroundColor =>${_backgroundColor()};
@override
MaterialStateProperty<Color?>? get foregroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return ${componentColor('$tokenGroup.disabled.label-text')};
return ${componentColor('$tokenGroup.label-text')};
});
@override
MaterialStateProperty<Color?>? get overlayColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered))
return ${componentColor('$tokenGroup.hover.state-layer')};
if (states.contains(MaterialState.focused))
return ${componentColor('$tokenGroup.focus.state-layer')};
if (states.contains(MaterialState.pressed))
return ${componentColor('$tokenGroup.pressed.state-layer')};
return null;
});
${tokens.containsKey("$tokenGroup.container.shadow-color") ? '''
@override
MaterialStateProperty<Color>? get shadowColor =>
ButtonStyleButton.allOrNull<Color>(${color("$tokenGroup.container.shadow-color")});''' : '''
// No default shadow color'''}
${tokens.containsKey("$tokenGroup.container.surface-tint-layer.color") ? '''
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
ButtonStyleButton.allOrNull<Color>(${color("$tokenGroup.container.surface-tint-layer.color")});''' : '''
// No default surface tint color'''}
@override
MaterialStateProperty<double>? get elevation =>${_elevation()};
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, ${tokens["$tokenGroup.container.height"]}));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
${tokens.containsKey("$tokenGroup.outline.color") ? '''
@override
MaterialStateProperty<BorderSide>? get side =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return ${border("$tokenGroup.disabled.outline")};
return ${border("$tokenGroup.outline")};
});''' : '''
// No default side'''}
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(${shape("$tokenGroup.container")});
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return SystemMouseCursors.basic;
return SystemMouseCursors.click;
});
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}
''';
}
......@@ -70,8 +70,8 @@ abstract class TokenTemplate {
/// * [componentColor], that provides support for an optional opacity.
String color(String colorToken) {
return tokens.containsKey(colorToken)
? '$colorSchemePrefix${tokens[colorToken]}'
: 'null';
? '$colorSchemePrefix${tokens[colorToken]}'
: 'null';
}
/// Generate a [ColorScheme] color name for the given component's color
......@@ -91,17 +91,25 @@ abstract class TokenTemplate {
if (!tokens.containsKey(colorToken))
return 'null';
String value = color(colorToken);
final String tokenOpacity = '$componentToken.opacity';
if (tokens.containsKey(tokenOpacity)) {
final dynamic opacityValue = tokens[tokenOpacity];
final String opacity = opacityValue is double
? opacityValue.toString()
: tokens[tokens[tokenOpacity]!]!.toString();
value += '.withOpacity($opacity)';
final String opacityToken = '$componentToken.opacity';
if (tokens.containsKey(opacityToken)) {
value += '.withOpacity(${opacity(opacityToken)})';
}
return value;
}
/// Generate the opacity value for the given token.
String? opacity(String token) {
final dynamic value = tokens[token];
if (value == null) {
return null;
}
if (value is double) {
return value.toString();
}
return tokens[value].toString();
}
/// Generate an elevation value for the given component token.
String elevation(String componentToken) {
return tokens[tokens['$componentToken.elevation']!]!.toString();
......@@ -135,7 +143,7 @@ abstract class TokenTemplate {
return 'null';
}
final String borderColor = componentColor(componentToken);
final double width = tokens['$componentToken.width'] as double;
final double width = (tokens['$componentToken.width'] ?? 1.0) as double;
return 'BorderSide(color: $borderColor${width != 1.0 ? ", width: $width" : ""})';
}
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for ElevatedButton
import 'package:flutter/material.dart';
void main() {
runApp(const ButtonApp());
}
class ButtonApp extends StatelessWidget {
const ButtonApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
title: 'Button Types',
home: const Scaffold(
body: ButtonTypesExample(),
),
);
}
}
class ButtonTypesExample extends StatelessWidget {
const ButtonTypesExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
children: const <Widget>[
Spacer(),
ButtonTypesGroup(enabled: true),
ButtonTypesGroup(enabled: false),
Spacer(),
],
),
);
}
}
class ButtonTypesGroup extends StatelessWidget {
const ButtonTypesGroup({ Key? key, required this.enabled }) : super(key: key);
final bool enabled;
@override
Widget build(BuildContext context) {
final VoidCallback? onPressed = enabled ? () {} : null;
return Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(onPressed: onPressed, child: const Text('Elevated')),
// Use an ElevatedButton with specific style to implement the
// 'Filled' type.
ElevatedButton(
style: ElevatedButton.styleFrom(
// Foreground color
onPrimary: Theme.of(context).colorScheme.onPrimary,
// Background color
primary: Theme.of(context).colorScheme.primary,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: onPressed,
child: const Text('Filled'),
),
// Use an ElevatedButton with specific style to implement the
// 'Filled Tonal' type.
ElevatedButton(
style: ElevatedButton.styleFrom(
// Foreground color
onPrimary: Theme.of(context).colorScheme.onSecondaryContainer,
// Background color
primary: Theme.of(context).colorScheme.secondaryContainer,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: onPressed,
child: const Text('Filled Tonal'),
),
OutlinedButton(onPressed: onPressed, child: const Text('Outlined')),
TextButton(onPressed: onPressed, child: const Text('Text')),
],
),
);
}
}
......@@ -91,6 +91,27 @@ import 'theme_data.dart';
/// home: MyAppHome(),
/// )
/// ```
///
/// ## Material 3 button types
///
/// Material Design 3 specifies five types of common buttons. Flutter provides
/// support for these using the following button classes:
/// <style>table,td,th { border-collapse: collapse; padding: 0.45em; } td { border: 1px solid }</style>
///
/// | Type | Flutter implementation |
/// | :----------- | :---------------------- |
/// | Elevated | [ElevatedButton] |
/// | Filled | Styled [ElevatedButton] |
/// | Filled Tonal | Styled [ElevatedButton] |
/// | Outlined | [OutlinedButton] |
/// | Text | [TextButton] |
///
/// {@tool dartpad}
/// This sample shows how to create each of the Material 3 button types with Flutter.
///
/// ** See code in examples/api/lib/material/button_style/button_style.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [TextButtonTheme], the theme for [TextButton]s.
......@@ -105,6 +126,7 @@ class ButtonStyle with Diagnosticable {
this.foregroundColor,
this.overlayColor,
this.shadowColor,
this.surfaceTintColor,
this.elevation,
this.padding,
this.minimumSize,
......@@ -150,6 +172,11 @@ class ButtonStyle with Diagnosticable {
/// [ThemeData.applyElevationOverlayColor].
final MaterialStateProperty<Color?>? shadowColor;
/// The surface tint color of the button's [Material].
///
/// See [Material.surfaceTintColor] for more details.
final MaterialStateProperty<Color?>? surfaceTintColor;
/// The elevation of the button's [Material].
final MaterialStateProperty<double?>? elevation;
......@@ -267,6 +294,7 @@ class ButtonStyle with Diagnosticable {
MaterialStateProperty<Color?>? foregroundColor,
MaterialStateProperty<Color?>? overlayColor,
MaterialStateProperty<Color?>? shadowColor,
MaterialStateProperty<Color?>? surfaceTintColor,
MaterialStateProperty<double?>? elevation,
MaterialStateProperty<EdgeInsetsGeometry?>? padding,
MaterialStateProperty<Size?>? minimumSize,
......@@ -288,6 +316,7 @@ class ButtonStyle with Diagnosticable {
foregroundColor: foregroundColor ?? this.foregroundColor,
overlayColor: overlayColor ?? this.overlayColor,
shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
elevation: elevation ?? this.elevation,
padding: padding ?? this.padding,
minimumSize: minimumSize ?? this.minimumSize,
......@@ -319,6 +348,7 @@ class ButtonStyle with Diagnosticable {
foregroundColor: foregroundColor ?? style.foregroundColor,
overlayColor: overlayColor ?? style.overlayColor,
shadowColor: shadowColor ?? style.shadowColor,
surfaceTintColor: surfaceTintColor ?? style.surfaceTintColor,
elevation: elevation ?? style.elevation,
padding: padding ?? style.padding,
minimumSize: minimumSize ?? style.minimumSize,
......@@ -343,6 +373,7 @@ class ButtonStyle with Diagnosticable {
foregroundColor,
overlayColor,
shadowColor,
surfaceTintColor,
elevation,
padding,
minimumSize,
......@@ -371,6 +402,7 @@ class ButtonStyle with Diagnosticable {
&& other.foregroundColor == foregroundColor
&& other.overlayColor == overlayColor
&& other.shadowColor == shadowColor
&& other.surfaceTintColor == surfaceTintColor
&& other.elevation == elevation
&& other.padding == padding
&& other.minimumSize == minimumSize
......@@ -395,6 +427,7 @@ class ButtonStyle with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('foregroundColor', foregroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('overlayColor', overlayColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('shadowColor', shadowColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<EdgeInsetsGeometry?>>('padding', padding, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('minimumSize', minimumSize, defaultValue: null));
......@@ -421,6 +454,7 @@ class ButtonStyle with Diagnosticable {
foregroundColor: _lerpProperties<Color?>(a?.foregroundColor, b?.foregroundColor, t, Color.lerp),
overlayColor: _lerpProperties<Color?>(a?.overlayColor, b?.overlayColor, t, Color.lerp),
shadowColor: _lerpProperties<Color?>(a?.shadowColor, b?.shadowColor, t, Color.lerp),
surfaceTintColor: _lerpProperties<Color?>(a?.surfaceTintColor, b?.surfaceTintColor, t, Color.lerp),
elevation: _lerpProperties<double?>(a?.elevation, b?.elevation, t, lerpDouble),
padding: _lerpProperties<EdgeInsetsGeometry?>(a?.padding, b?.padding, t, EdgeInsetsGeometry.lerp),
minimumSize: _lerpProperties<Size?>(a?.minimumSize, b?.minimumSize, t, Size.lerp),
......
......@@ -247,6 +247,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
Color? resolvedBackgroundColor = resolve<Color?>((ButtonStyle? style) => style?.backgroundColor);
final Color? resolvedForegroundColor = resolve<Color?>((ButtonStyle? style) => style?.foregroundColor);
final Color? resolvedShadowColor = resolve<Color?>((ButtonStyle? style) => style?.shadowColor);
final Color? resolvedSurfaceTintColor = resolve<Color?>((ButtonStyle? style) => style?.surfaceTintColor);
final EdgeInsetsGeometry? resolvedPadding = resolve<EdgeInsetsGeometry?>((ButtonStyle? style) => style?.padding);
final Size? resolvedMinimumSize = resolve<Size?>((ButtonStyle? style) => style?.minimumSize);
final Size? resolvedFixedSize = resolve<Size?>((ButtonStyle? style) => style?.fixedSize);
......@@ -343,6 +344,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
shape: resolvedShape!.copyWith(side: resolvedSide),
color: resolvedBackgroundColor,
shadowColor: resolvedShadowColor,
surfaceTintColor: resolvedSurfaceTintColor,
type: resolvedBackgroundColor == null ? MaterialType.transparency : MaterialType.button,
animationDuration: resolvedAnimationDuration,
clipBehavior: widget.clipBehavior,
......
......@@ -146,6 +146,7 @@ class OutlinedButton extends ButtonStyleButton {
Color? onSurface,
Color? backgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
......@@ -179,6 +180,7 @@ class OutlinedButton extends ButtonStyleButton {
backgroundColor: ButtonStyleButton.allOrNull<Color>(backgroundColor),
overlayColor: overlayColor,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
elevation: ButtonStyleButton.allOrNull<double>(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
......@@ -219,6 +221,8 @@ class OutlinedButton extends ButtonStyleButton {
/// The color of the [ButtonStyle.textStyle] is not used, the
/// [ButtonStyle.foregroundColor] is used instead.
///
/// ## Material 2 defaults
///
/// * `textStyle` - Theme.textTheme.button
/// * `backgroundColor` - transparent
/// * `foregroundColor`
......@@ -248,41 +252,75 @@ class OutlinedButton extends ButtonStyleButton {
/// * `enableFeedback` - true
/// * `alignment` - Alignment.center
/// * `splashFactory` - InkRipple.splashFactory
///
/// ## Material 3 defaults
///
/// If [ThemeData.useMaterial3] is set to true the following defaults will
/// be used:
///
/// * `textStyle` - Theme.textTheme.labelLarge
/// * `backgroundColor` - transparent
/// * `foregroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.38)
/// * others - Theme.colorScheme.primary
/// * `overlayColor`
/// * hovered - Theme.colorScheme.primary(0.08)
/// * focused or pressed - Theme.colorScheme.primary(0.12)
/// * others - null
/// * `shadowColor` - null
/// * `surfaceTintColor` - null
/// * `elevation` - 0
/// * `padding`
/// * `textScaleFactor <= 1` - horizontal(16)
/// * `1 < textScaleFactor <= 2` - lerp(horizontal(16), horizontal(8))
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
/// * `minimumSize` - Size(64, 40)
/// * `fixedSize` - null
/// * `maximumSize` - Size.infinite
/// * `side`
/// * disabled - BorderSide(color: Theme.colorScheme.onSurface(0.12))
/// * others - BorderSide(color: Theme.colorScheme.outline)
/// * `shape` - StadiumBorder()
/// * `mouseCursor`
/// * disabled - SystemMouseCursors.basic
/// * others - SystemMouseCursors.click
/// * `visualDensity` - theme.visualDensity
/// * `tapTargetSize` - theme.materialTapTargetSize
/// * `animationDuration` - kThemeChangeDuration
/// * `enableFeedback` - true
/// * `alignment` - Alignment.center
/// * `splashFactory` - Theme.splashFactory
@override
ButtonStyle defaultStyleOf(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
const EdgeInsets.symmetric(horizontal: 16),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
);
return styleFrom(
primary: colorScheme.primary,
onSurface: colorScheme.onSurface,
backgroundColor: Colors.transparent,
shadowColor: theme.shadowColor,
elevation: 0,
textStyle: theme.textTheme.button,
padding: scaledPadding,
minimumSize: const Size(64, 36),
maximumSize: Size.infinite,
side: BorderSide(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
enabledMouseCursor: SystemMouseCursors.click,
disabledMouseCursor: SystemMouseCursors.basic,
visualDensity: theme.visualDensity,
tapTargetSize: theme.materialTapTargetSize,
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
splashFactory: theme.useMaterial3 ? theme.splashFactory : InkRipple.splashFactory,
);
return Theme.of(context).useMaterial3
? _TokenDefaultsM3(context)
: styleFrom(
primary: colorScheme.primary,
onSurface: colorScheme.onSurface,
backgroundColor: Colors.transparent,
shadowColor: theme.shadowColor,
elevation: 0,
textStyle: theme.textTheme.button,
padding: _scaledPadding(context),
minimumSize: const Size(64, 36),
maximumSize: Size.infinite,
side: BorderSide(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
enabledMouseCursor: SystemMouseCursors.click,
disabledMouseCursor: SystemMouseCursors.basic,
visualDensity: theme.visualDensity,
tapTargetSize: theme.materialTapTargetSize,
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
splashFactory: InkRipple.splashFactory,
);
}
@override
......@@ -291,6 +329,15 @@ class OutlinedButton extends ButtonStyleButton {
}
}
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
return ButtonStyleButton.scaledPadding(
const EdgeInsets.symmetric(horizontal: 16),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
);
}
@immutable
class _OutlinedButtonDefaultForeground extends MaterialStateProperty<Color?> with Diagnosticable {
_OutlinedButtonDefaultForeground(this.primary, this.onSurface);
......@@ -382,3 +429,103 @@ class _OutlinedButtonWithIconChild extends StatelessWidget {
);
}
}
// BEGIN GENERATED TOKEN PROPERTIES
// Generated code to the end of this file. Do not edit by hand.
// These defaults are generated from the Material Design Token
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
// Generated version v0_92
class _TokenDefaultsM3 extends ButtonStyle {
_TokenDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<TextStyle?> get textStyle =>
MaterialStateProperty.all<TextStyle?>(Theme.of(context).textTheme.labelLarge);
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return _colors.onSurface.withOpacity(0.38);
return _colors.primary;
});
@override
MaterialStateProperty<Color?>? get overlayColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered))
return _colors.primary.withOpacity(0.08);
if (states.contains(MaterialState.focused))
return _colors.primary.withOpacity(0.12);
if (states.contains(MaterialState.pressed))
return _colors.primary.withOpacity(0.12);
return null;
});
// No default shadow color
// No default surface tint color
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
@override
MaterialStateProperty<BorderSide>? get side =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return BorderSide(color: _colors.onSurface.withOpacity(0.12));
return BorderSide(color: _colors.outline);
});
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return SystemMouseCursors.basic;
return SystemMouseCursors.click;
});
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}
// END GENERATED TOKEN PROPERTIES
......@@ -147,6 +147,7 @@ class TextButton extends ButtonStyleButton {
Color? onSurface,
Color? backgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
......@@ -180,6 +181,7 @@ class TextButton extends ButtonStyleButton {
foregroundColor: foregroundColor,
overlayColor: overlayColor,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
elevation: ButtonStyleButton.allOrNull<double>(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
......@@ -223,6 +225,8 @@ class TextButton extends ButtonStyleButton {
/// The color of the [ButtonStyle.textStyle] is not used, the
/// [ButtonStyle.foregroundColor] color is used instead.
///
/// ## Material 2 defaults
///
/// * `textStyle` - Theme.textTheme.button
/// * `backgroundColor` - transparent
/// * `foregroundColor`
......@@ -264,38 +268,70 @@ class TextButton extends ButtonStyleButton {
/// outline, is null. That means that the outline is defined by the button
/// shape's [OutlinedBorder.side]. Typically the default value of an
/// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
///
/// ## Material 3 defaults
///
/// If [ThemeData.useMaterial3] is set to true the following defaults will
/// be used:
///
/// * `textStyle` - Theme.textTheme.labelLarge
/// * `backgroundColor` - transparent
/// * `foregroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.38)
/// * others - Theme.colorScheme.primary
/// * `overlayColor`
/// * hovered - Theme.colorScheme.primary(0.08)
/// * focused or pressed - Theme.colorScheme.primary(0.12)
/// * others - null
/// * `shadowColor` - null
/// * `surfaceTintColor` - null
/// * `elevation` - 0
/// * `padding`
/// * `textScaleFactor <= 1` - all(8)
/// * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8))
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
/// * `minimumSize` - Size(64, 40)
/// * `fixedSize` - null
/// * `maximumSize` - Size.infinite
/// * `side` - null
/// * `shape` - StadiumBorder()
/// * `mouseCursor`
/// * disabled - SystemMouseCursors.basic
/// * others - SystemMouseCursors.click
/// * `visualDensity` - theme.visualDensity
/// * `tapTargetSize` - theme.materialTapTargetSize
/// * `animationDuration` - kThemeChangeDuration
/// * `enableFeedback` - true
/// * `alignment` - Alignment.center
/// * `splashFactory` - Theme.splashFactory
@override
ButtonStyle defaultStyleOf(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
const EdgeInsets.all(8),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
);
return styleFrom(
primary: colorScheme.primary,
onSurface: colorScheme.onSurface,
backgroundColor: Colors.transparent,
shadowColor: theme.shadowColor,
elevation: 0,
textStyle: theme.textTheme.button,
padding: scaledPadding,
minimumSize: const Size(64, 36),
maximumSize: Size.infinite,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
enabledMouseCursor: SystemMouseCursors.click,
disabledMouseCursor: SystemMouseCursors.basic,
visualDensity: theme.visualDensity,
tapTargetSize: theme.materialTapTargetSize,
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
splashFactory: theme.useMaterial3 ? theme.splashFactory : InkRipple.splashFactory,
);
return Theme.of(context).useMaterial3
? _TokenDefaultsM3(context)
: styleFrom(
primary: colorScheme.primary,
onSurface: colorScheme.onSurface,
backgroundColor: Colors.transparent,
shadowColor: theme.shadowColor,
elevation: 0,
textStyle: theme.textTheme.button,
padding: _scaledPadding(context),
minimumSize: const Size(64, 36),
maximumSize: Size.infinite,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
enabledMouseCursor: SystemMouseCursors.click,
disabledMouseCursor: SystemMouseCursors.basic,
visualDensity: theme.visualDensity,
tapTargetSize: theme.materialTapTargetSize,
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
splashFactory: InkRipple.splashFactory,
);
}
/// Returns the [TextButtonThemeData.style] of the closest
......@@ -306,6 +342,15 @@ class TextButton extends ButtonStyleButton {
}
}
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
return ButtonStyleButton.scaledPadding(
const EdgeInsets.all(8),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
);
}
@immutable
class _TextButtonDefaultForeground extends MaterialStateProperty<Color?> {
_TextButtonDefaultForeground(this.primary, this.onSurface);
......@@ -424,3 +469,97 @@ class _TextButtonWithIconChild extends StatelessWidget {
);
}
}
// BEGIN GENERATED TOKEN PROPERTIES
// Generated code to the end of this file. Do not edit by hand.
// These defaults are generated from the Material Design Token
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
// Generated version v0_92
class _TokenDefaultsM3 extends ButtonStyle {
_TokenDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<TextStyle?> get textStyle =>
MaterialStateProperty.all<TextStyle?>(Theme.of(context).textTheme.labelLarge);
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return _colors.onSurface.withOpacity(0.38);
return _colors.primary;
});
@override
MaterialStateProperty<Color?>? get overlayColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered))
return _colors.primary.withOpacity(0.08);
if (states.contains(MaterialState.focused))
return _colors.primary.withOpacity(0.12);
if (states.contains(MaterialState.pressed))
return _colors.primary.withOpacity(0.12);
return null;
});
// No default shadow color
// No default surface tint color
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return SystemMouseCursors.basic;
return SystemMouseCursors.click;
});
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}
// END GENERATED TOKEN PROPERTIES
......@@ -1170,21 +1170,25 @@ class ThemeData with Diagnosticable {
/// {@endtemplate}
final VisualDensity visualDensity;
/// A temporary flag used to opt-in to new Material 3 features.
/// A temporary flag used to opt-in to Material 3 features.
///
/// If true, then components that have been migrated to Material 3 will
/// start using new colors, typography and other features of Material 3.
/// use new colors, typography and other features of Material 3.
/// If false, they will use the Material 2 look and feel.
///
/// If a [ThemeData] is constructed with [useMaterial3] set to true, then
/// some properties will get special defaults. However, just copying a [ThemeData]
/// with [useMaterial3] set to true will not change any of these properties in the
/// resulting [ThemeData]. These properties are:
/// | Property | [useMaterial3] default | fallback default |
/// |:---|:---|:---|
/// | [typography] | [Typography.material2021] | [Typography.material2014] |
/// | [splashFactory] | [InkSparkle]* | [InkSplash] |
/// *if and only if the target platform is Android and the app is not running on the web.
/// <style>table,td,th { border-collapse: collapse; padding: 0.45em; } td { border: 1px solid }</style>
///
/// | Property | Material 3 default | Fallback default |
/// | :-------------- | :--------------------------- | :------------------------ |
/// | [typography] | [Typography.material2021] | [Typography.material2014] |
/// | [splashFactory] | [InkSparkle]* or [InkRipple] | [InkSplash] |
///
/// \* if and only if the target platform is Android and the app is not
/// running on the web, otherwise it will fallback to [InkRipple].
///
/// During the migration to Material 3, turning this on may yield
/// inconsistent look and feel in your app. Some components will be migrated
......@@ -1201,12 +1205,15 @@ class ThemeData with Diagnosticable {
/// * [AlertDialog]
/// * [Card]
/// * [Dialog]
/// * [ElevatedButton]
/// * [FloatingActionButton]
/// * [Material]
/// * [NavigationBar]
/// * [NavigationRail]
/// * [OutlinedButton]
/// * [StretchingOverscrollIndicator], replacing the
/// [GlowingOverscrollIndicator]
/// * [TextButton]
///
/// See also:
///
......
......@@ -20,6 +20,8 @@ void main() {
expect(style.backgroundColor, null);
expect(style.foregroundColor, null);
expect(style.overlayColor, null);
expect(style.shadowColor, null);
expect(style.surfaceTintColor, null);
expect(style.elevation, null);
expect(style.padding, null);
expect(style.minimumSize, null);
......@@ -53,10 +55,12 @@ void main() {
backgroundColor: MaterialStateProperty.all<Color>(const Color(0xfffffff1)),
foregroundColor: MaterialStateProperty.all<Color>(const Color(0xfffffff2)),
overlayColor: MaterialStateProperty.all<Color>(const Color(0xfffffff3)),
shadowColor: MaterialStateProperty.all<Color>(const Color(0xfffffff4)),
surfaceTintColor: MaterialStateProperty.all<Color>(const Color(0xfffffff5)),
elevation: MaterialStateProperty.all<double>(1.5),
padding: MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(1.0)),
minimumSize: MaterialStateProperty.all<Size>(const Size(1.0, 2.0)),
side: MaterialStateProperty.all<BorderSide>(const BorderSide(width: 4.0, color: Color(0xfffffff4))),
side: MaterialStateProperty.all<BorderSide>(const BorderSide(width: 4.0, color: Color(0xfffffff6))),
maximumSize: MaterialStateProperty.all<Size>(const Size(100.0, 200.0)),
shape: MaterialStateProperty.all<OutlinedBorder>(const StadiumBorder()),
mouseCursor: MaterialStateProperty.all<MouseCursor>(SystemMouseCursors.forbidden),
......@@ -75,11 +79,13 @@ void main() {
'backgroundColor: MaterialStateProperty.all(Color(0xfffffff1))',
'foregroundColor: MaterialStateProperty.all(Color(0xfffffff2))',
'overlayColor: MaterialStateProperty.all(Color(0xfffffff3))',
'shadowColor: MaterialStateProperty.all(Color(0xfffffff4))',
'surfaceTintColor: MaterialStateProperty.all(Color(0xfffffff5))',
'elevation: MaterialStateProperty.all(1.5)',
'padding: MaterialStateProperty.all(EdgeInsets.all(1.0))',
'minimumSize: MaterialStateProperty.all(Size(1.0, 2.0))',
'maximumSize: MaterialStateProperty.all(Size(100.0, 200.0))',
'side: MaterialStateProperty.all(BorderSide(Color(0xfffffff4), 4.0, BorderStyle.solid))',
'side: MaterialStateProperty.all(BorderSide(Color(0xfffffff6), 4.0, BorderStyle.solid))',
'shape: MaterialStateProperty.all(StadiumBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none)))',
'mouseCursor: MaterialStateProperty.all(SystemMouseCursor(forbidden))',
'tapTargetSize: shrinkWrap',
......@@ -93,6 +99,8 @@ void main() {
final MaterialStateProperty<Color> backgroundColor = MaterialStateProperty.all<Color>(const Color(0xfffffff1));
final MaterialStateProperty<Color> foregroundColor = MaterialStateProperty.all<Color>(const Color(0xfffffff2));
final MaterialStateProperty<Color> overlayColor = MaterialStateProperty.all<Color>(const Color(0xfffffff3));
final MaterialStateProperty<Color> shadowColor = MaterialStateProperty.all<Color>(const Color(0xfffffff4));
final MaterialStateProperty<Color> surfaceTintColor = MaterialStateProperty.all<Color>(const Color(0xfffffff5));
final MaterialStateProperty<double> elevation = MaterialStateProperty.all<double>(1);
final MaterialStateProperty<EdgeInsets> padding = MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(1));
final MaterialStateProperty<Size> minimumSize = MaterialStateProperty.all<Size>(const Size(1, 2));
......@@ -111,6 +119,8 @@ void main() {
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
overlayColor: overlayColor,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
......@@ -132,6 +142,8 @@ void main() {
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
overlayColor: overlayColor,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
......
......@@ -14,11 +14,13 @@ import '../widgets/semantics_tester.dart';
void main() {
testWidgets('ElevatedButton, ElevatedButton.icon defaults', (WidgetTester tester) async {
const ColorScheme colorScheme = ColorScheme.light();
final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
final bool material3 = theme.useMaterial3;
// Enabled ElevatedButton
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: colorScheme),
theme: theme,
home: Center(
child: ElevatedButton(
onPressed: () { },
......@@ -39,11 +41,13 @@ void main() {
expect(material.borderOnForeground, true);
expect(material.borderRadius, null);
expect(material.clipBehavior, Clip.none);
expect(material.color, colorScheme.primary);
expect(material.elevation, 2);
expect(material.color, material3 ? colorScheme.onPrimary : colorScheme.primary);
expect(material.elevation, material3 ? 1: 2);
expect(material.shadowColor, const Color(0xff000000));
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.onPrimary);
expect(material.shape, material3
? const StadiumBorder()
: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, material3 ? colorScheme.primary : colorScheme.onPrimary);
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
expect(material.textStyle!.fontWeight, FontWeight.w500);
......@@ -56,8 +60,13 @@ void main() {
final TestGesture gesture = await tester.startGesture(center);
await tester.pump(); // start the splash animation
await tester.pump(const Duration(milliseconds: 100)); // splash is underway
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
expect(inkFeatures, paints..circle(color: colorScheme.onPrimary.withAlpha(0x3d))); // splash color is onPrimary(0.24)
// Material 3 uses the InkSparkle which uses a shader, so we can't capture
// the effect with paint methods.
if (!material3) {
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
expect(inkFeatures, paints..circle(color: colorScheme.onPrimary.withOpacity(0.24)));
}
// Only elevation changes when enabled and pressed.
material = tester.widget<Material>(buttonMaterial);
......@@ -65,11 +74,13 @@ void main() {
expect(material.borderOnForeground, true);
expect(material.borderRadius, null);
expect(material.clipBehavior, Clip.none);
expect(material.color, colorScheme.primary);
expect(material.elevation, 8);
expect(material.color, material3 ? colorScheme.onPrimary : colorScheme.primary);
expect(material.elevation, material3 ? 1 : 8);
expect(material.shadowColor, const Color(0xff000000));
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.onPrimary);
expect(material.shape, material3
? const StadiumBorder()
: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, material3 ? colorScheme.primary : colorScheme.onPrimary);
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
expect(material.textStyle!.fontWeight, FontWeight.w500);
......@@ -82,7 +93,7 @@ void main() {
final Key iconButtonKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: colorScheme),
theme: theme,
home: Center(
child: ElevatedButton.icon(
key: iconButtonKey,
......@@ -104,11 +115,13 @@ void main() {
expect(material.borderOnForeground, true);
expect(material.borderRadius, null);
expect(material.clipBehavior, Clip.none);
expect(material.color, colorScheme.primary);
expect(material.elevation, 2);
expect(material.color, material3 ? colorScheme.onPrimary : colorScheme.primary);
expect(material.elevation, material3? 1 : 2);
expect(material.shadowColor, const Color(0xff000000));
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.onPrimary);
expect(material.shape, material3
? const StadiumBorder()
: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, material3 ? colorScheme.primary : colorScheme.onPrimary);
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
expect(material.textStyle!.fontWeight, FontWeight.w500);
......@@ -117,7 +130,7 @@ void main() {
// Disabled ElevatedButton
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: colorScheme),
theme: theme,
home: const Center(
child: ElevatedButton(
onPressed: null,
......@@ -138,7 +151,9 @@ void main() {
expect(material.color, colorScheme.onSurface.withOpacity(0.12));
expect(material.elevation, 0.0);
expect(material.shadowColor, const Color(0xff000000));
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.shape, material3
? const StadiumBorder()
: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.onSurface.withOpacity(0.38));
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
......@@ -770,6 +785,9 @@ void main() {
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
return tester.pumpWidget(
MaterialApp(
// Test was setup using fonts from Material 2, so make sure we always
// test against englishLike2014.
theme: ThemeData(textTheme: Typography.englishLike2014),
home: Directionality(
textDirection: TextDirection.rtl,
child: Center(
......@@ -961,7 +979,15 @@ void main() {
testWidgets(testName, (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: const ColorScheme.light()),
theme: ThemeData(
colorScheme: const ColorScheme.light(),
// Force Material 2 defaults for the typography and size
// default values as the test was designed against these settings.
textTheme: Typography.englishLike2014,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(minimumSize: const Size(64, 36)),
),
),
home: Builder(
builder: (BuildContext context) {
return MediaQuery(
......@@ -1137,7 +1163,7 @@ void main() {
Widget buildFrame({ required bool enabled }) {
return MaterialApp(
theme: ThemeData.from(colorScheme: colorScheme),
theme: ThemeData.from(colorScheme: colorScheme, useMaterial3: false),
home: Center(
child: ElevatedButton(
onPressed: enabled ? () { } : null,
......@@ -1181,23 +1207,29 @@ void main() {
const Color borderColor = Color(0xff4caf50);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: const ColorScheme.light()),
theme: ThemeData(colorScheme: const ColorScheme.light(), textTheme: Typography.englishLike2014, useMaterial3: false),
home: Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16)),
side: BorderSide(width: 4, color: borderColor),
side: BorderSide(width: 10, color: borderColor),
),
minimumSize: const Size(64, 36),
),
onPressed: () { },
onPressed: () {},
child: const Text('button'),
),
),
),
);
expect(find.byType(ElevatedButton), paints ..path(strokeWidth: 4) ..drrect(color: borderColor));
expect(find.byType(ElevatedButton), paints ..drrect(
// Outer and inner rect that give the outline a width of 10.
outer: RRect.fromLTRBR(0.0, 0.0, 116.0, 36.0, const Radius.circular(16)),
inner: RRect.fromLTRBR(10.0, 10.0, 106.0, 26.0, const Radius.circular(16 - 10)),
color: borderColor)
);
});
testWidgets('Fixed size ElevatedButtons', (WidgetTester tester) async {
......@@ -1261,8 +1293,8 @@ void main() {
await tester.pumpAndSettle();
}
// Default splashFactory (from Theme.of().splashFactory), one splash circle drawn.
await tester.pumpWidget(buildFrame());
// InkRipple.splashFactory, one splash circle drawn.
await tester.pumpWidget(buildFrame(splashFactory: InkRipple.splashFactory));
{
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('test')));
final MaterialInkController material = Material.of(tester.element(find.text('test')))!;
......@@ -1386,6 +1418,7 @@ void main() {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(textTheme: Typography.englishLike2014),
home: Scaffold(
body: Center(
child: Column(
......
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