[flutter_releases] Flutter beta 2.13.0-0.2.pre Framework Cherrypicks (#102193)
* Ensure that the engine frame callbacks are installed if the first scheduled frame is a forced frame (#101544)
See https://github.com/flutter/flutter/issues/98419
* Migrate common buttons to Material 3 (#100794)
* Always finish the timeline event logged by Element.inflateWidget (#101794)
* 'Create candidate branch version flutter-2.13-candidate.0 for beta'
* 'Update Engine revision to 24a02fa5ee681840cdc842c22f4cb4bdd5ec3115 for beta release 2.13.0-0.2.pre'
* Update release-candidate-branch.version
Co-authored-by: Jason Simmons <jason-simmons@users.noreply.github.com>
Co-authored-by: Darren Austin <darrenaustin@google.com>
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index 4bb41dd..9828924 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-499984f99cfe01e3b69d962ec4847f70c4317694
+24a02fa5ee681840cdc842c22f4cb4bdd5ec3115
diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart
index 187e0f7..0694020 100644
--- a/dev/tools/gen_defaults/bin/gen_defaults.dart
+++ b/dev/tools/gen_defaults/bin/gen_defaults.dart
@@ -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 @@
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();
diff --git a/dev/tools/gen_defaults/lib/button_template.dart b/dev/tools/gen_defaults/lib/button_template.dart
new file mode 100644
index 0000000..3810871
--- /dev/null
+++ b/dev/tools/gen_defaults/lib/button_template.dart
@@ -0,0 +1,153 @@
+// 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;
+}
+''';
+}
diff --git a/dev/tools/gen_defaults/lib/template.dart b/dev/tools/gen_defaults/lib/template.dart
index f63bea0..ff37837 100644
--- a/dev/tools/gen_defaults/lib/template.dart
+++ b/dev/tools/gen_defaults/lib/template.dart
@@ -70,8 +70,8 @@
/// * [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 @@
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 @@
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" : ""})';
}
diff --git a/dev/tracing_tests/test/inflate_widget_update_test.dart b/dev/tracing_tests/test/inflate_widget_update_test.dart
new file mode 100644
index 0000000..acfd014
--- /dev/null
+++ b/dev/tracing_tests/test/inflate_widget_update_test.dart
@@ -0,0 +1,77 @@
+// 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 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'common.dart';
+
+void main() {
+ WidgetsFlutterBinding.ensureInitialized();
+ initTimelineTests();
+ test('Widgets with updated keys produce well formed timelines', () async {
+ await runFrame(() { runApp(const TestRoot()); });
+ await SchedulerBinding.instance.endOfFrame;
+
+ debugProfileBuildsEnabled = true;
+
+ await runFrame(() {
+ TestRoot.state.updateKey();
+ });
+
+ int buildCount = 0;
+ for (final TimelineEvent event in await fetchTimelineEvents()) {
+ if (event.json!['name'] == 'BUILD') {
+ final String ph = event.json!['ph'] as String;
+ if (ph == 'B') {
+ buildCount++;
+ } else if (ph == 'E') {
+ buildCount--;
+ }
+ }
+ }
+ expect(buildCount, 0);
+
+ debugProfileBuildsEnabled = false;
+ }, skip: isBrowser); // [intended] uses dart:isolate and io.
+}
+
+class TestRoot extends StatefulWidget {
+ const TestRoot({super.key});
+
+ static late TestRootState state;
+
+ @override
+ State<TestRoot> createState() => TestRootState();
+}
+
+class TestRootState extends State<TestRoot> {
+ final Key _globalKey = GlobalKey();
+ Key _localKey = UniqueKey();
+
+ @override
+ void initState() {
+ super.initState();
+ TestRoot.state = this;
+ }
+
+ void updateKey() {
+ setState(() {
+ _localKey = UniqueKey();
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ key: _localKey,
+ child: SizedBox(
+ key: _globalKey,
+ width: 100,
+ height: 100,
+ ),
+ );
+ }
+}
diff --git a/examples/api/lib/material/button_style/button_style.0.dart b/examples/api/lib/material/button_style/button_style.0.dart
new file mode 100644
index 0000000..bff8602
--- /dev/null
+++ b/examples/api/lib/material/button_style/button_style.0.dart
@@ -0,0 +1,95 @@
+// 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')),
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/flutter/lib/src/material/button_style.dart b/packages/flutter/lib/src/material/button_style.dart
index 3d1ad80..4026fd7 100644
--- a/packages/flutter/lib/src/material/button_style.dart
+++ b/packages/flutter/lib/src/material/button_style.dart
@@ -91,6 +91,27 @@
/// 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 @@
this.foregroundColor,
this.overlayColor,
this.shadowColor,
+ this.surfaceTintColor,
this.elevation,
this.padding,
this.minimumSize,
@@ -150,6 +172,11 @@
/// [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 @@
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 @@
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 @@
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 @@
foregroundColor,
overlayColor,
shadowColor,
+ surfaceTintColor,
elevation,
padding,
minimumSize,
@@ -371,6 +402,7 @@
&& 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 @@
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 @@
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),
diff --git a/packages/flutter/lib/src/material/button_style_button.dart b/packages/flutter/lib/src/material/button_style_button.dart
index 385fd22..c30480b 100644
--- a/packages/flutter/lib/src/material/button_style_button.dart
+++ b/packages/flutter/lib/src/material/button_style_button.dart
@@ -247,6 +247,7 @@
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 @@
shape: resolvedShape!.copyWith(side: resolvedSide),
color: resolvedBackgroundColor,
shadowColor: resolvedShadowColor,
+ surfaceTintColor: resolvedSurfaceTintColor,
type: resolvedBackgroundColor == null ? MaterialType.transparency : MaterialType.button,
animationDuration: resolvedAnimationDuration,
clipBehavior: widget.clipBehavior,
diff --git a/packages/flutter/lib/src/material/elevated_button.dart b/packages/flutter/lib/src/material/elevated_button.dart
index dbb3285..b01c3d6 100644
--- a/packages/flutter/lib/src/material/elevated_button.dart
+++ b/packages/flutter/lib/src/material/elevated_button.dart
@@ -148,6 +148,7 @@
Color? onPrimary,
Color? onSurface,
Color? shadowColor,
+ Color? surfaceTintColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
@@ -187,6 +188,7 @@
foregroundColor: foregroundColor,
overlayColor: overlayColor,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
+ surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
elevation: elevationValue,
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
@@ -228,6 +230,8 @@
/// The color of the [ButtonStyle.textStyle] is not used, the
/// [ButtonStyle.foregroundColor] color is used instead.
///
+ /// ## Material 2 defaults
+ ///
/// * `textStyle` - Theme.textTheme.button
/// * `backgroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.12)
@@ -245,7 +249,7 @@
/// * hovered or focused - 4
/// * pressed - 8
/// * `padding`
- /// * textScaleFactor <= 1 - horizontal(16)
+ /// * `textScaleFactor <= 1` - horizontal(16)
/// * `1 < textScaleFactor <= 2` - lerp(horizontal(16), horizontal(8))
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
@@ -276,38 +280,75 @@
/// 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`
+ /// * disabled - Theme.colorScheme.onSurface(0.12)
+ /// * others - Theme.colorScheme.surface
+ /// * `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)
+ /// * `shadowColor` - Theme.colorScheme.shadow
+ /// * `surfaceTintColor` - Theme.colorScheme.surfaceTint
+ /// * `elevation`
+ /// * disabled - 0
+ /// * default - 1
+ /// * hovered - 3
+ /// * focused or pressed - 1
+ /// * `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` - 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.symmetric(horizontal: 16),
- const EdgeInsets.symmetric(horizontal: 8),
- const EdgeInsets.symmetric(horizontal: 4),
- MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
- );
-
- return styleFrom(
- primary: colorScheme.primary,
- onPrimary: colorScheme.onPrimary,
- onSurface: colorScheme.onSurface,
- shadowColor: theme.shadowColor,
- elevation: 2,
- 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,
+ onPrimary: colorScheme.onPrimary,
+ onSurface: colorScheme.onSurface,
+ shadowColor: theme.shadowColor,
+ elevation: 2,
+ 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 [ElevatedButtonThemeData.style] of the closest
@@ -318,6 +359,15 @@
}
}
+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 _ElevatedButtonDefaultBackground extends MaterialStateProperty<Color?> with Diagnosticable {
_ElevatedButtonDefaultBackground(this.primary, this.onSurface);
@@ -457,3 +507,115 @@
);
}
}
+
+// 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 =>
+ MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+ if (states.contains(MaterialState.disabled))
+ return _colors.onSurface.withOpacity(0.12);
+ return _colors.surface;
+ });
+
+ @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;
+ });
+
+ @override
+ MaterialStateProperty<Color>? get shadowColor =>
+ ButtonStyleButton.allOrNull<Color>(_colors.shadow);
+
+ @override
+ MaterialStateProperty<Color>? get surfaceTintColor =>
+ ButtonStyleButton.allOrNull<Color>(_colors.surfaceTint);
+
+ @override
+ MaterialStateProperty<double>? get elevation =>
+ MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+ if (states.contains(MaterialState.disabled))
+ return 0.0;
+ if (states.contains(MaterialState.hovered))
+ return 3.0;
+ if (states.contains(MaterialState.focused))
+ return 1.0;
+ if (states.contains(MaterialState.pressed))
+ return 1.0;
+ return 1.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
diff --git a/packages/flutter/lib/src/material/outlined_button.dart b/packages/flutter/lib/src/material/outlined_button.dart
index 0e233e6..e6fbdcd 100644
--- a/packages/flutter/lib/src/material/outlined_button.dart
+++ b/packages/flutter/lib/src/material/outlined_button.dart
@@ -146,6 +146,7 @@
Color? onSurface,
Color? backgroundColor,
Color? shadowColor,
+ Color? surfaceTintColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
@@ -179,6 +180,7 @@
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 @@
/// 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 @@
/// * `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 @@
}
}
+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 @@
);
}
}
+
+// 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
diff --git a/packages/flutter/lib/src/material/text_button.dart b/packages/flutter/lib/src/material/text_button.dart
index 92fac91..2caa127 100644
--- a/packages/flutter/lib/src/material/text_button.dart
+++ b/packages/flutter/lib/src/material/text_button.dart
@@ -147,6 +147,7 @@
Color? onSurface,
Color? backgroundColor,
Color? shadowColor,
+ Color? surfaceTintColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
@@ -180,6 +181,7 @@
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 @@
/// 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 @@
/// 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 @@
}
}
+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 @@
);
}
}
+
+// 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
diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart
index b7f79df..95b445a 100644
--- a/packages/flutter/lib/src/material/theme_data.dart
+++ b/packages/flutter/lib/src/material/theme_data.dart
@@ -1224,21 +1224,25 @@
/// {@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
@@ -1255,12 +1259,15 @@
/// * [AlertDialog]
/// * [Card]
/// * [Dialog]
+ /// * [ElevatedButton]
/// * [FloatingActionButton]
/// * [Material]
/// * [NavigationBar]
/// * [NavigationRail]
+ /// * [OutlinedButton]
/// * [StretchingOverscrollIndicator], replacing the
/// [GlowingOverscrollIndicator]
+ /// * [TextButton]
///
/// See also:
///
diff --git a/packages/flutter/lib/src/scheduler/binding.dart b/packages/flutter/lib/src/scheduler/binding.dart
index eb1eb76..1be5607 100644
--- a/packages/flutter/lib/src/scheduler/binding.dart
+++ b/packages/flutter/lib/src/scheduler/binding.dart
@@ -825,6 +825,7 @@
debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
return true;
}());
+ ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index ac3673e..c6382b9 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -3793,33 +3793,35 @@
);
}
- final Key? key = newWidget.key;
- if (key is GlobalKey) {
- final Element? newChild = _retakeInactiveElement(key, newWidget);
- if (newChild != null) {
- assert(newChild._parent == null);
- assert(() {
- _debugCheckForCycles(newChild);
- return true;
- }());
- newChild._activateWithParent(this, newSlot);
- final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
- assert(newChild == updatedChild);
- return updatedChild!;
+ try {
+ final Key? key = newWidget.key;
+ if (key is GlobalKey) {
+ final Element? newChild = _retakeInactiveElement(key, newWidget);
+ if (newChild != null) {
+ assert(newChild._parent == null);
+ assert(() {
+ _debugCheckForCycles(newChild);
+ return true;
+ }());
+ newChild._activateWithParent(this, newSlot);
+ final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
+ assert(newChild == updatedChild);
+ return updatedChild!;
+ }
}
+ final Element newChild = newWidget.createElement();
+ assert(() {
+ _debugCheckForCycles(newChild);
+ return true;
+ }());
+ newChild.mount(this, newSlot);
+ assert(newChild._lifecycleState == _ElementLifecycle.active);
+
+ return newChild;
+ } finally {
+ if (isTimelineTracked)
+ Timeline.finishSync();
}
- final Element newChild = newWidget.createElement();
- assert(() {
- _debugCheckForCycles(newChild);
- return true;
- }());
- newChild.mount(this, newSlot);
- assert(newChild._lifecycleState == _ElementLifecycle.active);
-
- if (isTimelineTracked)
- Timeline.finishSync();
-
- return newChild;
}
void _debugCheckForCycles(Element newChild) {
diff --git a/packages/flutter/test/material/button_style_test.dart b/packages/flutter/test/material/button_style_test.dart
index b9a2640..32afc9b 100644
--- a/packages/flutter/test/material/button_style_test.dart
+++ b/packages/flutter/test/material/button_style_test.dart
@@ -20,6 +20,8 @@
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 @@
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 @@
'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 @@
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 @@
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
overlayColor: overlayColor,
+ shadowColor: shadowColor,
+ surfaceTintColor: surfaceTintColor,
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
@@ -132,6 +142,8 @@
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
overlayColor: overlayColor,
+ shadowColor: shadowColor,
+ surfaceTintColor: surfaceTintColor,
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
diff --git a/packages/flutter/test/material/elevated_button_test.dart b/packages/flutter/test/material/elevated_button_test.dart
index 12afdeb..3478826 100644
--- a/packages/flutter/test/material/elevated_button_test.dart
+++ b/packages/flutter/test/material/elevated_button_test.dart
@@ -14,11 +14,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// Disabled ElevatedButton
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: const Center(
child: ElevatedButton(
onPressed: null,
@@ -138,7 +151,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
await tester.pumpWidget(
MaterialApp(
+ theme: ThemeData(textTheme: Typography.englishLike2014),
home: Scaffold(
body: Center(
child: Column(
diff --git a/packages/flutter/test/material/outlined_button_test.dart b/packages/flutter/test/material/outlined_button_test.dart
index bcdbeb4..ecbf00c 100644
--- a/packages/flutter/test/material/outlined_button_test.dart
+++ b/packages/flutter/test/material/outlined_button_test.dart
@@ -14,11 +14,13 @@
void main() {
testWidgets('OutlinedButton, OutlinedButton.icon defaults', (WidgetTester tester) async {
const ColorScheme colorScheme = ColorScheme.light();
+ final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
+ final bool material3 = theme.useMaterial3;
// Enabled OutlinedButton
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: Center(
child: OutlinedButton(
onPressed: () { },
@@ -40,12 +42,14 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
- expect(material.shape, isInstanceOf<RoundedRectangleBorder>());
- RoundedRectangleBorder materialShape = material.shape! as RoundedRectangleBorder;
- expect(materialShape.side, BorderSide(color: colorScheme.onSurface.withOpacity(0.12)));
- expect(materialShape.borderRadius, const BorderRadius.all(Radius.circular(4.0)));
+ expect(material.shape, material3
+ ? StadiumBorder(side: BorderSide(color: colorScheme.outline))
+ : RoundedRectangleBorder(
+ side: BorderSide(color: colorScheme.onSurface.withOpacity(0.12)),
+ borderRadius: const BorderRadius.all(Radius.circular(4))
+ ));
expect(material.textStyle!.color, colorScheme.primary);
expect(material.textStyle!.fontFamily, 'Roboto');
@@ -60,8 +64,13 @@
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.primary.withAlpha(0x1f))); // splash color is primary(0.12)
+
+ // 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.primary.withOpacity(0.12)));
+ }
await gesture.up();
await tester.pumpAndSettle();
@@ -73,12 +82,14 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
- expect(material.shape, isInstanceOf<RoundedRectangleBorder>());
- materialShape = material.shape! as RoundedRectangleBorder;
- expect(materialShape.side, BorderSide(color: colorScheme.onSurface.withOpacity(0.12)));
- expect(materialShape.borderRadius, const BorderRadius.all(Radius.circular(4.0)));
+ expect(material.shape, material3
+ ? StadiumBorder(side: BorderSide(color: colorScheme.outline))
+ : RoundedRectangleBorder(
+ side: BorderSide(color: colorScheme.onSurface.withOpacity(0.12)),
+ borderRadius: const BorderRadius.all(Radius.circular(4))
+ ));
expect(material.textStyle!.color, colorScheme.primary);
expect(material.textStyle!.fontFamily, 'Roboto');
@@ -90,7 +101,7 @@
final Key iconButtonKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: Center(
child: OutlinedButton.icon(
key: iconButtonKey,
@@ -114,12 +125,14 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
- expect(material.shape, isInstanceOf<RoundedRectangleBorder>());
- materialShape = material.shape! as RoundedRectangleBorder;
- expect(materialShape.side, BorderSide(color: colorScheme.onSurface.withOpacity(0.12)));
- expect(materialShape.borderRadius, const BorderRadius.all(Radius.circular(4.0)));
+ expect(material.shape, material3
+ ? StadiumBorder(side: BorderSide(color: colorScheme.outline))
+ : RoundedRectangleBorder(
+ side: BorderSide(color: colorScheme.onSurface.withOpacity(0.12)),
+ borderRadius: const BorderRadius.all(Radius.circular(4))
+ ));
expect(material.textStyle!.color, colorScheme.primary);
expect(material.textStyle!.fontFamily, 'Roboto');
@@ -130,7 +143,7 @@
// Disabled OutlinedButton
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: const Center(
child: OutlinedButton(
onPressed: null,
@@ -147,12 +160,14 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
- expect(material.shape, isInstanceOf<RoundedRectangleBorder>());
- materialShape = material.shape! as RoundedRectangleBorder;
- expect(materialShape.side, BorderSide(color: colorScheme.onSurface.withOpacity(0.12)));
- expect(materialShape.borderRadius, const BorderRadius.all(Radius.circular(4.0)));
+ expect(material.shape, material3
+ ? StadiumBorder(side: BorderSide(color: colorScheme.onSurface.withOpacity(0.12)))
+ : RoundedRectangleBorder(
+ side: BorderSide(color: colorScheme.onSurface.withOpacity(0.12)),
+ borderRadius: const BorderRadius.all(Radius.circular(4))
+ ));
expect(material.textStyle!.color, colorScheme.onSurface.withOpacity(0.38));
expect(material.textStyle!.fontFamily, 'Roboto');
@@ -531,6 +546,10 @@
child: OutlinedButton(
style: ButtonStyle(
side: MaterialStateProperty.resolveWith<BorderSide>(getBorderSide),
+ // Test assumes a rounded rect for the shape
+ shape: ButtonStyleButton.allOrNull(
+ const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4)))
+ ),
),
onPressed: () {},
focusNode: focusNode,
@@ -808,13 +827,14 @@
return Directionality(
textDirection: TextDirection.ltr,
child: Theme(
- data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
+ data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, textTheme: Typography.englishLike2014),
child: Container(
alignment: Alignment.topLeft,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: const RoundedRectangleBorder(), // default border radius is 0
backgroundColor: fillColor,
+ minimumSize: const Size(64, 36),
).copyWith(
side: MaterialStateProperty.resolveWith<BorderSide>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
@@ -964,20 +984,24 @@
testWidgets('OutlinedButton scales textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: MediaQuery(
- data: const MediaQueryData(),
- child: Center(
- child: OutlinedButton(
- style: ButtonStyle(
- // Specifying minimumSize to mimic the original minimumSize for
- // RaisedButton so that the corresponding button size matches
- // the original version of this test.
- minimumSize: MaterialStateProperty.all<Size>(const Size(88, 36)),
+ Theme(
+ // Force Material 2 typography.
+ data: ThemeData(textTheme: Typography.englishLike2014),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: MediaQuery(
+ data: const MediaQueryData(),
+ child: Center(
+ child: OutlinedButton(
+ style: ButtonStyle(
+ // Specifying minimumSize to mimic the original minimumSize for
+ // RaisedButton so that the corresponding button size matches
+ // the original version of this test.
+ minimumSize: MaterialStateProperty.all<Size>(const Size(88, 36)),
+ ),
+ onPressed: () {},
+ child: const Text('ABC'),
),
- onPressed: () {},
- child: const Text('ABC'),
),
),
),
@@ -989,20 +1013,24 @@
// textScaleFactor expands text, but not button.
await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: MediaQuery(
- data: const MediaQueryData(textScaleFactor: 1.3),
- child: Center(
- child: OutlinedButton(
- style: ButtonStyle(
- // Specifying minimumSize to mimic the original minimumSize for
- // RaisedButton so that the corresponding button size matches
- // the original version of this test.
- minimumSize: MaterialStateProperty.all<Size>(const Size(88, 36)),
+ Theme(
+ // Force Material 2 typography.
+ data: ThemeData(textTheme: Typography.englishLike2014),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: MediaQuery(
+ data: const MediaQueryData(textScaleFactor: 1.3),
+ child: Center(
+ child: OutlinedButton(
+ style: ButtonStyle(
+ // Specifying minimumSize to mimic the original minimumSize for
+ // RaisedButton so that the corresponding button size matches
+ // the original version of this test.
+ minimumSize: MaterialStateProperty.all<Size>(const Size(88, 36)),
+ ),
+ onPressed: () {},
+ child: const Text('ABC'),
),
- onPressed: () {},
- child: const Text('ABC'),
),
),
),
@@ -1017,14 +1045,18 @@
// Set text scale large enough to expand text and button.
await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: MediaQuery(
- data: const MediaQueryData(textScaleFactor: 3.0),
- child: Center(
- child: OutlinedButton(
- onPressed: () {},
- child: const Text('ABC'),
+ Theme(
+ // Force Material 2 typography.
+ data: ThemeData(textTheme: Typography.englishLike2014),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: MediaQuery(
+ data: const MediaQueryData(textScaleFactor: 3.0),
+ child: Center(
+ child: OutlinedButton(
+ onPressed: () {},
+ child: const Text('ABC'),
+ ),
),
),
),
@@ -1039,7 +1071,6 @@
expect(tester.getSize(find.byType(Text)).height, equals(42.0));
});
-
testWidgets('OutlinedButton onPressed and onLongPress callbacks are distinctly recognized', (WidgetTester tester) async {
bool didPressButton = false;
bool didLongPressButton = false;
@@ -1078,11 +1109,15 @@
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
return tester.pumpWidget(
MaterialApp(
+ theme: ThemeData(textTheme: Typography.englishLike2014),
home: Directionality(
textDirection: TextDirection.rtl,
child: Center(
child: OutlinedButton(
- style: ButtonStyle(visualDensity: visualDensity),
+ style: ButtonStyle(
+ visualDensity: visualDensity,
+ minimumSize: ButtonStyleButton.allOrNull(const Size(64, 36)),
+ ),
key: key,
onPressed: () {},
child: useText
@@ -1203,7 +1238,15 @@
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,
+ outlinedButtonTheme: OutlinedButtonThemeData(
+ style: OutlinedButton.styleFrom(minimumSize: const Size(64, 36)),
+ ),
+ ),
home: Builder(
builder: (BuildContext context) {
return MediaQuery(
@@ -1434,8 +1477,8 @@
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')))!;
@@ -1559,6 +1602,7 @@
await tester.pumpWidget(
MaterialApp(
+ theme: ThemeData(textTheme: Typography.englishLike2014),
home: Scaffold(
body: Center(
child: Column(
diff --git a/packages/flutter/test/material/text_button_test.dart b/packages/flutter/test/material/text_button_test.dart
index ba90dc9..8da1d07 100644
--- a/packages/flutter/test/material/text_button_test.dart
+++ b/packages/flutter/test/material/text_button_test.dart
@@ -14,11 +14,13 @@
void main() {
testWidgets('TextButton, TextButton.icon defaults', (WidgetTester tester) async {
const ColorScheme colorScheme = ColorScheme.light();
+ final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
+ final bool material3 = theme.useMaterial3;
// Enabled TextButton
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: Center(
child: TextButton(
onPressed: () { },
@@ -40,8 +42,10 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
- expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
+ expect(material.shape, material3
+ ? const StadiumBorder()
+ : const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.primary);
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
@@ -55,8 +59,13 @@
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.primary.withAlpha(0x1f))); // splash color is primary(0.12)
+
+ // 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.primary.withOpacity(0.12)));
+ }
await gesture.up();
await tester.pumpAndSettle();
@@ -68,8 +77,10 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
- expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
+ expect(material.shape, material3
+ ? const StadiumBorder()
+ : const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.primary);
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
@@ -80,7 +91,7 @@
final Key iconButtonKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: Center(
child: TextButton.icon(
key: iconButtonKey,
@@ -104,8 +115,10 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
- expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
+ expect(material.shape, material3
+ ? const StadiumBorder()
+ : const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))));
expect(material.textStyle!.color, colorScheme.primary);
expect(material.textStyle!.fontFamily, 'Roboto');
expect(material.textStyle!.fontSize, 14);
@@ -115,7 +128,7 @@
// Disabled TextButton
await tester.pumpWidget(
MaterialApp(
- theme: ThemeData.from(colorScheme: colorScheme),
+ theme: theme,
home: const Center(
child: TextButton(
onPressed: null,
@@ -132,8 +145,10 @@
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
- expect(material.shadowColor, const Color(0xff000000));
- expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
+ expect(material.shadowColor, material3 ? null : const Color(0xff000000));
+ 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);
@@ -517,14 +532,18 @@
testWidgets('Does TextButton scale with font scale changes', (WidgetTester tester) async {
await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: MediaQuery(
- data: const MediaQueryData(),
- child: Center(
- child: TextButton(
- onPressed: () { },
- child: const Text('ABC'),
+ Theme(
+ // Force Material 2 typography.
+ data: ThemeData(textTheme: Typography.englishLike2014),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: MediaQuery(
+ data: const MediaQueryData(),
+ child: Center(
+ child: TextButton(
+ onPressed: () { },
+ child: const Text('ABC'),
+ ),
),
),
),
@@ -536,14 +555,18 @@
// textScaleFactor expands text, but not button.
await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: MediaQuery(
- data: const MediaQueryData(textScaleFactor: 1.3),
- child: Center(
- child: TextButton(
- onPressed: () { },
- child: const Text('ABC'),
+ Theme(
+ // Force Material 2 typography.
+ data: ThemeData(textTheme: Typography.englishLike2014),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: MediaQuery(
+ data: const MediaQueryData(textScaleFactor: 1.3),
+ child: Center(
+ child: TextButton(
+ onPressed: () { },
+ child: const Text('ABC'),
+ ),
),
),
),
@@ -559,14 +582,18 @@
// Set text scale large enough to expand text and button.
await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: MediaQuery(
- data: const MediaQueryData(textScaleFactor: 3.0),
- child: Center(
- child: TextButton(
- onPressed: () { },
- child: const Text('ABC'),
+ Theme(
+ // Force Material 2 typography.
+ data: ThemeData(textTheme: Typography.englishLike2014),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: MediaQuery(
+ data: const MediaQueryData(textScaleFactor: 3.0),
+ child: Center(
+ child: TextButton(
+ onPressed: () { },
+ child: const Text('ABC'),
+ ),
),
),
),
@@ -581,7 +608,6 @@
expect(tester.getSize(find.byType(Text)).height, equals(42.0));
});
-
testWidgets('TextButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
return Theme(
@@ -591,6 +617,7 @@
child: Center(
child: TextButton(
key: key,
+ style: TextButton.styleFrom(minimumSize: const Size(64, 36)),
child: const SizedBox(width: 50.0, height: 8.0),
onPressed: () { },
),
@@ -855,6 +882,7 @@
Future<void> buildTest(VisualDensity visualDensity, { bool useText = false }) async {
return tester.pumpWidget(
MaterialApp(
+ theme: ThemeData(textTheme: Typography.englishLike2014),
home: Directionality(
textDirection: TextDirection.rtl,
child: Center(
@@ -993,7 +1021,15 @@
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,
+ textButtonTheme: TextButtonThemeData(
+ style: TextButton.styleFrom(minimumSize: const Size(64, 36)),
+ ),
+ ),
home: Builder(
builder: (BuildContext context) {
return MediaQuery(
@@ -1227,8 +1263,8 @@
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')))!;
@@ -1352,6 +1388,7 @@
await tester.pumpWidget(
MaterialApp(
+ theme: ThemeData(textTheme: Typography.englishLike2014),
home: Scaffold(
body: Center(
child: Column(
diff --git a/packages/flutter/test/scheduler/binding_test.dart b/packages/flutter/test/scheduler/binding_test.dart
new file mode 100644
index 0000000..d4018ff
--- /dev/null
+++ b/packages/flutter/test/scheduler/binding_test.dart
@@ -0,0 +1,16 @@
+// 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 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ test('scheduleForcedFrame sets up frame callbacks', () async {
+ SchedulerBinding.instance.scheduleForcedFrame();
+ expect(SchedulerBinding.instance.platformDispatcher.onBeginFrame, isNotNull);
+ });
+}