Skip to content

Conversation

@auto-submit
Copy link
Contributor

@auto-submit auto-submit bot commented Feb 1, 2024

Reverts #141818
Initiated by: XilaiZhang
This change reverts the following previous change:
Original Description:
Fixes #139456, #130335, #89563.

Two new properties have been added to ButtonStyle to make it possible to insert arbitrary state-dependent widgets in a button's background or foreground. These properties can be specified for an individual button, using the style parameter, or for all buttons using a button theme's style parameter.

The new ButtonStyle properties are backgroundBuilder and foregroundBuilder and their (function) types are:

typedef ButtonLayerBuilder = Widget Function(
  BuildContext context,
  Set<MaterialState> states,
  Widget? child
);

The new builder functions are called whenever the button is built and the states parameter communicates the pressed/hovered/etc state fo the button.

backgroundBuilder

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. By default the returned widget is clipped to the Material's ButtonStyle.shape.

The backgroundBuilder can be used to add a gradient to the button's background. Here's an example that creates a yellow/orange gradient background:

opaque-gradient-bg

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    backgroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      return DecoratedBox(
        decoration: BoxDecoration(
          gradient: LinearGradient(colors: [Colors.orange, Colors.yellow]),
        ),
        child: child,
      );
    },
  ),
  child: Text('Text Button'),
)

Because the background widget becomes the child of the button's Material, if it's opaque (as it is in this case) then it obscures the overlay highlights which are painted on the button's Material. To ensure that the highlights show through one can decorate the background with an Ink widget. This version also overrides the overlay color to be (shades of) red, because that makes the highlights look a little nicer with the yellow/orange background.

ink-gradient-bg

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    overlayColor: Colors.red,
    backgroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      return Ink(
        decoration: BoxDecoration(
          gradient: LinearGradient(colors: [Colors.orange, Colors.yellow]),
        ),
        child: child,
      );
    },
  ),
  child: Text('Text Button'),
)

Now the button's overlay highlights are painted on the Ink widget. An Ink widget isn't needed if the background is sufficiently translucent. This version of the example creates a translucent backround widget.

translucent-graident-bg

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    overlayColor: Colors.red,
    backgroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      return DecoratedBox(
        decoration: BoxDecoration(
          gradient: LinearGradient(colors: [
            Colors.orange.withOpacity(0.5),
            Colors.yellow.withOpacity(0.5),
          ]),
        ),
        child: child,
      );
    },
  ),
  child: Text('Text Button'),
)

One can also decorate the background with an image. In this example, the button's background is an burlap texture image. The foreground color has been changed to black to make the button's text a little clearer relative to the mottled brown backround.

burlap-bg

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    foregroundColor: Colors.black,
    backgroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      return Ink(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage(burlapUrl),
            fit: BoxFit.cover,
          ),
        ),
        child: child,
      );
    },
  ),
  child: Text('Text Button'),
)

The background widget can depend on the states parameter. In this example the blue/orange gradient flips horizontally when the button is hovered/pressed.

gradient-flip

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    backgroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      final Color color1 = Colors.blue.withOpacity(0.5);
      final Color color2 = Colors.orange.withOpacity(0.5);
      return DecoratedBox(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: switch (states.contains(MaterialState.hovered)) {
              true => <Color>[color1, color2],
              false => <Color>[color2, color1],
            },
          ),
        ),
        child: child,
      );
    },
  ),
  child: Text('Text Button'),
)

The preceeding examples have not included a BoxDecoration border because ButtonStyle already supports ButtonStyle.shape and ButtonStyle.side parameters that can be uesd to define state-dependent borders. Borders defined with the ButtonStyle side parameter match the button's shape. To add a border that changes color when the button is hovered or pressed, one must specify the side property using copyWith, since there's no styleFrom shorthand for this case.

border-gradient-bg

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    foregroundColor: Colors.indigo,
    backgroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      final Color color1 = Colors.blue.withOpacity(0.5);
      final Color color2 = Colors.orange.withOpacity(0.5);
      return DecoratedBox(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: switch (states.contains(MaterialState.hovered)) {
              true => <Color>[color1, color2],
              false => <Color>[color2, color1],
            },
          ),
        ),
        child: child,
      );
    },
  ).copyWith(
    side: MaterialStateProperty.resolveWith<BorderSide?>((Set<MaterialState> states) {
      if (states.contains(MaterialState.hovered)) {
        return BorderSide(width: 3, color: Colors.yellow);
      }
      return null; // defer to the default
    }),
  ),
  child: Text('Text Button'),
)

Although all of the examples have created a ButtonStyle locally and only applied it to one button, they could have configured the ThemeData.textButtonTheme instead and applied the style to all TextButtons. And, of course, all of this works for all of the ButtonStyleButton classes, not just TextButton.

foregroundBuilder

Creates a Widget that contains the button's child parameter. 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].

