diff --git a/dev/bots/check_code_samples.dart b/dev/bots/check_code_samples.dart index 41e4554649..8d947f57a4 100644 --- a/dev/bots/check_code_samples.dart +++ b/dev/bots/check_code_samples.dart @@ -374,6 +374,7 @@ final Set _knownMissingTests = { 'examples/api/test/material/checkbox/checkbox.1_test.dart', 'examples/api/test/material/checkbox/checkbox.0_test.dart', 'examples/api/test/material/navigation_rail/navigation_rail.extended_animation.0_test.dart', + 'examples/api/test/material/text_button/text_button.0_test.dart', 'examples/api/test/rendering/growth_direction/growth_direction.0_test.dart', 'examples/api/test/rendering/sliver_grid/sliver_grid_delegate_with_fixed_cross_axis_count.0_test.dart', 'examples/api/test/rendering/sliver_grid/sliver_grid_delegate_with_fixed_cross_axis_count.1_test.dart', diff --git a/examples/api/lib/material/text_button/text_button.0.dart b/examples/api/lib/material/text_button/text_button.0.dart index 0f7f020928..98fa6bc770 100644 --- a/examples/api/lib/material/text_button/text_button.0.dart +++ b/examples/api/lib/material/text_button/text_button.0.dart @@ -6,461 +6,78 @@ import 'package:flutter/material.dart'; /// Flutter code sample for [TextButton]. -void main() { - runApp(const TextButtonExampleApp()); -} +void main() => runApp(const TextButtonExampleApp()); -class TextButtonExampleApp extends StatefulWidget { - const TextButtonExampleApp({ super.key }); - - @override - State createState() => _TextButtonExampleAppState(); -} - -class _TextButtonExampleAppState extends State { - bool darkMode = false; +class TextButtonExampleApp extends StatelessWidget { + const TextButtonExampleApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( - themeMode: darkMode ? ThemeMode.dark : ThemeMode.light, - theme: ThemeData(brightness: Brightness.light), - darkTheme: ThemeData(brightness: Brightness.dark), home: Scaffold( - body: Padding( - padding: const EdgeInsets.all(16), - child: TextButtonExample( - darkMode: darkMode, - updateDarkMode: (bool value) { - setState(() { darkMode = value; }); - }, - ), - ), + appBar: AppBar(title: const Text('TextButton Sample')), + body: const TextButtonExample(), ), ); } } -class TextButtonExample extends StatefulWidget { - const TextButtonExample({ super.key, required this.darkMode, required this.updateDarkMode }); - - final bool darkMode; - final ValueChanged updateDarkMode; - - @override - State createState() => _TextButtonExampleState(); -} - -class _TextButtonExampleState extends State { - TextDirection textDirection = TextDirection.ltr; - ThemeMode themeMode = ThemeMode.light; - late final ScrollController scrollController; - - static const Widget verticalSpacer = SizedBox(height: 16); - static const Widget horizontalSpacer = SizedBox(width: 32); - - @override - void initState() { - scrollController = ScrollController(); - super.initState(); - } - - @override - void dispose() { - scrollController.dispose(); - super.dispose(); - } +class TextButtonExample extends StatelessWidget { + const TextButtonExample({super.key}); @override Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - - // Adapt colors that are not part of the color scheme to - // the current dark/light mode. Used to define TextButton #7's - // gradients. - final (Color color1, Color color2, Color color3) = switch (colorScheme.brightness) { - Brightness.light => (Colors.blue.withOpacity(1.0), Colors.orange.withOpacity(1.0), Colors.yellow.withOpacity(1.0)), - Brightness.dark => (Colors.purple.withOpacity(1.0), Colors.cyan.withOpacity(1.0), Colors.yellow.withOpacity(1.0)), - }; - - // This gradient's appearance reflects the button's state. - // Always return a gradient decoration so that AnimatedContainer - // can interpolorate in between. Used by TextButton #7. - Decoration? statesToDecoration(Set states) { - if (states.contains(MaterialState.pressed)) { - return BoxDecoration( - gradient: LinearGradient(colors: [color2, color2]), // solid fill - ); - } - return BoxDecoration( - gradient: LinearGradient( - colors: switch (states.contains(MaterialState.hovered)) { - true => [color1, color2], - false => [color2, color1], - }, - ), - ); - } - - // To make this method a little easier to read, the buttons that - // appear in the two columns to the right of the demo switches - // Card are broken out below. - - final List columnOneButtons = [ - TextButton( - onPressed: () {}, - child: const Text('Enabled'), - ), - verticalSpacer, - - const TextButton( - onPressed: null, - child: Text('Disabled'), - ), - verticalSpacer, - - TextButton.icon( - onPressed: () {}, - icon: const Icon(Icons.access_alarm), - label: const Text('TextButton.icon #1'), - ), - verticalSpacer, - - // Override the foreground and background colors. - // - // In this example, and most of the ones that follow, we're using - // the TextButton.styleFrom() convenience method to create a ButtonStyle. - // The styleFrom method is a little easier because it creates - // ButtonStyle MaterialStateProperty parameters for you. - // In this case, Specifying foregroundColor overrides the text, - // icon and overlay (splash and highlight) colors a little differently - // depending on the button's state. BackgroundColor is just the background - // color for all states. - TextButton.icon( - style: TextButton.styleFrom( - foregroundColor: colorScheme.onError, - backgroundColor: colorScheme.error, - ), - onPressed: () { }, - icon: const Icon(Icons.access_alarm), - label: const Text('TextButton.icon #2'), - ), - verticalSpacer, - - // Override the button's shape and its border. - // - // In this case we've specified a shape that has border - the - // RoundedRectangleBorder's side parameter. If the styleFrom - // side parameter was also specified, or if the TextButtonTheme - // defined above included a side parameter, then that would - // override the RoundedRectangleBorder's side. - TextButton( - style: TextButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(8)), - side: BorderSide( - color: colorScheme.primary, - width: 5, + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + style: TextButton.styleFrom( + textStyle: const TextStyle(fontSize: 20), ), + onPressed: null, + child: const Text('Disabled'), ), - ), - onPressed: () { }, - child: const Text('TextButton #3'), - ), - verticalSpacer, - - // Override overlay: the ink splash and highlight colors. - // - // The styleFrom method turns the specified overlayColor - // into a value MaterialStyleProperty ButtonStyle.overlay - // value that uses opacities depending on the button's state. - // If the overlayColor was Colors.transparent, no splash - // or highlights would be shown. - TextButton( - style: TextButton.styleFrom( - overlayColor: Colors.yellow, - ), - onPressed: () { }, - child: const Text('TextButton #4'), - ), - ]; - - final List columnTwoButtons = [ - // Override the foregroundBuilder: apply a ShaderMask. - // - // Apply a ShaderMask to the button's child. This kind of thing - // can be applied to one button easily enough by just wrapping the - // button's child directly. However to affect all buttons in this - // way you can specify a similar foregroundBuilder in a TextButton - // theme or the MaterialApp theme's ThemeData.textButtonTheme. - TextButton( - style: TextButton.styleFrom( - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return ShaderMask( - shaderCallback: (Rect bounds) { - return LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - colorScheme.primary, - colorScheme.onPrimary, - ], - ).createShader(bounds); - }, - blendMode: BlendMode.srcATop, - child: child, - ); - }, - ), - onPressed: () { }, - child: const Text('TextButton #5'), - ), - verticalSpacer, - - // Override the foregroundBuilder: add an underline. - // - // Add a border around button's child. In this case the - // border only appears when the button is hovered or pressed - // (if it's pressed it's always hovered too). Not that this - // border is different than the one specified with the styleFrom - // side parameter (or the ButtonStyle.side property). The foregroundBuilder - // is applied to a widget that contains the child and has already - // included the button's padding. It is unaffected by the button's shape. - // The styleFrom side parameter controls the button's outermost border and it - // outlines the button's shape. - TextButton( - style: TextButton.styleFrom( - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: BoxDecoration( - border: states.contains(MaterialState.hovered) - ? Border(bottom: BorderSide(color: colorScheme.primary)) - : const Border(), // essentially "no border" - ), - child: child, - ); - }, - ), - onPressed: () { }, - child: const Text('TextButton #6'), - ), - verticalSpacer, - - // Override the backgroundBuilder to add a state specific gradient background - // and add an outline that only appears when the button is hovered or pressed. - // - // The gradient background decoration is computed by the statesToDecoration() - // method. The gradient flips horizontally when the button is hovered (watch - // closely). Because we want the outline to only appear when the button is hovered - // we can't use the styleFrom() side parameter, because that creates the same - // outline for all states. The ButtonStyle.copyWith() method is used to add - // a MaterialState property that does the right thing. - // - // The gradient background is translucent - all of the colors have opacity 0.5 - - // so the overlay's splash and highlight colors are visible even though they're - // drawn on the Material widget that's effectively behind the background. The - // border is also translucent, so if you look carefully, you'll see that the - // background - which is part of the button's Material but is drawn on top of the - // the background gradient - shows through the border. - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - overlayColor: color2, - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return AnimatedContainer( - duration: const Duration(milliseconds: 500), - decoration: statesToDecoration(states), - child: child, - ); - }, - ).copyWith( - side: MaterialStateProperty.resolveWith((Set states) { - if (states.contains(MaterialState.hovered)) { - return BorderSide(width: 3, color: color3); - } - return null; // defer to the default - }), - ), - child: const Text('TextButton #7'), - ), - verticalSpacer, - - // Override the backgroundBuilder to add a grass image background. - // - // The image is clipped to the button's shape. We've included an Ink widget - // because the background image is opaque and would otherwise obscure the splash - // and highlight overlays that are painted on the button's Material widget - // by default. They're drawn on the Ink widget instead. The foreground color - // was overridden as well because white shows up a little better on the mottled - // green background. - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return Ink( - decoration: const BoxDecoration( - image: DecorationImage( - image: NetworkImage(grassUrl), - fit: BoxFit.cover, - ), - ), - child: child, - ); - }, - ), - child: const Text('TextButton #8'), - ), - verticalSpacer, - - // Override the foregroundBuilder to specify images for the button's pressed - // hovered and inactive states. - // - // This is an example of completely changing the default appearance of a button - // by specifying images for each state and by turning off the overlays by - // overlayColor: Colors.transparent. AnimatedContainer takes care of the - // fade in and out segues between images. - // - // This foregroundBuilder function ignores its child parameter. Unfortunately - // TextButton's child parameter is required, so we still have - // to provide one. - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - overlayColor: Colors.transparent, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - String url = states.contains(MaterialState.hovered) ? smiley3Url : smiley1Url; - if (states.contains(MaterialState.pressed)) { - url = smiley2Url; - } - return AnimatedContainer( - width: 64, - height: 64, - duration: const Duration(milliseconds: 300), - curve: Curves.fastOutSlowIn, - decoration: BoxDecoration( - image: DecorationImage( - image: NetworkImage(url), - fit: BoxFit.contain, - ), - ), - ); - }, - ), - child: const Text('This child is not used'), - ), - ]; - - return Row( - children: [ - // The dark/light and LTR/RTL switches. We use the updateDarkMode function - // provided by the parent TextButtonExampleApp to rebuild the MaterialApp - // in the appropriate dark/light ThemeMdoe. The directionality of the rest - // of the UI is controlled by the Directionality widget below, and the - // textDirection local state variable. - TextButtonExampleSwitches( - darkMode: widget.darkMode, - updateDarkMode: widget.updateDarkMode, - textDirection: textDirection, - updateRTL: (bool value) { - setState(() { - textDirection = value ? TextDirection.rtl : TextDirection.ltr; - }); - }, - ), - horizontalSpacer, - - // All of the button examples appear below. They're arranged in two columns. - - Expanded( - child: Scrollbar( - controller: scrollController, - thumbVisibility: true, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: scrollController, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.min, - children: [ - Directionality( - textDirection: textDirection, - child: Column( - children: columnOneButtons, + const SizedBox(height: 30), + TextButton( + style: TextButton.styleFrom( + textStyle: const TextStyle(fontSize: 20), + ), + onPressed: () {}, + child: const Text('Enabled'), + ), + const SizedBox(height: 30), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Stack( + children: [ + Positioned.fill( + child: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [ + Color(0xFF0D47A1), + Color(0xFF1976D2), + Color(0xFF42A5F5), + ], + ), ), ), - horizontalSpacer, - - Directionality( - textDirection: textDirection, - child: Column( - children: columnTwoButtons - ), + ), + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + padding: const EdgeInsets.all(16.0), + textStyle: const TextStyle(fontSize: 20), ), - horizontalSpacer, - ], - ), + onPressed: () {}, + child: const Text('Gradient'), + ), + ], ), ), - ), - ], - ); - } -} - -class TextButtonExampleSwitches extends StatelessWidget { - const TextButtonExampleSwitches({ - super.key, - required this.darkMode, - required this.updateDarkMode, - required this.textDirection, - required this.updateRTL - }); - - final bool darkMode; - final ValueChanged updateDarkMode; - final TextDirection textDirection; - final ValueChanged updateRTL; - - @override - Widget build(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: IntrinsicWidth( - child: Column( - children: [ - Row( - children: [ - const Expanded(child: Text('Dark Mode')), - const SizedBox(width: 4), - Switch( - value: darkMode, - onChanged: updateDarkMode, - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - const Expanded(child: Text('RTL Text')), - const SizedBox(width: 4), - Switch( - value: textDirection == TextDirection.rtl, - onChanged: updateRTL, - ), - ], - ), - ], - ), - ), + ], ), ); } } - -const String grassUrl = 'https://flutter.github.io/assets-for-api-docs/assets/material/text_button_grass.jpeg'; -const String smiley1Url = 'https://flutter.github.io/assets-for-api-docs/assets/material/text_button_smiley1.png'; -const String smiley2Url = 'https://flutter.github.io/assets-for-api-docs/assets/material/text_button_smiley2.png'; -const String smiley3Url = 'https://flutter.github.io/assets-for-api-docs/assets/material/text_button_smiley3.png'; diff --git a/examples/api/test/material/text_button/text_button.0_test.dart b/examples/api/test/material/text_button/text_button.0_test.dart deleted file mode 100644 index e6a13b9dc3..0000000000 --- a/examples/api/test/material/text_button/text_button.0_test.dart +++ /dev/null @@ -1,66 +0,0 @@ -// 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 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_api_samples/material/text_button/text_button.0.dart' as example; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - - // The app being tested loads images via HTTP which the test - // framework defeats by default. - setUpAll(() { - HttpOverrides.global = null; - }); - - testWidgets('TextButtonExample smoke test', (WidgetTester tester) async { - await tester.pumpWidget(const example.TextButtonExampleApp()); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'Enabled')); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'Disabled')); - await tester.pumpAndSettle(); - - // TextButton.icon buttons are _TextButtonWithIcons rather than TextButtons. - // For the purposes of this test, just tapping in the right place is OK. - - await tester.tap(find.text('TextButton.icon #1')); - await tester.pumpAndSettle(); - - await tester.tap(find.text('TextButton.icon #2')); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'TextButton #3')); - await tester.pumpAndSettle(); - - - await tester.tap(find.widgetWithText(TextButton, 'TextButton #4')); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'TextButton #5')); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'TextButton #6')); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'TextButton #7')); - await tester.pumpAndSettle(); - - await tester.tap(find.widgetWithText(TextButton, 'TextButton #8')); - await tester.pumpAndSettle(); - - await tester.tap(find.byType(TextButton).last); // Smiley image button - await tester.pumpAndSettle(); - - await tester.tap(find.byType(Switch).at(0)); // Dark Mode Switch - await tester.pumpAndSettle(); - - await tester.tap(find.byType(Switch).at(1)); // RTL Text Switch - await tester.pumpAndSettle(); - }); -} diff --git a/packages/flutter/lib/src/material/button_style.dart b/packages/flutter/lib/src/material/button_style.dart index d2c7dffca0..701071dff4 100644 --- a/packages/flutter/lib/src/material/button_style.dart +++ b/packages/flutter/lib/src/material/button_style.dart @@ -16,12 +16,6 @@ import 'theme_data.dart'; // late BuildContext context; // typedef MyAppHome = Placeholder; -/// The type for [ButtonStyle.backgroundBuilder] and [ButtonStyle.foregroundBuilder]. -/// -/// The [states] parameter is the button's current pressed/hovered/etc state. The [child] is -/// typically a descendant of the returned widget. -typedef ButtonLayerBuilder = Widget Function(BuildContext context, Set states, Widget? child); - /// The visual properties that most buttons have in common. /// /// Buttons and their themes have a ButtonStyle property which defines the visual @@ -168,8 +162,6 @@ class ButtonStyle with Diagnosticable { this.enableFeedback, this.alignment, this.splashFactory, - this.backgroundBuilder, - this.foregroundBuilder, }); /// The style for a button's [Text] widget descendants. @@ -323,42 +315,6 @@ class ButtonStyle with Diagnosticable { /// ``` final InteractiveInkFeatureFactory? splashFactory; - /// Creates a widget that becomes the child of the button's [Material] - /// and whose child is the rest of the button, including the button's - /// `child` parameter. - /// - /// The widget created by [backgroundBuilder] is constrained to be - /// the same size as the overall button and will appear behind the - /// button's child. The widget created by [foregroundBuilder] is - /// constrained to be the same size as the button's child, i.e. it's - /// inset by [ButtonStyle.padding] and aligned by the button's - /// [ButtonStyle.alignment]. - /// - /// By default the returned widget is clipped to the Material's [ButtonStyle.shape]. - /// - /// See also: - /// - /// * [foregroundBuilder], to create a widget that's as big as the button's - /// child and is layered behind the child. - /// * [ButtonStyleButton.clipBehavior], for more information about - /// configuring clipping. - final ButtonLayerBuilder? backgroundBuilder; - - /// Creates a Widget that contains the button's child parameter which is used - /// instead of the button's child. - /// - /// The returned widget is clipped by the button's - /// [ButtonStyle.shape], inset by the button's [ButtonStyle.padding] - /// and aligned by the button's [ButtonStyle.alignment]. - /// - /// See also: - /// - /// * [backgroundBuilder], to create a widget that's as big as the button and - /// is layered behind the button's child. - /// * [ButtonStyleButton.clipBehavior], for more information about - /// configuring clipping. - final ButtonLayerBuilder? foregroundBuilder; - /// Returns a copy of this ButtonStyle with the given fields replaced with /// the new values. ButtonStyle copyWith({ @@ -384,8 +340,6 @@ class ButtonStyle with Diagnosticable { bool? enableFeedback, AlignmentGeometry? alignment, InteractiveInkFeatureFactory? splashFactory, - ButtonLayerBuilder? backgroundBuilder, - ButtonLayerBuilder? foregroundBuilder, }) { return ButtonStyle( textStyle: textStyle ?? this.textStyle, @@ -410,8 +364,6 @@ class ButtonStyle with Diagnosticable { enableFeedback: enableFeedback ?? this.enableFeedback, alignment: alignment ?? this.alignment, splashFactory: splashFactory ?? this.splashFactory, - backgroundBuilder: backgroundBuilder ?? this.backgroundBuilder, - foregroundBuilder: foregroundBuilder ?? this.foregroundBuilder, ); } @@ -447,8 +399,6 @@ class ButtonStyle with Diagnosticable { enableFeedback: enableFeedback ?? style.enableFeedback, alignment: alignment ?? style.alignment, splashFactory: splashFactory ?? style.splashFactory, - backgroundBuilder: backgroundBuilder ?? style.backgroundBuilder, - foregroundBuilder: foregroundBuilder ?? style.foregroundBuilder, ); } @@ -477,8 +427,6 @@ class ButtonStyle with Diagnosticable { enableFeedback, alignment, splashFactory, - backgroundBuilder, - foregroundBuilder, ]; return Object.hashAll(values); } @@ -513,9 +461,7 @@ class ButtonStyle with Diagnosticable { && other.animationDuration == animationDuration && other.enableFeedback == enableFeedback && other.alignment == alignment - && other.splashFactory == splashFactory - && other.backgroundBuilder == backgroundBuilder - && other.foregroundBuilder == foregroundBuilder; + && other.splashFactory == splashFactory; } @override @@ -542,8 +488,6 @@ class ButtonStyle with Diagnosticable { properties.add(DiagnosticsProperty('animationDuration', animationDuration, defaultValue: null)); properties.add(DiagnosticsProperty('enableFeedback', enableFeedback, defaultValue: null)); properties.add(DiagnosticsProperty('alignment', alignment, defaultValue: null)); - properties.add(DiagnosticsProperty('backgroundBuilder', backgroundBuilder, defaultValue: null)); - properties.add(DiagnosticsProperty('foregroundBuilder', foregroundBuilder, defaultValue: null)); } /// Linearly interpolate between two [ButtonStyle]s. @@ -574,8 +518,6 @@ class ButtonStyle with Diagnosticable { enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback, alignment: AlignmentGeometry.lerp(a?.alignment, b?.alignment, t), splashFactory: t < 0.5 ? a?.splashFactory : b?.splashFactory, - backgroundBuilder: t < 0.5 ? a?.backgroundBuilder : b?.backgroundBuilder, - foregroundBuilder: t < 0.5 ? a?.foregroundBuilder : b?.foregroundBuilder, ); } diff --git a/packages/flutter/lib/src/material/button_style_button.dart b/packages/flutter/lib/src/material/button_style_button.dart index 5b35eb583c..e51d5a1717 100644 --- a/packages/flutter/lib/src/material/button_style_button.dart +++ b/packages/flutter/lib/src/material/button_style_button.dart @@ -89,10 +89,8 @@ abstract class ButtonStyleButton extends StatefulWidget { /// {@macro flutter.material.Material.clipBehavior} /// - /// Defaults to [Clip.none] unless [ButtonStyle.backgroundBuilder] or - /// [ButtonStyle.foregroundBuilder] is specified. In those - /// cases the default is [Clip.antiAlias]. - final Clip? clipBehavior; + /// Defaults to [Clip.none]. + final Clip clipBehavior; /// {@macro flutter.widgets.Focus.focusNode} final FocusNode? focusNode; @@ -320,11 +318,6 @@ class _ButtonStyleState extends State with TickerProviderStat final AlignmentGeometry? resolvedAlignment = effectiveValue((ButtonStyle? style) => style?.alignment); final Offset densityAdjustment = resolvedVisualDensity!.baseSizeAdjustment; final InteractiveInkFeatureFactory? resolvedSplashFactory = effectiveValue((ButtonStyle? style) => style?.splashFactory); - final ButtonLayerBuilder? resolvedBackgroundBuilder = effectiveValue((ButtonStyle? style) => style?.backgroundBuilder); - final ButtonLayerBuilder? resolvedForegroundBuilder = effectiveValue((ButtonStyle? style) => style?.foregroundBuilder); - - final Clip effectiveClipBehavior = widget.clipBehavior - ?? ((resolvedBackgroundBuilder ?? resolvedForegroundBuilder) != null ? Clip.antiAlias : Clip.none); BoxConstraints effectiveConstraints = resolvedVisualDensity.effectiveConstraints( BoxConstraints( @@ -391,21 +384,6 @@ class _ButtonStyleState extends State with TickerProviderStat elevation = resolvedElevation; backgroundColor = resolvedBackgroundColor; - Widget effectiveChild = Padding( - padding: padding, - child: Align( - alignment: resolvedAlignment!, - widthFactor: 1.0, - heightFactor: 1.0, - child: resolvedForegroundBuilder != null - ? resolvedForegroundBuilder(context, statesController.value, widget.child) - : widget.child, - ), - ); - if (resolvedBackgroundBuilder != null) { - effectiveChild = resolvedBackgroundBuilder(context, statesController.value, effectiveChild); - } - final Widget result = ConstrainedBox( constraints: effectiveConstraints, child: Material( @@ -417,7 +395,7 @@ class _ButtonStyleState extends State with TickerProviderStat surfaceTintColor: resolvedSurfaceTintColor, type: resolvedBackgroundColor == null ? MaterialType.transparency : MaterialType.button, animationDuration: resolvedAnimationDuration, - clipBehavior: effectiveClipBehavior, + clipBehavior: widget.clipBehavior, child: InkWell( onTap: widget.onPressed, onLongPress: widget.onLongPress, @@ -435,7 +413,15 @@ class _ButtonStyleState extends State with TickerProviderStat statesController: statesController, child: IconTheme.merge( data: IconThemeData(color: resolvedIconColor ?? resolvedForegroundColor, size: resolvedIconSize), - child: effectiveChild, + child: Padding( + padding: padding, + child: Align( + alignment: resolvedAlignment!, + widthFactor: 1.0, + heightFactor: 1.0, + child: widget.child, + ), + ), ), ), ), diff --git a/packages/flutter/lib/src/material/elevated_button.dart b/packages/flutter/lib/src/material/elevated_button.dart index 802a482ea7..ca0e85baa5 100644 --- a/packages/flutter/lib/src/material/elevated_button.dart +++ b/packages/flutter/lib/src/material/elevated_button.dart @@ -10,7 +10,6 @@ import 'package:flutter/widgets.dart'; import 'button_style.dart'; import 'button_style_button.dart'; import 'color_scheme.dart'; -import 'colors.dart'; import 'constants.dart'; import 'elevated_button_theme.dart'; import 'ink_ripple.dart'; @@ -71,7 +70,7 @@ class ElevatedButton extends ButtonStyleButton { super.style, super.focusNode, super.autofocus = false, - super.clipBehavior, + super.clipBehavior = Clip.none, super.statesController, required super.child, }); @@ -133,26 +132,19 @@ class ElevatedButton extends ButtonStyleButton { /// /// The [foregroundColor] and [disabledForegroundColor] colors are used /// to create a [MaterialStateProperty] [ButtonStyle.foregroundColor], and - /// a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified. - /// - /// If [overlayColor] is specified and its value is [Colors.transparent] - /// then the pressed/focused/hovered highlights are effectively defeated. - /// Otherwise a [MaterialStateProperty] with the same opacities as the - /// default is created. + /// a derived [ButtonStyle.overlayColor]. /// /// The [backgroundColor] and [disabledBackgroundColor] colors are /// used to create a [MaterialStateProperty] [ButtonStyle.backgroundColor]. /// - /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor] - /// parameters are used to construct [ButtonStyle.mouseCursor] and - /// [iconColor], [disabledIconColor] are used to construct - /// [ButtonStyle.iconColor]. - /// /// The button's elevations are defined relative to the [elevation] /// parameter. The disabled elevation is the same as the parameter /// value, [elevation] + 2 is used when the button is hovered /// or focused, and elevation + 6 is used when the button is pressed. /// + /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor] + /// parameters are used to construct [ButtonStyle].mouseCursor. + /// /// All of the other parameters are either used directly or used to /// create a [MaterialStateProperty] with a single value for all /// states. @@ -194,9 +186,6 @@ class ElevatedButton extends ButtonStyleButton { Color? disabledBackgroundColor, Color? shadowColor, Color? surfaceTintColor, - Color? iconColor, - Color? disabledIconColor, - Color? overlayColor, double? elevation, TextStyle? textStyle, EdgeInsetsGeometry? padding, @@ -213,40 +202,32 @@ class ElevatedButton extends ButtonStyleButton { bool? enableFeedback, AlignmentGeometry? alignment, InteractiveInkFeatureFactory? splashFactory, - ButtonLayerBuilder? backgroundBuilder, - ButtonLayerBuilder? foregroundBuilder, }) { - final MaterialStateProperty? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) { - (null, null) => null, - (_, _) => _ElevatedButtonDefaultColor(foregroundColor, disabledForegroundColor), - }; - final MaterialStateProperty? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) { - (null, null) => null, - (_, _) => _ElevatedButtonDefaultColor(backgroundColor, disabledBackgroundColor), - }; - final MaterialStateProperty? iconColorProp = switch ((iconColor, disabledIconColor)) { - (null, null) => null, - (_, _) => _ElevatedButtonDefaultColor(iconColor, disabledIconColor), - }; - final MaterialStateProperty? overlayColorProp = switch ((foregroundColor, overlayColor)) { - (null, null) => null, - (_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll(Colors.transparent), - (_, _) => _ElevatedButtonDefaultOverlay((overlayColor ?? foregroundColor)!), - }; - final MaterialStateProperty? elevationValue = switch (elevation) { - null => null, - _ => _ElevatedButtonDefaultElevation(elevation), - }; + final Color? background = backgroundColor; + final Color? disabledBackground = disabledBackgroundColor; + final MaterialStateProperty? backgroundColorProp = (background == null && disabledBackground == null) + ? null + : _ElevatedButtonDefaultColor(background, disabledBackground); + final Color? foreground = foregroundColor; + final Color? disabledForeground = disabledForegroundColor; + final MaterialStateProperty? foregroundColorProp = (foreground == null && disabledForeground == null) + ? null + : _ElevatedButtonDefaultColor(foreground, disabledForeground); + final MaterialStateProperty? overlayColor = (foreground == null) + ? null + : _ElevatedButtonDefaultOverlay(foreground); + final MaterialStateProperty? elevationValue = (elevation == null) + ? null + : _ElevatedButtonDefaultElevation(elevation); final MaterialStateProperty mouseCursor = _ElevatedButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor); return ButtonStyle( textStyle: MaterialStatePropertyAll(textStyle), backgroundColor: backgroundColorProp, foregroundColor: foregroundColorProp, - overlayColor: overlayColorProp, + overlayColor: overlayColor, shadowColor: ButtonStyleButton.allOrNull(shadowColor), surfaceTintColor: ButtonStyleButton.allOrNull(surfaceTintColor), - iconColor: iconColorProp, elevation: elevationValue, padding: ButtonStyleButton.allOrNull(padding), minimumSize: ButtonStyleButton.allOrNull(minimumSize), @@ -261,8 +242,6 @@ class ElevatedButton extends ButtonStyleButton { enableFeedback: enableFeedback, alignment: alignment, splashFactory: splashFactory, - backgroundBuilder: backgroundBuilder, - foregroundBuilder: foregroundBuilder, ); } @@ -304,7 +283,7 @@ class ElevatedButton extends ButtonStyleButton { /// * others - Theme.colorScheme.onPrimary /// * `overlayColor` /// * hovered - Theme.colorScheme.onPrimary(0.08) - /// * focused or pressed - Theme.colorScheme.onPrimary(0.12) + /// * focused or pressed - Theme.colorScheme.onPrimary(0.24) /// * `shadowColor` - Theme.shadowColor /// * `elevation` /// * disabled - 0 @@ -528,12 +507,13 @@ class _ElevatedButtonWithIcon extends ElevatedButton { super.style, super.focusNode, bool? autofocus, - super.clipBehavior, + Clip? clipBehavior, super.statesController, required Widget icon, required Widget label, }) : super( autofocus: autofocus ?? false, + clipBehavior: clipBehavior ?? Clip.none, child: _ElevatedButtonWithIconChild(icon: icon, label: label, buttonStyle: style), ); diff --git a/packages/flutter/lib/src/material/filled_button.dart b/packages/flutter/lib/src/material/filled_button.dart index d2a9c6cfd4..463322e364 100644 --- a/packages/flutter/lib/src/material/filled_button.dart +++ b/packages/flutter/lib/src/material/filled_button.dart @@ -202,23 +202,19 @@ class FilledButton extends ButtonStyleButton { /// A static convenience method that constructs a filled button /// [ButtonStyle] given simple values. /// - /// The [foregroundColor] and [disabledForegroundColor] colors are used - /// to create a [MaterialStateProperty] [ButtonStyle.foregroundColor], and - /// a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified. - /// - /// If [overlayColor] is specified and its value is [Colors.transparent] - /// then the pressed/focused/hovered highlights are effectively defeated. - /// Otherwise a [MaterialStateProperty] with the same opacities as the - /// default is created. - /// - /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor] - /// parameters are used to construct [ButtonStyle.mouseCursor]. + /// The [foregroundColor], and [disabledForegroundColor] colors are used to create a + /// [MaterialStateProperty] [ButtonStyle.foregroundColor] value. The + /// [backgroundColor] and [disabledBackgroundColor] are used to create a + /// [MaterialStateProperty] [ButtonStyle.backgroundColor] value. /// /// The button's elevations are defined relative to the [elevation] /// parameter. The disabled elevation is the same as the parameter /// value, [elevation] + 2 is used when the button is hovered /// or focused, and elevation + 6 is used when the button is pressed. /// + /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor] + /// parameters are used to construct [ButtonStyle.mouseCursor]. + /// /// All of the other parameters are either used directly or used to /// create a [MaterialStateProperty] with a single value for all /// states. @@ -254,9 +250,6 @@ class FilledButton extends ButtonStyleButton { Color? disabledBackgroundColor, Color? shadowColor, Color? surfaceTintColor, - Color? iconColor, - Color? disabledIconColor, - Color? overlayColor, double? elevation, TextStyle? textStyle, EdgeInsetsGeometry? padding, @@ -273,36 +266,29 @@ class FilledButton extends ButtonStyleButton { bool? enableFeedback, AlignmentGeometry? alignment, InteractiveInkFeatureFactory? splashFactory, - ButtonLayerBuilder? backgroundBuilder, - ButtonLayerBuilder? foregroundBuilder, }) { - final MaterialStateProperty? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) { - (null, null) => null, - (_, _) => _FilledButtonDefaultColor(foregroundColor, disabledForegroundColor), - }; - final MaterialStateProperty? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) { - (null, null) => null, - (_, _) => _FilledButtonDefaultColor(backgroundColor, disabledBackgroundColor), - }; - final MaterialStateProperty? iconColorProp = switch ((iconColor, disabledIconColor)) { - (null, null) => null, - (_, _) => _FilledButtonDefaultColor(iconColor, disabledIconColor), - }; - final MaterialStateProperty? overlayColorProp = switch ((foregroundColor, overlayColor)) { - (null, null) => null, - (_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll(Colors.transparent), - (_, _) => _FilledButtonDefaultOverlay((overlayColor ?? foregroundColor)!), - }; + final MaterialStateProperty? backgroundColorProp = + (backgroundColor == null && disabledBackgroundColor == null) + ? null + : _FilledButtonDefaultColor(backgroundColor, disabledBackgroundColor); + final Color? foreground = foregroundColor; + final Color? disabledForeground = disabledForegroundColor; + final MaterialStateProperty? foregroundColorProp = + (foreground == null && disabledForeground == null) + ? null + : _FilledButtonDefaultColor(foreground, disabledForeground); + final MaterialStateProperty? overlayColor = (foreground == null) + ? null + : _FilledButtonDefaultOverlay(foreground); final MaterialStateProperty mouseCursor = _FilledButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor); return ButtonStyle( textStyle: MaterialStatePropertyAll(textStyle), backgroundColor: backgroundColorProp, foregroundColor: foregroundColorProp, - overlayColor: overlayColorProp, + overlayColor: overlayColor, shadowColor: ButtonStyleButton.allOrNull(shadowColor), surfaceTintColor: ButtonStyleButton.allOrNull(surfaceTintColor), - iconColor: iconColorProp, elevation: ButtonStyleButton.allOrNull(elevation), padding: ButtonStyleButton.allOrNull(padding), minimumSize: ButtonStyleButton.allOrNull(minimumSize), @@ -317,8 +303,6 @@ class FilledButton extends ButtonStyleButton { enableFeedback: enableFeedback, alignment: alignment, splashFactory: splashFactory, - backgroundBuilder: backgroundBuilder, - foregroundBuilder: foregroundBuilder, ); } @@ -533,13 +517,14 @@ class _FilledButtonWithIcon extends FilledButton { super.style, super.focusNode, bool? autofocus, - super.clipBehavior, + Clip? clipBehavior, super.statesController, required Widget icon, required Widget label, }) : super( autofocus: autofocus ?? false, - child: _FilledButtonWithIconChild(icon: icon, label: label, buttonStyle: style) + clipBehavior: clipBehavior ?? Clip.none, + child: _FilledButtonWithIconChild(icon: icon, label: label, buttonStyle: style), ); _FilledButtonWithIcon.tonal({ @@ -551,13 +536,14 @@ class _FilledButtonWithIcon extends FilledButton { super.style, super.focusNode, bool? autofocus, - super.clipBehavior, + Clip? clipBehavior, super.statesController, required Widget icon, required Widget label, }) : super.tonal( autofocus: autofocus ?? false, - child: _FilledButtonWithIconChild(icon: icon, label: label, buttonStyle: style) + clipBehavior: clipBehavior ?? Clip.none, + child: _FilledButtonWithIconChild(icon: icon, label: label, buttonStyle: style), ); @override diff --git a/packages/flutter/lib/src/material/material_state.dart b/packages/flutter/lib/src/material/material_state.dart index c1d23d622c..59d5dfcb71 100644 --- a/packages/flutter/lib/src/material/material_state.dart +++ b/packages/flutter/lib/src/material/material_state.dart @@ -168,9 +168,6 @@ abstract class MaterialStateColor extends Color implements MaterialStateProperty /// specified state. @override Color resolve(Set states); - - /// A constant whose value is [Colors.transparent] for all states. - static const MaterialStateColor transparent = _MaterialStateColorTransparent(); } /// A [MaterialStateColor] created from a [MaterialPropertyResolver] @@ -192,13 +189,6 @@ class _MaterialStateColor extends MaterialStateColor { Color resolve(Set states) => _resolve(states); } -class _MaterialStateColorTransparent extends MaterialStateColor { - const _MaterialStateColorTransparent() : super(0x00000000); - - @override - Color resolve(Set states) => const Color(0x00000000); -} - /// Defines a [MouseCursor] whose value depends on a set of [MaterialState]s which /// represent the interactive state of a component. /// diff --git a/packages/flutter/lib/src/material/outlined_button.dart b/packages/flutter/lib/src/material/outlined_button.dart index abba6a7e05..fbc3fcf01d 100644 --- a/packages/flutter/lib/src/material/outlined_button.dart +++ b/packages/flutter/lib/src/material/outlined_button.dart @@ -75,7 +75,7 @@ class OutlinedButton extends ButtonStyleButton { super.style, super.focusNode, super.autofocus = false, - super.clipBehavior, + super.clipBehavior = Clip.none, super.statesController, required super.child, }); @@ -129,22 +129,16 @@ class OutlinedButton extends ButtonStyleButton { /// A static convenience method that constructs an outlined button /// [ButtonStyle] given simple values. /// + /// /// The [foregroundColor] and [disabledForegroundColor] colors are used /// to create a [MaterialStateProperty] [ButtonStyle.foregroundColor], and - /// a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified. + /// a derived [ButtonStyle.overlayColor]. /// /// The [backgroundColor] and [disabledBackgroundColor] colors are /// used to create a [MaterialStateProperty] [ButtonStyle.backgroundColor]. /// /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor] - /// parameters are used to construct [ButtonStyle.mouseCursor] and - /// [iconColor], [disabledIconColor] are used to construct - /// [ButtonStyle.iconColor]. - /// - /// If [overlayColor] is specified and its value is [Colors.transparent] - /// then the pressed/focused/hovered highlights are effectively defeated. - /// Otherwise a [MaterialStateProperty] with the same opacities as the - /// default is created. + /// parameters are used to construct [ButtonStyle.mouseCursor]. /// /// All of the other parameters are either used directly or used to /// create a [MaterialStateProperty] with a single value for all @@ -175,9 +169,6 @@ class OutlinedButton extends ButtonStyleButton { Color? disabledBackgroundColor, Color? shadowColor, Color? surfaceTintColor, - Color? iconColor, - Color? disabledIconColor, - Color? overlayColor, double? elevation, TextStyle? textStyle, EdgeInsetsGeometry? padding, @@ -194,36 +185,29 @@ class OutlinedButton extends ButtonStyleButton { bool? enableFeedback, AlignmentGeometry? alignment, InteractiveInkFeatureFactory? splashFactory, - ButtonLayerBuilder? backgroundBuilder, - ButtonLayerBuilder? foregroundBuilder, }) { - final MaterialStateProperty? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) { - (null, null) => null, - (_, _) => _OutlinedButtonDefaultColor(foregroundColor, disabledForegroundColor), - }; - final MaterialStateProperty? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) { - (null, null) => null, - (_, _) => _OutlinedButtonDefaultColor(backgroundColor, disabledBackgroundColor), - }; - final MaterialStateProperty? iconColorProp = switch ((iconColor, disabledIconColor)) { - (null, null) => null, - (_, _) => _OutlinedButtonDefaultColor(iconColor, disabledIconColor), - }; - final MaterialStateProperty? overlayColorProp = switch ((foregroundColor, overlayColor)) { - (null, null) => null, - (_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll(Colors.transparent), - (_, _) => _OutlinedButtonDefaultOverlay((overlayColor ?? foregroundColor)!), - }; + final Color? foreground = foregroundColor; + final Color? disabledForeground = disabledForegroundColor; + final MaterialStateProperty? foregroundColorProp = (foreground == null && disabledForeground == null) + ? null + : _OutlinedButtonDefaultColor(foreground, disabledForeground); + final MaterialStateProperty? backgroundColorProp = (backgroundColor == null && disabledBackgroundColor == null) + ? null + : disabledBackgroundColor == null + ? ButtonStyleButton.allOrNull(backgroundColor) + : _OutlinedButtonDefaultColor(backgroundColor, disabledBackgroundColor); + final MaterialStateProperty? overlayColor = (foreground == null) + ? null + : _OutlinedButtonDefaultOverlay(foreground); final MaterialStateProperty mouseCursor = _OutlinedButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor); return ButtonStyle( textStyle: ButtonStyleButton.allOrNull(textStyle), foregroundColor: foregroundColorProp, backgroundColor: backgroundColorProp, - overlayColor: overlayColorProp, + overlayColor: overlayColor, shadowColor: ButtonStyleButton.allOrNull(shadowColor), surfaceTintColor: ButtonStyleButton.allOrNull(surfaceTintColor), - iconColor: iconColorProp, elevation: ButtonStyleButton.allOrNull(elevation), padding: ButtonStyleButton.allOrNull(padding), minimumSize: ButtonStyleButton.allOrNull(minimumSize), @@ -238,8 +222,6 @@ class OutlinedButton extends ButtonStyleButton { enableFeedback: enableFeedback, alignment: alignment, splashFactory: splashFactory, - backgroundBuilder: backgroundBuilder, - foregroundBuilder: foregroundBuilder, ); } @@ -274,7 +256,7 @@ class OutlinedButton extends ButtonStyleButton { /// * disabled - Theme.colorScheme.onSurface(0.38) /// * others - Theme.colorScheme.primary /// * `overlayColor` - /// * hovered - Theme.colorScheme.primary(0.08) + /// * hovered - Theme.colorScheme.primary(0.04) /// * focused or pressed - Theme.colorScheme.primary(0.12) /// * `shadowColor` - Theme.shadowColor /// * `elevation` - 0 @@ -357,7 +339,9 @@ class OutlinedButton extends ButtonStyleButton { padding: _scaledPadding(context), minimumSize: const Size(64, 36), maximumSize: Size.infinite, - side: BorderSide(color: colorScheme.onSurface.withOpacity(0.12)), + 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, @@ -450,12 +434,13 @@ class _OutlinedButtonWithIcon extends OutlinedButton { super.style, super.focusNode, bool? autofocus, - super.clipBehavior, + Clip? clipBehavior, super.statesController, required Widget icon, required Widget label, }) : super( autofocus: autofocus ?? false, + clipBehavior: clipBehavior ?? Clip.none, child: _OutlinedButtonWithIconChild(icon: icon, label: label, buttonStyle: style), ); diff --git a/packages/flutter/lib/src/material/text_button.dart b/packages/flutter/lib/src/material/text_button.dart index 325e1d3312..7efa8c473a 100644 --- a/packages/flutter/lib/src/material/text_button.dart +++ b/packages/flutter/lib/src/material/text_button.dart @@ -50,9 +50,8 @@ import 'theme_data.dart'; /// button will be disabled, it will not react to touch. /// /// {@tool dartpad} -/// This sample shows various ways to configure TextButtons, from the -/// simplest default appearance to versions that don't resemble -/// Material Design at all. +/// This sample shows how to render a disabled TextButton, an enabled TextButton +/// and lastly a TextButton with gradient background. /// /// ** See code in examples/api/lib/material/text_button/text_button.0.dart ** /// {@end-tool} @@ -83,7 +82,7 @@ class TextButton extends ButtonStyleButton { super.style, super.focusNode, super.autofocus = false, - super.clipBehavior, + super.clipBehavior = Clip.none, super.statesController, super.isSemanticButton, required Widget super.child, @@ -143,20 +142,13 @@ class TextButton extends ButtonStyleButton { /// /// The [foregroundColor] and [disabledForegroundColor] colors are used /// to create a [MaterialStateProperty] [ButtonStyle.foregroundColor], and - /// a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified. + /// a derived [ButtonStyle.overlayColor]. /// /// The [backgroundColor] and [disabledBackgroundColor] colors are /// used to create a [MaterialStateProperty] [ButtonStyle.backgroundColor]. /// /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor] - /// parameters are used to construct [ButtonStyle.mouseCursor] and - /// [iconColor], [disabledIconColor] are used to construct - /// [ButtonStyle.iconColor]. - /// - /// If [overlayColor] is specified and its value is [Colors.transparent] - /// then the pressed/focused/hovered highlights are effectively defeated. - /// Otherwise a [MaterialStateProperty] with the same opacities as the - /// default is created. + /// parameters are used to construct [ButtonStyle.mouseCursor]. /// /// All of the other parameters are either used directly or used to /// create a [MaterialStateProperty] with a single value for all @@ -188,7 +180,6 @@ class TextButton extends ButtonStyleButton { Color? surfaceTintColor, Color? iconColor, Color? disabledIconColor, - Color? overlayColor, double? elevation, TextStyle? textStyle, EdgeInsetsGeometry? padding, @@ -205,33 +196,32 @@ class TextButton extends ButtonStyleButton { bool? enableFeedback, AlignmentGeometry? alignment, InteractiveInkFeatureFactory? splashFactory, - ButtonLayerBuilder? backgroundBuilder, - ButtonLayerBuilder? foregroundBuilder, }) { - final MaterialStateProperty? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) { - (null, null) => null, - (_, _) => _TextButtonDefaultColor(foregroundColor, disabledForegroundColor), - }; - final MaterialStateProperty? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) { - (null, null) => null, - (_, _) => _TextButtonDefaultColor(backgroundColor, disabledBackgroundColor), - }; - final MaterialStateProperty? iconColorProp = switch ((iconColor, disabledIconColor)) { - (null, null) => null, - (_, _) => _TextButtonDefaultColor(iconColor, disabledIconColor), - }; - final MaterialStateProperty? overlayColorProp = switch ((foregroundColor, overlayColor)) { - (null, null) => null, - (_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll(Colors.transparent), - (_, _) => _TextButtonDefaultOverlay((overlayColor ?? foregroundColor)!), - }; + final Color? foreground = foregroundColor; + final Color? disabledForeground = disabledForegroundColor; + final MaterialStateProperty? foregroundColorProp = (foreground == null && disabledForeground == null) + ? null + : _TextButtonDefaultColor(foreground, disabledForeground); + final MaterialStateProperty? backgroundColorProp = (backgroundColor == null && disabledBackgroundColor == null) + ? null + : disabledBackgroundColor == null + ? ButtonStyleButton.allOrNull(backgroundColor) + : _TextButtonDefaultColor(backgroundColor, disabledBackgroundColor); + final MaterialStateProperty? overlayColor = (foreground == null) + ? null + : _TextButtonDefaultOverlay(foreground); + final MaterialStateProperty? iconColorProp = (iconColor == null && disabledIconColor == null) + ? null + : disabledIconColor == null + ? ButtonStyleButton.allOrNull(iconColor) + : _TextButtonDefaultIconColor(iconColor, disabledIconColor); final MaterialStateProperty mouseCursor = _TextButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor); return ButtonStyle( textStyle: ButtonStyleButton.allOrNull(textStyle), foregroundColor: foregroundColorProp, backgroundColor: backgroundColorProp, - overlayColor: overlayColorProp, + overlayColor: overlayColor, shadowColor: ButtonStyleButton.allOrNull(shadowColor), surfaceTintColor: ButtonStyleButton.allOrNull(surfaceTintColor), iconColor: iconColorProp, @@ -249,8 +239,6 @@ class TextButton extends ButtonStyleButton { enableFeedback: enableFeedback, alignment: alignment, splashFactory: splashFactory, - backgroundBuilder: backgroundBuilder, - foregroundBuilder: foregroundBuilder, ); } @@ -291,7 +279,7 @@ class TextButton extends ButtonStyleButton { /// * disabled - Theme.colorScheme.onSurface(0.38) /// * others - Theme.colorScheme.primary /// * `overlayColor` - /// * hovered - Theme.colorScheme.primary(0.08) + /// * hovered - Theme.colorScheme.primary(0.04) /// * focused or pressed - Theme.colorScheme.primary(0.12) /// * `shadowColor` - Theme.shadowColor /// * `elevation` - 0 @@ -465,6 +453,27 @@ class _TextButtonDefaultOverlay extends MaterialStateProperty { } } +@immutable +class _TextButtonDefaultIconColor extends MaterialStateProperty { + _TextButtonDefaultIconColor(this.iconColor, this.disabledIconColor); + + final Color? iconColor; + final Color? disabledIconColor; + + @override + Color? resolve(Set states) { + if (states.contains(MaterialState.disabled)) { + return disabledIconColor; + } + return iconColor; + } + + @override + String toString() { + return '{disabled: $disabledIconColor, color: $iconColor}'; + } +} + @immutable class _TextButtonDefaultMouseCursor extends MaterialStateProperty with Diagnosticable { _TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor); @@ -491,12 +500,13 @@ class _TextButtonWithIcon extends TextButton { super.style, super.focusNode, bool? autofocus, - super.clipBehavior, + Clip? clipBehavior, super.statesController, required Widget icon, required Widget label, }) : super( autofocus: autofocus ?? false, + clipBehavior: clipBehavior ?? Clip.none, child: _TextButtonWithIconChild(icon: icon, label: label, buttonStyle: style), ); diff --git a/packages/flutter/test/material/button_style_test.dart b/packages/flutter/test/material/button_style_test.dart index de0c355844..2244c2940a 100644 --- a/packages/flutter/test/material/button_style_test.dart +++ b/packages/flutter/test/material/button_style_test.dart @@ -42,8 +42,6 @@ void main() { expect(style.tapTargetSize, null); expect(style.animationDuration, null); expect(style.enableFeedback, null); - expect(style.backgroundBuilder, null); - expect(style.foregroundBuilder, null); }); testWidgets('Default ButtonStyle debugFillProperties', (WidgetTester tester) async { @@ -109,9 +107,6 @@ void main() { }); testWidgets('ButtonStyle copyWith, merge', (WidgetTester tester) async { - Widget backgroundBuilder(BuildContext context, Set states, Widget? child) => child!; - Widget foregroundBuilder(BuildContext context, Set states, Widget? child) => child!; - const MaterialStateProperty textStyle = MaterialStatePropertyAll(TextStyle(fontSize: 10)); const MaterialStateProperty backgroundColor = MaterialStatePropertyAll(Color(0xfffffff1)); const MaterialStateProperty foregroundColor = MaterialStatePropertyAll(Color(0xfffffff2)); @@ -133,7 +128,7 @@ void main() { const Duration animationDuration = Duration(seconds: 1); const bool enableFeedback = true; - final ButtonStyle style = ButtonStyle( + const ButtonStyle style = ButtonStyle( textStyle: textStyle, backgroundColor: backgroundColor, foregroundColor: foregroundColor, @@ -154,8 +149,6 @@ void main() { tapTargetSize: tapTargetSize, animationDuration: animationDuration, enableFeedback: enableFeedback, - backgroundBuilder: backgroundBuilder, - foregroundBuilder: foregroundBuilder, ); expect( @@ -181,8 +174,6 @@ void main() { tapTargetSize: tapTargetSize, animationDuration: animationDuration, enableFeedback: enableFeedback, - backgroundBuilder: backgroundBuilder, - foregroundBuilder:foregroundBuilder, ), ); diff --git a/packages/flutter/test/material/elevated_button_test.dart b/packages/flutter/test/material/elevated_button_test.dart index e7f1a6e68c..bf5e69e5f3 100644 --- a/packages/flutter/test/material/elevated_button_test.dart +++ b/packages/flutter/test/material/elevated_button_test.dart @@ -1981,203 +1981,6 @@ void main() { expect(controller.value, {MaterialState.disabled}); expect(count, 1); }); - - testWidgets('ElevatedButton backgroundBuilder and foregroundBuilder', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: backgroundColor, - ), - child: child, - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: foregroundColor, - ), - child: child, - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - BoxDecoration boxDecorationOf(Finder finder) { - return tester.widget(finder).decoration as BoxDecoration; - } - - final Finder decorations = find.descendant( - of: find.byType(ElevatedButton), - matching: find.byType(DecoratedBox), - ); - - expect(boxDecorationOf(decorations.at(0)).color, backgroundColor); - expect(boxDecorationOf(decorations.at(1)).color, foregroundColor); - - Text textChildOf(Finder finder) { - return tester.widget( - find.descendant( - of: finder, - matching: find.byType(Text), - ), - ); - } - - expect(textChildOf(decorations.at(0)).data, 'button'); - expect(textChildOf(decorations.at(1)).data, 'button'); - }); - - - testWidgets('ElevatedButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - ), - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder background = find.descendant( - of: find.byType(ElevatedButton), - matching: find.byType(DecoratedBox), - ); - - expect(background, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('ElevatedButton foregroundBuilder drops button child', (WidgetTester tester) async { - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder foreground = find.descendant( - of: find.byType(ElevatedButton), - matching: find.byType(DecoratedBox), - ); - - expect(foreground, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('ElevatedButton foreground and background builders are applied to the correct states', (WidgetTester tester) async { - Set foregroundStates = {}; - Set backgroundStates = {}; - final FocusNode focusNode = FocusNode(); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: ElevatedButton( - style: ButtonStyle( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - backgroundStates = states; - return child!; - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - foregroundStates = states; - return child!; - }, - ), - onPressed: () {}, - focusNode: focusNode, - child: const Text('button'), - ), - ), - ), - ), - ); - - // Default. - expect(backgroundStates.isEmpty, isTrue); - expect(foregroundStates.isEmpty, isTrue); - - const Set focusedStates = {MaterialState.focused}; - const Set focusedHoveredStates = {MaterialState.focused, MaterialState.hovered}; - const Set focusedHoveredPressedStates = {MaterialState.focused, MaterialState.hovered, MaterialState.pressed}; - - bool sameStates(Set expectedValue, Set actualValue) { - return expectedValue.difference(actualValue).isEmpty && actualValue.difference(expectedValue).isEmpty; - } - - // Focused. - focusNode.requestFocus(); - await tester.pumpAndSettle(); - expect(sameStates(focusedStates, backgroundStates), isTrue); - expect(sameStates(focusedStates, foregroundStates), isTrue); - - // Hovered. - final Offset center = tester.getCenter(find.byType(ElevatedButton)); - final TestGesture gesture = await tester.createGesture( - kind: PointerDeviceKind.mouse, - ); - await gesture.addPointer(); - await gesture.moveTo(center); - await tester.pumpAndSettle(); - expect(sameStates(focusedHoveredStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredStates, foregroundStates), isTrue); - - - // Highlighted (pressed). - await gesture.down(center); - await tester.pump(); // Start the splash and highlight animations. - await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way. - expect(sameStates(focusedHoveredPressedStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredPressedStates, foregroundStates), isTrue); - - focusNode.dispose(); - }); } TextStyle _iconStyle(WidgetTester tester, IconData icon) { diff --git a/packages/flutter/test/material/filled_button_test.dart b/packages/flutter/test/material/filled_button_test.dart index 535d8ba52f..a7524fa8fa 100644 --- a/packages/flutter/test/material/filled_button_test.dart +++ b/packages/flutter/test/material/filled_button_test.dart @@ -2092,202 +2092,6 @@ void main() { expect(count, 1); }); - testWidgets('FilledButton backgroundBuilder and foregroundBuilder', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: FilledButton( - style: FilledButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: backgroundColor, - ), - child: child, - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: foregroundColor, - ), - child: child, - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - BoxDecoration boxDecorationOf(Finder finder) { - return tester.widget(finder).decoration as BoxDecoration; - } - - final Finder decorations = find.descendant( - of: find.byType(FilledButton), - matching: find.byType(DecoratedBox), - ); - - expect(boxDecorationOf(decorations.at(0)).color, backgroundColor); - expect(boxDecorationOf(decorations.at(1)).color, foregroundColor); - - Text textChildOf(Finder finder) { - return tester.widget( - find.descendant( - of: finder, - matching: find.byType(Text), - ), - ); - } - - expect(textChildOf(decorations.at(0)).data, 'button'); - expect(textChildOf(decorations.at(1)).data, 'button'); - }); - - - testWidgets('FilledButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: FilledButton( - style: FilledButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - ), - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder background = find.descendant( - of: find.byType(FilledButton), - matching: find.byType(DecoratedBox), - ); - - expect(background, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('FilledButton foregroundBuilder drops button child', (WidgetTester tester) async { - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: FilledButton( - style: FilledButton.styleFrom( - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder foreground = find.descendant( - of: find.byType(FilledButton), - matching: find.byType(DecoratedBox), - ); - - expect(foreground, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('FilledButton foreground and background builders are applied to the correct states', (WidgetTester tester) async { - Set foregroundStates = {}; - Set backgroundStates = {}; - final FocusNode focusNode = FocusNode(); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: FilledButton( - style: ButtonStyle( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - backgroundStates = states; - return child!; - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - foregroundStates = states; - return child!; - }, - ), - onPressed: () {}, - focusNode: focusNode, - child: const Text('button'), - ), - ), - ), - ), - ); - - // Default. - expect(backgroundStates.isEmpty, isTrue); - expect(foregroundStates.isEmpty, isTrue); - - const Set focusedStates = {MaterialState.focused}; - const Set focusedHoveredStates = {MaterialState.focused, MaterialState.hovered}; - const Set focusedHoveredPressedStates = {MaterialState.focused, MaterialState.hovered, MaterialState.pressed}; - - bool sameStates(Set expectedValue, Set actualValue) { - return expectedValue.difference(actualValue).isEmpty && actualValue.difference(expectedValue).isEmpty; - } - - // Focused. - focusNode.requestFocus(); - await tester.pumpAndSettle(); - expect(sameStates(focusedStates, backgroundStates), isTrue); - expect(sameStates(focusedStates, foregroundStates), isTrue); - - // Hovered. - final Offset center = tester.getCenter(find.byType(FilledButton)); - final TestGesture gesture = await tester.createGesture( - kind: PointerDeviceKind.mouse, - ); - await gesture.addPointer(); - await gesture.moveTo(center); - await tester.pumpAndSettle(); - expect(sameStates(focusedHoveredStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredStates, foregroundStates), isTrue); - - - // Highlighted (pressed). - await gesture.down(center); - await tester.pump(); // Start the splash and highlight animations. - await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way. - expect(sameStates(focusedHoveredPressedStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredPressedStates, foregroundStates), isTrue); - - focusNode.dispose(); - }); } TextStyle _iconStyle(WidgetTester tester, IconData icon) { diff --git a/packages/flutter/test/material/outlined_button_test.dart b/packages/flutter/test/material/outlined_button_test.dart index eec8653181..b45ad9ad41 100644 --- a/packages/flutter/test/material/outlined_button_test.dart +++ b/packages/flutter/test/material/outlined_button_test.dart @@ -2131,203 +2131,6 @@ void main() { expect(tester.takeException(), isNull); }); - - testWidgets('OutlinedButton backgroundBuilder and foregroundBuilder', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: backgroundColor, - ), - child: child, - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: foregroundColor, - ), - child: child, - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - BoxDecoration boxDecorationOf(Finder finder) { - return tester.widget(finder).decoration as BoxDecoration; - } - - final Finder decorations = find.descendant( - of: find.byType(OutlinedButton), - matching: find.byType(DecoratedBox), - ); - - expect(boxDecorationOf(decorations.at(0)).color, backgroundColor); - expect(boxDecorationOf(decorations.at(1)).color, foregroundColor); - - Text textChildOf(Finder finder) { - return tester.widget( - find.descendant( - of: finder, - matching: find.byType(Text), - ), - ); - } - - expect(textChildOf(decorations.at(0)).data, 'button'); - expect(textChildOf(decorations.at(1)).data, 'button'); - }); - - - testWidgets('OutlinedButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - ), - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder background = find.descendant( - of: find.byType(OutlinedButton), - matching: find.byType(DecoratedBox), - ); - - expect(background, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('OutlinedButton foregroundBuilder drops button child', (WidgetTester tester) async { - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder foreground = find.descendant( - of: find.byType(OutlinedButton), - matching: find.byType(DecoratedBox), - ); - - expect(foreground, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('OutlinedButton foreground and background builders are applied to the correct states', (WidgetTester tester) async { - Set foregroundStates = {}; - Set backgroundStates = {}; - final FocusNode focusNode = FocusNode(); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: OutlinedButton( - style: ButtonStyle( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - backgroundStates = states; - return child!; - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - foregroundStates = states; - return child!; - }, - ), - onPressed: () {}, - focusNode: focusNode, - child: const Text('button'), - ), - ), - ), - ), - ); - - // Default. - expect(backgroundStates.isEmpty, isTrue); - expect(foregroundStates.isEmpty, isTrue); - - const Set focusedStates = {MaterialState.focused}; - const Set focusedHoveredStates = {MaterialState.focused, MaterialState.hovered}; - const Set focusedHoveredPressedStates = {MaterialState.focused, MaterialState.hovered, MaterialState.pressed}; - - bool sameStates(Set expectedValue, Set actualValue) { - return expectedValue.difference(actualValue).isEmpty && actualValue.difference(expectedValue).isEmpty; - } - - // Focused. - focusNode.requestFocus(); - await tester.pumpAndSettle(); - expect(sameStates(focusedStates, backgroundStates), isTrue); - expect(sameStates(focusedStates, foregroundStates), isTrue); - - // Hovered. - final Offset center = tester.getCenter(find.byType(OutlinedButton)); - final TestGesture gesture = await tester.createGesture( - kind: PointerDeviceKind.mouse, - ); - await gesture.addPointer(); - await gesture.moveTo(center); - await tester.pumpAndSettle(); - expect(sameStates(focusedHoveredStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredStates, foregroundStates), isTrue); - - - // Highlighted (pressed). - await gesture.down(center); - await tester.pump(); // Start the splash and highlight animations. - await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way. - expect(sameStates(focusedHoveredPressedStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredPressedStates, foregroundStates), isTrue); - - focusNode.dispose(); - }); } TextStyle _iconStyle(WidgetTester tester, IconData icon) { diff --git a/packages/flutter/test/material/text_button_test.dart b/packages/flutter/test/material/text_button_test.dart index fc86ae3aef..9747790ed3 100644 --- a/packages/flutter/test/material/text_button_test.dart +++ b/packages/flutter/test/material/text_button_test.dart @@ -1964,203 +1964,6 @@ void main() { expect(tester.takeException(), isNull); }); - - testWidgets('TextButton backgroundBuilder and foregroundBuilder', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TextButton( - style: TextButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: backgroundColor, - ), - child: child, - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return DecoratedBox( - decoration: const BoxDecoration( - color: foregroundColor, - ), - child: child, - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - BoxDecoration boxDecorationOf(Finder finder) { - return tester.widget(finder).decoration as BoxDecoration; - } - - final Finder decorations = find.descendant( - of: find.byType(TextButton), - matching: find.byType(DecoratedBox), - ); - - expect(boxDecorationOf(decorations.at(0)).color, backgroundColor); - expect(boxDecorationOf(decorations.at(1)).color, foregroundColor); - - Text textChildOf(Finder finder) { - return tester.widget( - find.descendant( - of: finder, - matching: find.byType(Text), - ), - ); - } - - expect(textChildOf(decorations.at(0)).data, 'button'); - expect(textChildOf(decorations.at(1)).data, 'button'); - }); - - - testWidgets('TextButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async { - const Color backgroundColor = Color(0xFF000011); - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TextButton( - style: TextButton.styleFrom( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - ), - ); - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder background = find.descendant( - of: find.byType(TextButton), - matching: find.byType(DecoratedBox), - ); - - expect(background, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('TextButton foregroundBuilder drops button child', (WidgetTester tester) async { - const Color foregroundColor = Color(0xFF000022); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TextButton( - style: TextButton.styleFrom( - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - return const DecoratedBox( - decoration: BoxDecoration( - color: foregroundColor, - ), - ); - }, - ), - onPressed: () { }, - child: const Text('button'), - ), - ), - ); - - final Finder foreground = find.descendant( - of: find.byType(TextButton), - matching: find.byType(DecoratedBox), - ); - - expect(foreground, findsOneWidget); - expect(find.text('button'), findsNothing); - }); - - testWidgets('TextButton foreground and background builders are applied to the correct states', (WidgetTester tester) async { - Set foregroundStates = {}; - Set backgroundStates = {}; - final FocusNode focusNode = FocusNode(); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: TextButton( - style: ButtonStyle( - backgroundBuilder: (BuildContext context, Set states, Widget? child) { - backgroundStates = states; - return child!; - }, - foregroundBuilder: (BuildContext context, Set states, Widget? child) { - foregroundStates = states; - return child!; - }, - ), - onPressed: () {}, - focusNode: focusNode, - child: const Text('button'), - ), - ), - ), - ), - ); - - // Default. - expect(backgroundStates.isEmpty, isTrue); - expect(foregroundStates.isEmpty, isTrue); - - const Set focusedStates = {MaterialState.focused}; - const Set focusedHoveredStates = {MaterialState.focused, MaterialState.hovered}; - const Set focusedHoveredPressedStates = {MaterialState.focused, MaterialState.hovered, MaterialState.pressed}; - - bool sameStates(Set expectedValue, Set actualValue) { - return expectedValue.difference(actualValue).isEmpty && actualValue.difference(expectedValue).isEmpty; - } - - // Focused. - focusNode.requestFocus(); - await tester.pumpAndSettle(); - expect(sameStates(focusedStates, backgroundStates), isTrue); - expect(sameStates(focusedStates, foregroundStates), isTrue); - - // Hovered. - final Offset center = tester.getCenter(find.byType(TextButton)); - final TestGesture gesture = await tester.createGesture( - kind: PointerDeviceKind.mouse, - ); - await gesture.addPointer(); - await gesture.moveTo(center); - await tester.pumpAndSettle(); - expect(sameStates(focusedHoveredStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredStates, foregroundStates), isTrue); - - - // Highlighted (pressed). - await gesture.down(center); - await tester.pump(); // Start the splash and highlight animations. - await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way. - expect(sameStates(focusedHoveredPressedStates, backgroundStates), isTrue); - expect(sameStates(focusedHoveredPressedStates, foregroundStates), isTrue); - - focusNode.dispose(); - }); } TextStyle? _iconStyle(WidgetTester tester, IconData icon) { diff --git a/packages/flutter/test/material/text_button_theme_test.dart b/packages/flutter/test/material/text_button_theme_test.dart index c80ee7a82a..473e0a6447 100644 --- a/packages/flutter/test/material/text_button_theme_test.dart +++ b/packages/flutter/test/material/text_button_theme_test.dart @@ -104,15 +104,6 @@ void main() { const bool enableFeedback = false; const AlignmentGeometry alignment = Alignment.centerLeft; - final Key backgroundKey = UniqueKey(); - final Key foregroundKey = UniqueKey(); - Widget backgroundBuilder(BuildContext context, Set states, Widget? child) { - return KeyedSubtree(key: backgroundKey, child: child!); - } - Widget foregroundBuilder(BuildContext context, Set states, Widget? child) { - return KeyedSubtree(key: foregroundKey, child: child!); - } - final ButtonStyle style = TextButton.styleFrom( foregroundColor: foregroundColor, disabledForegroundColor: disabledColor, @@ -131,8 +122,6 @@ void main() { animationDuration: animationDuration, enableFeedback: enableFeedback, alignment: alignment, - backgroundBuilder: backgroundBuilder, - foregroundBuilder: foregroundBuilder, ); Widget buildFrame({ ButtonStyle? buttonStyle, ButtonStyle? themeStyle, ButtonStyle? overallStyle }) { @@ -196,8 +185,6 @@ void main() { expect(tester.getSize(find.byType(TextButton)), const Size(200, 200)); final Align align = tester.firstWidget(find.ancestor(of: find.text('button'), matching: find.byType(Align))); expect(align.alignment, alignment); - expect(find.descendant(of: findMaterial, matching: find.byKey(backgroundKey)), findsOneWidget); - expect(find.descendant(of: findInkWell, matching: find.byKey(foregroundKey)), findsOneWidget); } testWidgets('Button style overrides defaults', (WidgetTester tester) async {