1. 23 Feb, 2024 1 commit
  2. 22 Feb, 2024 1 commit
  3. 21 Feb, 2024 1 commit
    • auto-submit[bot]'s avatar
      Reverts "Changing `TextPainter.getOffsetForCaret` implementation to remove the... · f9b3b84d
      auto-submit[bot] authored
      Reverts "Changing `TextPainter.getOffsetForCaret` implementation to remove the logarithmic search (#143281)" (#143801)
      
      Reverts flutter/flutter#143281
      
      Initiated by: LongCatIsLooong
      
      Reason for reverting: https://github.com/flutter/flutter/issues/143797
      
      Original PR Author: LongCatIsLooong
      
      Reviewed By: {justinmc, jason-simmons}
      
      This change reverts the following previous change:
      Original Description:
      The behavior largely remains the same, except:
      
      1. The EOT cursor `(textLength, downstream)` for text ending in the opposite writing direction as the paragraph is now placed at the visual end of the last line. 
        For example, in a LTR paragraph, the EOT cursor for `aA` (lowercase for LTR and uppercase for RTL) is placed to the right of the line: `aA|` (it was `a|A` before). 
        This matches the behavior of most applications that do logical order arrow key navigation instead of visual order navigation. 
        And it makes the navigation order consistent for `aA\naA`:
      ```
        |aA    =>  aA|  => aA|  => aA  => aA   => aA 
         aA        aA      aA     |aA     aA|     aA|     
         (1)       (2)     (3)    (4)    (5)      (6)
      ```
      This is indeed still pretty confusing as (2) and (3), as well as (5) and (6) are hard to distinguish (when the I beam has a large width they are actually visually distinguishable -- they use the same anchor but one gets painted to the left and the other to the right. I noticed that emacs does the same). 
      But logical order navigation will always be confusing in bidi text, in one way or another.
      
      Interestingly there are 3 different behaviors I've observed in chrome:
      - the chrome download dialog (which I think uses GTK text widgets but not sure which version) gives me 2 cursors when navigating bidi text, and 
      - its HTML fields only show one, and presumably they place the I beam at the **trailing edge** of the character (which makes more sense for backspacing I guess). 
      - On the other hand, its (new) omnibar seems to use visual order arrow navigation
      
      Side note: we may need to update the "tap to place the caret here" logic to handle the case where the tap lands outside of the text and the text ends in the opposite writing direction. 
      
      2. Removed the logarithmic search. The same could be done using the characters package but when glyphInfo tells you about the baseline location in the future we probably don't need the `getBoxesForRange` call. This should fix https://github.com/flutter/flutter/issues/123424.
      
      ## Internal Tests
      
      This is going to change the image output of some internal golden tests. I'm planning to merge https://github.com/flutter/flutter/pull/143281 before this to avoid updating the same golden files twice for invalid selections.
      f9b3b84d
  4. 20 Feb, 2024 2 commits
    • LongCatIsLooong's avatar
      Changing `TextPainter.getOffsetForCaret` implementation to remove the logarithmic search (#143281) · 3538e4c7
      LongCatIsLooong authored
      The behavior largely remains the same, except:
      
      1. The EOT cursor `(textLength, downstream)` for text ending in the opposite writing direction as the paragraph is now placed at the visual end of the last line. 
        For example, in a LTR paragraph, the EOT cursor for `aA` (lowercase for LTR and uppercase for RTL) is placed to the right of the line: `aA|` (it was `a|A` before). 
        This matches the behavior of most applications that do logical order arrow key navigation instead of visual order navigation. 
        And it makes the navigation order consistent for `aA\naA`:
      ```
        |aA    =>  aA|  => aA|  => aA  => aA   => aA 
         aA        aA      aA     |aA     aA|     aA|     
         (1)       (2)     (3)    (4)    (5)      (6)
      ```
      This is indeed still pretty confusing as (2) and (3), as well as (5) and (6) are hard to distinguish (when the I beam has a large width they are actually visually distinguishable -- they use the same anchor but one gets painted to the left and the other to the right. I noticed that emacs does the same). 
      But logical order navigation will always be confusing in bidi text, in one way or another.
      
      Interestingly there are 3 different behaviors I've observed in chrome:
      - the chrome download dialog (which I think uses GTK text widgets but not sure which version) gives me 2 cursors when navigating bidi text, and 
      - its HTML fields only show one, and presumably they place the I beam at the **trailing edge** of the character (which makes more sense for backspacing I guess). 
      - On the other hand, its (new) omnibar seems to use visual order arrow navigation
      
      Side note: we may need to update the "tap to place the caret here" logic to handle the case where the tap lands outside of the text and the text ends in the opposite writing direction. 
      
      2. Removed the logarithmic search. The same could be done using the characters package but when glyphInfo tells you about the baseline location in the future we probably don't need the `getBoxesForRange` call. This should fix https://github.com/flutter/flutter/issues/123424.
      
      ## Internal Tests
      
      This is going to change the image output of some internal golden tests. I'm planning to merge https://github.com/flutter/flutter/pull/143281 before this to avoid updating the same golden files twice for invalid selections.
      3538e4c7
    • Qun Cheng's avatar
      Introduce tone-based surfaces and accent color add-ons - Part 2 (#138521) · a2c7ed95
      Qun Cheng authored
      This PR is to introduce 19 new color roles and deprecate 3 color roles in `ColorScheme`.
      **Tone-based surface colors** (7 colors): 
      * surfaceBright
      * surfaceDim
      * surfaceContainer
      * surfaceContainerLowest
      * surfaceContainerLow
      * surfaceContainerHigh
      * surfaceContainerHighest
      
      **Accent color add-ons** (12 colors):
      * primary/secondary/tertiary-Fixed
      * primary/secondary/tertiary-FixedDim
      * onPrimary/onSecondary/onTertiary-Fixed
      * onPrimary/onSecondary/onTertiary-FixedVariant
      
      **Deprecated colors**:
      * background -> replaced with surface
      * onBackground -> replaced with onSurface
      * surfaceVariant -> replaced with surfaceContainerHighest
      
      Please checkout this [design doc](https://docs.google.com/document/d/1ODqivpM_6c490T4j5XIiWCDKo5YqHy78YEFqDm4S8h4/edit?usp=sharing) for more information:)
      
      ![Screenshot 2024-01-08 at 4 56 51 PM](https://github.com/flutter/flutter/assets/36861262/353cdb4c-6ba9-4435-a518-fd3f67e415f0)
      a2c7ed95
  5. 14 Feb, 2024 1 commit
  6. 13 Feb, 2024 1 commit
    • Taha Tesser's avatar
      Introduce `avatarBoxConstraints` & `deleteIconBoxConstraints` for the chips (#143302) · ccf42dde
      Taha Tesser authored
      fixes [Chip widget's avatar padding changing if label text is more than 1 line](https://github.com/flutter/flutter/issues/136892)
      
      ### Code sample
      
      <details>
      <summary>expand to view the code sample</summary> 
      
      ```dart
      import 'package:flutter/material.dart';
      
      List<String> strings = [
        'hello good morning',
        'hello good morning hello good morning',
        'hello good morning hello good morning hello good morning'
      ];
      
      void main() => runApp(const MyApp());
      
      class MyApp extends StatelessWidget {
        const MyApp({super.key});
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            home: Scaffold(
              body: Center(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        const Text(
                            'avatarBoxConstraints: null \ndeleteIconBoxConstraints: null',
                            textAlign: TextAlign.center),
                        for (String string in strings)
                          Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: RawChip(
                              label: Container(
                                width: 150,
                                color: Colors.amber,
                                child: Text(
                                  string,
                                  maxLines: 3,
                                  overflow: TextOverflow.ellipsis,
                                ),
                              ),
                              avatar: const Icon(Icons.settings),
                              onDeleted: () {},
                            ),
                          ),
                      ],
                    ),
                    Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        const Text(
                            'avatarBoxConstraints: BoxConstraints.tightForFinite() \ndeleteIconBoxConstraints: BoxConstraints.tightForFinite()',
                            textAlign: TextAlign.center),
                        for (String string in strings)
                          Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: RawChip(
                              avatarBoxConstraints:
                                  const BoxConstraints.tightForFinite(),
                              deleteIconBoxConstraints:
                                  const BoxConstraints.tightForFinite(),
                              label: Container(
                                width: 150,
                                color: Colors.amber,
                                child: Text(
                                  string,
                                  maxLines: 3,
                                  overflow: TextOverflow.ellipsis,
                                ),
                              ),
                              avatar: const Icon(Icons.settings),
                              onDeleted: () {},
                            ),
                          ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          );
        }
      }
      
      ```
      
      </details>
      
      ### Preview
      ![Screenshot 2024-02-12 at 14 58 35](https://github.com/flutter/flutter/assets/48603081/5724bd07-7ac7-4987-b992-fa3ab8488273)
      
      # Example previews
      ![Screenshot 2024-02-12 at 22 15 14](https://github.com/flutter/flutter/assets/48603081/33af472d-3561-47d4-8d0d-e1628de1e0aa)
      ![Screenshot 2024-02-12 at 22 15 46](https://github.com/flutter/flutter/assets/48603081/3de78b59-5cb6-4fd8-879b-8e204aacb069)
      ccf42dde
  7. 12 Feb, 2024 1 commit
  8. 07 Feb, 2024 2 commits
  9. 06 Feb, 2024 2 commits
  10. 02 Feb, 2024 1 commit
    • Hans Muller's avatar
      Reland: Added ButtonStyle.foregroundBuilder and ButtonStyle.backgroundBuilder (#142762) · c6f2cea6
      Hans Muller authored
      Reland https://github.com/flutter/flutter/pull/141818 with a fix for a special case: If only `background` is specified for `TextButton.styleFrom` or `OutlinedButton.styleFrom` it applies the button's disabled state, i.e. as if the same value had been specified for disabledBackgroundColor.
      
      The change relative to #141818 is the indicated line below:
      ```dart
      final MaterialStateProperty<Color?>? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) {
        (null, null) => null,
        (_, null) => MaterialStatePropertyAll<Color?>(backgroundColor), // ADDED THIS LINE
        (_, _) => _TextButtonDefaultColor(backgroundColor, disabledBackgroundColor),
      };
        ```
      
      This backwards incompatibility cropped up in an internal test, see internal Google issue b/323399158.
      c6f2cea6
  11. 01 Feb, 2024 3 commits
    • Tirth's avatar
      Write Tests for API Example of `form.0.dart` (#142635) · 10e4f268
      Tirth authored
      Write Tests for API Example of `form.0.dart`.
      
      Part of #130459
      10e4f268
    • auto-submit[bot]'s avatar
      Reverts "Added ButtonStyle.foregroundBuilder and ButtonStyle.backgroundBuilder" (#142748) · 07ca92a6
      auto-submit[bot] authored
      Reverts flutter/flutter#141818
      Initiated by: XilaiZhang
      This change reverts the following previous change:
      Original Description:
      Fixes https://github.com/flutter/flutter/issues/139456, https://github.com/flutter/flutter/issues/130335, https://github.com/flutter/flutter/issues/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:
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/80df8368-e7cf-49ef-aee7-2776a573644c)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/68a49733-f30e-44a1-a948-dc8cc95e1716)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/3b016e1f-200a-4d07-8111-e20d29f18014)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/f2f61ab1-10d9-43a4-bd63-beecdce33b45)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/c6c6fe26-ae47-445b-b82d-4605d9583bd8)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/63cffcd3-0dcf-4eb1-aed5-d14adf1e57f6)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/687a3245-fe68-4983-a04e-5fcc77f8aa21)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/54010f24-e65d-4551-ae58-712135df3d8d)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/f5b1a22f-43ce-4be3-8e70-06de4c958380)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/7ab9da2f-f661-4374-b395-c2e0c7c4cf13)
      
      ```dart
      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'),
      )
      ```
      07ca92a6
    • Hans Muller's avatar
      Added ButtonStyle.foregroundBuilder and ButtonStyle.backgroundBuilder (#141818) · ff6c8f5d
      Hans Muller authored
      Fixes https://github.com/flutter/flutter/issues/139456, https://github.com/flutter/flutter/issues/130335, https://github.com/flutter/flutter/issues/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:
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/80df8368-e7cf-49ef-aee7-2776a573644c)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/68a49733-f30e-44a1-a948-dc8cc95e1716)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/3b016e1f-200a-4d07-8111-e20d29f18014)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/f2f61ab1-10d9-43a4-bd63-beecdce33b45)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/c6c6fe26-ae47-445b-b82d-4605d9583bd8)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/63cffcd3-0dcf-4eb1-aed5-d14adf1e57f6)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/687a3245-fe68-4983-a04e-5fcc77f8aa21)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/54010f24-e65d-4551-ae58-712135df3d8d)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/f5b1a22f-43ce-4be3-8e70-06de4c958380)
      
      ```dart
      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](https://github.com/flutter/flutter/assets/1377460/7ab9da2f-f661-4374-b395-c2e0c7c4cf13)
      
      ```dart
      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'),
      )
      ```
      ff6c8f5d
  12. 24 Jan, 2024 1 commit
  13. 22 Jan, 2024 1 commit
  14. 18 Jan, 2024 1 commit
  15. 12 Jan, 2024 5 commits
  16. 09 Jan, 2024 1 commit
  17. 03 Jan, 2024 1 commit
    • Furkan Acar's avatar
      Add `SegmentedButton.styleFrom` (#137542) · 83ac7605
      Furkan Acar authored
      fixes https://github.com/flutter/flutter/issues/138289
      
      ---
      
      SegmentedButtom.styleFrom has been added to the segment button, so there is no longer any need to the button style from the beginning. It works like ElevatedButton.styleFrom only I added selectedForegroundColor, selectedBackgroundColor. In this way, the user will be able to change the color first without checking the MaterialState states. I added tests of the same controls.
      
      #129215 I opened this problem myself, but I was rejected because I handled too many items in a PR. For now, I wrote a structure that only handles MaterialStates instead of users.
      
      old (still avaliable)
      <img width="626" alt="image" src="https://github.com/flutter/flutter/assets/65075121/9446b13b-c355-4d20-bda2-c47a23d42d4f">
      
      new (just an option for developer)
      <img width="483" alt="image" src="https://github.com/flutter/flutter/assets/65075121/0a645257-4c83-4029-9484-bd746c02265f">
      
      ### Code sample
      
      <details>
      <summary>expand to view the code sample</summary> 
      
      ```dart
      import 'package:flutter/material.dart';
      
      /// Flutter code sample for [SegmentedButton].
      
      void main() {
        runApp(const SegmentedButtonApp());
      }
      
      enum Calendar { day, week, month, year }
      
      class SegmentedButtonApp extends StatefulWidget {
        const SegmentedButtonApp({super.key});
      
        @override
        State<SegmentedButtonApp> createState() => _SegmentedButtonAppState();
      }
      
      class _SegmentedButtonAppState extends State<SegmentedButtonApp> {
        Calendar calendarView = Calendar.day;
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            theme: ThemeData(useMaterial3: true),
            home: Scaffold(
              body: Center(
                child: SegmentedButton<Calendar>(
                  style: SegmentedButton.styleFrom(
                    foregroundColor: Colors.amber,
                    visualDensity: VisualDensity.comfortable,
                  ),
                  // style: const ButtonStyle(
                  //   foregroundColor: MaterialStatePropertyAll<Color>(Colors.deepPurple),
                  //   visualDensity: VisualDensity.comfortable,
                  // ),
                  segments: const <ButtonSegment<Calendar>>[
                    ButtonSegment<Calendar>(
                        value: Calendar.day,
                        label: Text('Day'),
                        icon: Icon(Icons.calendar_view_day)),
                    ButtonSegment<Calendar>(
                        value: Calendar.week,
                        label: Text('Week'),
                        icon: Icon(Icons.calendar_view_week)),
                    ButtonSegment<Calendar>(
                        value: Calendar.month,
                        label: Text('Month'),
                        icon: Icon(Icons.calendar_view_month)),
                    ButtonSegment<Calendar>(
                        value: Calendar.year,
                        label: Text('Year'),
                        icon: Icon(Icons.calendar_today)),
                  ],
                  selected: <Calendar>{calendarView},
                  onSelectionChanged: (Set<Calendar> newSelection) {
                    setState(() {
                      calendarView = newSelection.first;
                    });
                  },
                ),
              ),
            ),
          );
        }
      }
      
      ```
      
      </details>
      83ac7605
  18. 11 Dec, 2023 1 commit
    • Tirth's avatar
      Renamed `appbar` to `app_bar` directory in API Examples Tests (#139922) · 6a61e878
      Tirth authored
      Tests for `app_bar.0`, `app_bar.1`, `app_bar.2`, `app_bar.3`, `sliver_app_bar.1` and `sliver_app_bar.4` were already present. But directory name was `appbar` rather than `app_bar`. I've renamed the directory to `app_bar` since example files uses that only.
      
      Part of #130459
      6a61e878
  19. 08 Dec, 2023 1 commit
  20. 07 Dec, 2023 1 commit
    • Jasper van Riet's avatar
      Introduce `exitDuration` to Tooltip for mouse pointer devices (#138321) · 3c422dd7
      Jasper van Riet authored
      This PR introduces a new property `exitDuration` to Tooltip, the counterpart to `waitDuration`. The need for this is shown by #136586. This changes the behaviour of `showDuration` on mouse pointer devices. This is because the use cases for the current behaviour on touch screen devices vs mouse pointer devices is fundamentally different.
      
      <details>
      <summary>Demo: tooltip with showDuration set</summary>
      
      Tooltip disappears after 100 ms when moving away the mouse. Tooltip will not disappear when hovered.
      
      https://github.com/flutter/flutter/assets/5138348/81d36dc9-78e0-4723-a84b-2552843ee181
      
      </details>
      
      Currently, when `showDuration` is set, this adjusts the time it takes for the tooltip to hide _after_ a mouse pointer has left the tooltip. This is not the same use case as its effect on touch screen devices, where it dictates how long the tooltip stays on screen after a long press. That is needed because the tooltip takes up screen space and there is not an intuitive way to hide it, whereas when using a mouse users expect to simply have to hover somewhere else. Having the tooltip stay around will look broken.
      
      Thus, this PR splits the two use cases. `showDuration` no longer affects mouse pointer devices at all*. There is a precedent for such mouse pointer-only behaviour in `waitDuration`. Instead, I have split up the two use cases and created the new property `exitDuration`, which will still allow for tweaking the time it takes for the tooltip to hide after the user has moved their mouse pointer somewhere else.
      
      *Note: Should `showDuration` affect [this line](https://github.com/flutter/flutter/blob/e33d4b86270e3c012ba13d68d6e90f2eabc4912b/packages/flutter/lib/src/material/tooltip.dart#L610)?
      
      Fixes #136586.
      
      Note: I noticed that when I made the change, no tests were broken. Hopefully, the tests added here help that in the future. I also noticed that in the _existing_ tests, the `waitDuration` tests contain assertions that implicate that it is the role of `waitDuration` to change this behaviour, but that's not currently (nor in the new behaviour) true, so I have fixed those tests.
      3c422dd7
  21. 06 Dec, 2023 1 commit
    • Taha Tesser's avatar
      Add `AnimationStyle` to `ExpansionTile` (#139664) · f794cf9d
      Taha Tesser authored
      fixes [Expose animation parameters for the [ExpansionTile] widget](https://github.com/flutter/flutter/issues/138047)
      
      ### Description
      Add `AnimationStyle` to the `ExpansionTile` widget to override the default expand and close animation.
      
      Syntax:
      ```dart
              child: ExpansionTile(
                title: const Text('Tap to expand'),
                expansionAnimationStyle: AnimationStyle(
                  duration: Durations.extralong1,
                  curve: Easing.emphasizedAccelerate,
                ),
                children: const <Widget>[FlutterLogo(size: 200)],
              ),
      ```
      
      ### Code sample
      
      <details>
      <summary>expand to view the code sample</summary> 
      
      ```dart
      // Copyright 2014 The Flutter Authors. All rights reserved.
      // Use of this source code is governed by a BSD-style license that can be
      // found in the LICENSE file.
      
      import 'package:flutter/material.dart';
      
      /// Flutter code sample for [ExpansionTile] and [AnimationStyle].
      
      void main() {
        runApp(const ExpansionTileAnimationStyleApp());
      }
      
      enum AnimationStyles { defaultStyle, custom, none }
      const List<(AnimationStyles, String)> animationStyleSegments = <(AnimationStyles, String)>[
        (AnimationStyles.defaultStyle, 'Default'),
        (AnimationStyles.custom, 'Custom'),
        (AnimationStyles.none, 'None'),
      ];
      
      class ExpansionTileAnimationStyleApp extends StatefulWidget {
        const ExpansionTileAnimationStyleApp({super.key});
      
        @override
        State<ExpansionTileAnimationStyleApp> createState() => _ExpansionTileAnimationStyleAppState();
      }
      
      class _ExpansionTileAnimationStyleAppState extends State<ExpansionTileAnimationStyleApp> {
        Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
        AnimationStyle? _animationStyle;
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            home: Scaffold(
              body: SafeArea(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    SegmentedButton<AnimationStyles>(
                      selected: _animationStyleSelection,
                      onSelectionChanged: (Set<AnimationStyles> styles) {
                        setState(() {
                          _animationStyleSelection = styles;
                          switch (styles.first) {
                            case AnimationStyles.defaultStyle:
                              _animationStyle = null;
                            case AnimationStyles.custom:
                              _animationStyle = AnimationStyle(
                                curve: Easing.emphasizedAccelerate,
                                duration: Durations.extralong1,
                              );
                            case AnimationStyles.none:
                              _animationStyle = AnimationStyle.noAnimation;
                          }
                        });
                      },
                      segments: animationStyleSegments
                        .map<ButtonSegment<AnimationStyles>>(((AnimationStyles, String) shirt) {
                          return ButtonSegment<AnimationStyles>(value: shirt.$1, label: Text(shirt.$2));
                        })
                        .toList(),
                    ),
                    const SizedBox(height: 20),
                    ExpansionTile(
                      expansionAnimationStyle: _animationStyle,
                      title: const Text('ExpansionTile'),
                      children: const <Widget>[
                        ListTile(title: Text('Expanded Item 1')),
                        ListTile(title: Text('Expanded Item 2')),
                      ],
                    )
                  ],
                ),
              ),
            ),
          );
        }
      }
      ```
      
      </details>
      
      Related to https://github.com/flutter/flutter/pull/138721.
      f794cf9d
  22. 30 Nov, 2023 1 commit
    • Tirth's avatar
      Write Tests for API Examples of `cupertino_text_field.0`, `data_table.0`,... · c10787bc
      Tirth authored
      Write Tests for API Examples of `cupertino_text_field.0`, `data_table.0`, `icon_button.2` & `ink_well.0` (#139258)
      
      Write Tests for API Examples of `cupertino_text_field.0`, `data_table.0`, `icon_button.2` & `ink_well.0`
      
      Note: test for `cupertino_text_field.0` was already there but it was named `cupertino_text_field.0.dart`. I renamed it to `cupertino_text_field.0_test.dart`.
      
      Part of #130459
      c10787bc
  23. 28 Nov, 2023 1 commit
    • Tirth's avatar
      Write Tests for API Examples of `snack_bar.0`, `elevated_button.0`,... · a49ee534
      Tirth authored
      Write Tests for API Examples of `snack_bar.0`, `elevated_button.0`, `stepper.0`, `radio.0`, `filled_button.0`, `outlined_button.0` & `card.0` (#138987)
      
      Write Tests for API Examples of `snack_bar.0`, `elevated_button.0`, `stepper.0`, `radio.0`, `filled_button.0`, `outlined_button.0` & `card.0`
      
      Part of #130459
      a49ee534
  24. 27 Nov, 2023 1 commit
  25. 20 Nov, 2023 1 commit
  26. 17 Nov, 2023 1 commit
    • auto-submit[bot]'s avatar
      Reverts "Introduce `AnimationStyle`" (#138628) · 0135a331
      auto-submit[bot] authored
      Reverts flutter/flutter#137945
      Initiated by: HansMuller
      This change reverts the following previous change:
      Original Description:
      This PR introduces `AnimationStyle`, it is used to override default animation curves and durations in several widgets.
      
      fixes  [Add the ability to customize MaterialApp theme animation duration](https://github.com/flutter/flutter/issues/78372)
      fixes [Allow customization of showMenu transition animation curves and duration](https://github.com/flutter/flutter/issues/135638)
      
      Here is an example where popup menu curve and transition duration is overriden:
      
      ```dart
                popUpAnimationStyle: AnimationStyle(
                  curve: Easing.emphasizedAccelerate,
                  duration: Durations.medium4,
                ),
      ```
      
      Set `AnimationStyle.noAnimation` to disable animation.
      ```dart
          return MaterialApp(
            themeAnimationStyle: AnimationStyle.noAnimation,
      ```
      0135a331
  27. 16 Nov, 2023 1 commit
    • Taha Tesser's avatar
      Introduce `AnimationStyle` (#137945) · 19e284f8
      Taha Tesser authored
      This PR introduces `AnimationStyle`, it is used to override default animation curves and durations in several widgets.
      
      fixes  [Add the ability to customize MaterialApp theme animation duration](https://github.com/flutter/flutter/issues/78372)
      fixes [Allow customization of showMenu transition animation curves and duration](https://github.com/flutter/flutter/issues/135638)
      
      Here is an example where popup menu curve and transition duration is overriden:
      
      ```dart
                popUpAnimationStyle: AnimationStyle(
                  curve: Easing.emphasizedAccelerate,
                  duration: Durations.medium4,
                ),
      ```
      
      Set `AnimationStyle.noAnimation` to disable animation.
      ```dart
          return MaterialApp(
            themeAnimationStyle: AnimationStyle.noAnimation,
      ```
      19e284f8
  28. 13 Nov, 2023 1 commit
    • huycozy's avatar
      Update DraggableScrollableSheet docs to reflect API change (#136471) · 61c007a3
      huycozy authored
      ### Description
      
      This PR intends to update `DraggableScrollableSheet` docs for Web and Desktop platforms. On these platforms, the vertical dragging gesture does not provide natural behavior similar to other desktop applications. 
      
      By adding a note before the sample code so users are aware that the sample code will not work as expected on Desktop and Web. Also, refer to the instructions if they still want to implement it on these platforms.
      
      ### Related issue
      
      Fixes https://github.com/flutter/flutter/issues/111372
      61c007a3
  29. 07 Nov, 2023 2 commits
    • Hans Muller's avatar
      Added an AnimationController API doc example (#137975) · 84bfe640
      Hans Muller authored
      This example shows how to use `AnimationController` and
      `SlideTransition` to create an animated digit like you might find on a
      digital clock. New digit values slide into place from below, as the old
      value slides upwards and out of view. Taps that occur while the
      controller is already animating cause the controller's
      `AnimationController.duration` to be reduced so that the visuals don't
      fall behind.
      
      You can try the example here:
      https://dartpad.dev/?id=9553c20fe0fdb0c5447c1293e02400eb
      84bfe640
    • Qun Cheng's avatar
      Adaptive `Switch` (#130425) · ed70f4e2
      Qun Cheng authored
      Currently, `Switch.factory` delegates to `CupertinoSwitch` when platform
      is iOS or macOS. This PR is to:
      * have the factory configure the Material `Switch` for the expected look
      and feel.
      * introduce `Adaptation` class to customize themes for the adaptive
      components.
      ed70f4e2
  30. 01 Nov, 2023 1 commit
    • Qun Cheng's avatar
      Add `Card.filled` and `Card.outlined` factory methods (#136229) · 4a0f261b
      Qun Cheng authored
      Fixes #119401
      
      This PR is to:
      * add `Card.filled` and `Card.outlined` factory methods so that we can use tokens for these two types of cards to generate default theme instead of providing hard-corded values in example.
      * update card.2.dart example.
      * add test file for card.2.dart example.
      * fix some mismatch caused by editing the auto-generated defaults by hand in navigation_bar.dart and navigation_drawer.dart.
      4a0f261b