The foregroundBuilder can be used to wrap the button's child, e.g. with a border or a ShaderMask or as a state-dependent substitute for the child.

This example adds a border that's just applied to the child. The border only appears when the button is hovered/pressed.

border-fg

ElevatedButton(
  onPressed: () {},
  style: ElevatedButton.styleFrom(
    foregroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      final ColorScheme colorScheme = Theme.of(context).colorScheme;
      return DecoratedBox(
        decoration: BoxDecoration(
          border: states.contains(MaterialState.hovered)
            ? Border(bottom: BorderSide(color: colorScheme.primary))
            : Border(), // essentially "no border"
        ),
        child: child,
      );
    },
  ),
  child: Text('Text Button'),
)

The foregroundBuilder can be used with ShaderMask to change the way the button's child is rendered. In this example the ShaderMask's gradient causes the button's child to fade out on top.

shader_mask_fg

ElevatedButton(
  onPressed: () { },
  style: ElevatedButton.styleFrom(
    foregroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      final ColorScheme colorScheme = Theme.of(context).colorScheme;
      return ShaderMask(
        shaderCallback: (Rect bounds) {
          return LinearGradient(
            begin: Alignment.bottomCenter,
            end: Alignment.topCenter,
            colors: <Color>[
              colorScheme.primary,
              colorScheme.primaryContainer,
            ],
          ).createShader(bounds);
        },
        blendMode: BlendMode.srcATop,
        child: child,
      );
    },
  ),
  child:  const Text('Elevated Button'),
)

A commonly requested configuration for butttons has the developer provide images, one for pressed/hovered/normal state. You can use the foregroundBuilder to create a button that fades between a normal image and another image when the button is pressed. In this case the foregroundBuilder doesn't use the child it's passed, even though we've provided the required TextButton child parameter.

image-button

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    foregroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      final String url = states.contains(MaterialState.pressed) ? smiley2Url : smiley1Url;
      return AnimatedContainer(
        width: 100,
        height: 100,
        duration: Duration(milliseconds: 300),
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage(url),
            fit: BoxFit.contain,
          ),
        ),
      );
    },
  ),
  child: Text('No Child'),
)

In this example the button's default overlay appears when the button is hovered and pressed. Another image can be used to indicate the hovered state and the default overlay can be defeated by specifying Colors.transparent for the overlayColor:

image-per-state

TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    overlayColor: Colors.transparent,
    foregroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
      String url = states.contains(MaterialState.hovered) ? smiley3Url : smiley1Url;
      if (states.contains(MaterialState.pressed)) {
        url = smiley2Url;
      }
      return AnimatedContainer(
        width: 100,
        height: 100,
        duration: Duration(milliseconds: 300),
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage(url),
            fit: BoxFit.contain,
          ),
        ),
      );
    },
  ),
  child: Text('No Child'),
)

@auto-submit auto-submit bot added the revert of Bot Only: Tracking label for bot. Tracks new revert of pull requests. label Feb 1, 2024
@github-actions github-actions bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos labels Feb 1, 2024
@auto-submit auto-submit bot merged commit 07ca92a into master Feb 1, 2024
@auto-submit auto-submit bot deleted the revert_ff6c8f5d37d059f928d9996a63b64a5271d422b0 branch February 1, 2024 21:11
@HansMuller
Copy link
Contributor

Reland: #142762

engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 2, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 2, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 2, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 2, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 2, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 2, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 3, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 3, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 4, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 4, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 5, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 5, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 5, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 5, 2024
tarrinneal pushed a commit to flutter/packages that referenced this pull request Feb 5, 2024
Manual roll Flutter from e02e207 to 0b5cd50 (46 revisions)

Manual roll requested by [email protected]

flutter/flutter@e02e207...0b5cd50

2024-02-05 [email protected] fix AppBar docs
for backgroundColor & foregroundColor (flutter/flutter#142430)
2024-02-04 98614782+auto-submit[bot]@users.noreply.github.com Reverts
"Update gradle lockfiles template" (flutter/flutter#142889)
2024-02-04 [email protected] Update gradle lockfiles template
(flutter/flutter#140115)
2024-02-04 [email protected] Roll Flutter Engine from
20742e37e54e to f34c658b9600 (1 revision) (flutter/flutter#142876)
2024-02-03 [email protected] Roll Flutter Engine from
23763db72272 to 20742e37e54e (1 revision) (flutter/flutter#142850)
2024-02-03 [email protected] Roll Flutter Engine from
fee02145da8c to 23763db72272 (3 revisions) (flutter/flutter#142848)
2024-02-03 [email protected] Roll Flutter Engine from
9869d47a2736 to fee02145da8c (2 revisions) (flutter/flutter#142847)
2024-02-03 [email protected] Roll Flutter Engine from
78c63d3c2c68 to 9869d47a2736 (1 revision) (flutter/flutter#142842)
2024-02-02 [email protected] Roll Flutter Engine from
266d5d0b5588 to 78c63d3c2c68 (1 revision) (flutter/flutter#142836)
2024-02-02 49699333+dependabot[bot]@users.noreply.github.com Bump
github/codeql-action from 3.23.2 to 3.24.0 (flutter/flutter#142839)
2024-02-02 49699333+dependabot[bot]@users.noreply.github.com Bump
codecov/codecov-action from 3.1.6 to 4.0.1 (flutter/flutter#142838)
2024-02-02 [email protected] Update TextSelectionOverlay
(flutter/flutter#142463)
2024-02-02 [email protected] Roll Flutter Engine from
e29263212bfd to 266d5d0b5588 (5 revisions) (flutter/flutter#142832)
2024-02-02 [email protected] Fix CupertinoTextSelectionToolbar
clipping (flutter/flutter#138195)
2024-02-02 [email protected] Reland "Add support for Gradle Kotlin DSL
(#140744)" (flutter/flutter#142752)
2024-02-02 [email protected] Support navigation during a Cupertino
back gesture (flutter/flutter#142248)
2024-02-02 [email protected] Avoid depending on files from
build_system/targets other than from top level entrypoints in
flutter_tools. (flutter/flutter#142760)
2024-02-02 [email protected] Roll Packages from
5b48c44 to d37fb0a (14 revisions) (flutter/flutter#142812)
2024-02-02 [email protected] Add a link
the different possible Android virtual device configs
(flutter/flutter#142765)
2024-02-02 [email protected] Allow all iOS
tests to use either iOS 16 or 17 (flutter/flutter#142714)
2024-02-02 [email protected] Roll Flutter Engine from
b35153d00b2e to e29263212bfd (2 revisions) (flutter/flutter#142799)
2024-02-02 [email protected] Roll Flutter Engine from
dd4c79a6c864 to b35153d00b2e (10 revisions) (flutter/flutter#142783)
2024-02-02 [email protected] Wasm/JS Dual Compile with the
flutter tool (flutter/flutter#141396)
2024-02-02 [email protected] Reland: Added
ButtonStyle.foregroundBuilder and ButtonStyle.backgroundBuilder
(flutter/flutter#142762)
2024-02-01 [email protected] Use proto
name for emulator version and show cipd package version
(flutter/flutter#142262)
2024-02-01 [email protected] [github actions] ping actor of workflow
on cherry pick pr creation (flutter/flutter#142676)
2024-02-01 [email protected] Marks Linux_android_emu android
views to be unflaky (flutter/flutter#142590)
2024-02-01 [email protected] Implement `switch` expressions in
`lib/src/material/` (flutter/flutter#142634)
2024-02-01 [email protected] Roll Flutter Engine from
9beb7e82e081 to dd4c79a6c864 (1 revision) (flutter/flutter#142749)
2024-02-01 [email protected] Write Tests for API Example of
`form.0.dart` (flutter/flutter#142635)
2024-02-01 [email protected] Make leak_tracking bots sticked to the
left even if bot thinks they are non-flacky. (flutter/flutter#142744)
2024-02-01 [email protected] Upload
DerivedData logs in CI (flutter/flutter#142643)
2024-02-01 [email protected] Test codesigning xcframeworks in artifacts
(flutter/flutter#142666)
2024-02-01 [email protected] Fix gen_defaults test randomness
(flutter/flutter#142743)
2024-02-01 98614782+auto-submit[bot]@users.noreply.github.com Reverts
"Added ButtonStyle.foregroundBuilder and ButtonStyle.backgroundBuilder"
(flutter/flutter#142748)
2024-02-01 [email protected] Roll Flutter Engine from
39415c3eed42 to 9beb7e82e081 (5 revisions) (flutter/flutter#142745)
2024-02-01 [email protected] Remove unused deprecated autoroll
mirror-remote flag (flutter/flutter#142738)
2024-02-01 [email protected] Fix leaks in tests.
(flutter/flutter#142677)
2024-02-01 [email protected] Roll Flutter Engine from
8c43332c6ffc to 39415c3eed42 (1 revision) (flutter/flutter#142740)
2024-02-01 [email protected] Remove verbose-system-logs on iOS perf
tests (flutter/flutter#142739)
2024-02-01 [email protected] Remove outdated arm64_armv7 check
(flutter/flutter#142737)
2024-02-01 [email protected] fix
CupertinoTabView's Android back button handling with PopScope
(flutter/flutter#141604)
2024-02-01 [email protected] Roll Flutter Engine from
68943afd62d1 to 8c43332c6ffc (8 revisions) (flutter/flutter#142726)
2024-02-01 [email protected] Unpin test
(flutter/flutter#141427)
...
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. revert of Bot Only: Tracking label for bot. Tracks new revert of pull requests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow arbitrary foreground and background elements for ButtonStyle

3 participants