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.
On the beta branch:
```
Verifying the code signature of /Users/m/Projects/flutter/bin/cache/artifacts/engine/ios-profile/extension_safe/Flutter.xcframework
Verifying the code signature of /Users/m/Projects/flutter/bin/cache/artifacts/engine/ios-profile/Flutter.xcframework
Verifying the code signature of /Users/m/Projects/flutter/bin/cache/artifacts/engine/ios/extension_safe/Flutter.xcframework
Verifying the code signature of /Users/m/Projects/flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework
Verifying the code signature of /Users/m/Projects/flutter/bin/cache/artifacts/engine/ios-release/extension_safe/Flutter.xcframework
Verifying the code signature of /Users/m/Projects/flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework
```
Fixes https://github.com/flutter/flutter/issues/140934
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:

```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.

```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.

```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.

```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.

```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.

```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.

```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.

```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.

```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`:

```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'),
)
```
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:

```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.

```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.

```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.

```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.

```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.

```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.

```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.

```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.

```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`:

```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'),
)
```
- Unskip `text_style_test` for CanvasKit.
- Remove no longer necessary `kIsWeb` checks in a few tests.
This PR depends on https://github.com/flutter/engine/pull/49786, which rolled into the framework. If the engine PR needs to be reverted, this PR will need to be reverted too.
I continued [my mission](https://github.com/flutter/flutter/pull/141431) to find as many typos as I could. This time it's a smaller set than before.
There is no need for issues since it's a typo fix.
The migration of customer tests to sharded tests adds a step that checks out the current tip-of-tree of the framework repo, removing local changes. This does not work with monorepo testing, which modifies engine.version, and does not work with local testing of a branch.
The sharded tests should already be running with the correct checkout of the framework repo. If the REVISION environment variable is set, the framework checkout will still be reset to check out that revision.
These commands were migrated from the existing shell script to the sharded tester in
https://github.com/flutter/flutter/pull/138659
Bug: https://github.com/dart-lang/sdk/issues/51042
Fix and unskip the following CanvasKit tests:
- `test/painting/decoration_test.dart`
- `test/rendering/layers_test.dart`
- `test/widgets/app_overrides_test.dart`
Each error section is numbered, so you can all be sure you're talking about the same one.
A message is printed at the very end telling you how to find the error blocks in the verbose logs.
1. Move leak_tracker and leak_tracker_testing out of direct dependencies.
2. Move leak_tracker_flutter_testing from dev to prod dependencies for flutter_test
It is prerequisite for https://github.com/flutter/flutter/issues/135856
Pinning the package:web dependency constrains downstream packages from
using newer versions and making sure they support the version pinned in
Flutter. Since the usage of package:web in Flutter is light, we should
instead have a small shim like the engine and keep package:web as a dev
dependency only.
I did not include the `'// flutter_ignore_for_file: stopwatch (see analyze.dart)'` directive since it's currently not used, and adding that shouldn't be too difficult.
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
Reverts flutter/flutter#132985
Initiated by: christopherfujino
This change reverts the following previous change:
Original Description:
Provides support for conditional bundling of assets through the existing `--flavor` option for `flutter build` and `flutter run`. Closes https://github.com/flutter/flutter/issues/21682. Resolves https://github.com/flutter/flutter/issues/136092
## Change
Within the `assets` section pubspec.yaml, the user can now specify one or more `flavors` that an asset belongs to. Consider this example:
```yaml
# pubspec.yaml
flutter:
assets:
- assets/normal-asset.png
- path: assets/vanilla/ice-cream.png
flavors:
- vanilla
- path: assets/strawberry/ice-cream.png
flavors:
- strawberry
```
With this pubspec,
* `flutter run --flavor vanilla` will not include `assets/strawberry/ice-cream.png` in the build output.
* `flutter run --flavor strawberry` will not include `assets/vanilla/ice-cream.png`.
* `flutter run` will only include `assets/normal-asset.png`.
## Open questions
* Should this be supported for all platforms, or should this change be limited to ones with documented `--flavor` support (Android, iOS, and (implicitly) MacOS)? This PR currently only enables this feature for officially supported platforms.
## Design thoughts, what this PR does not do, etc.
### This does not provide an automatic mapping/resolution of asset keys/paths to others based on flavor at runtime.
The implementation in this PR represents a simplest approach. Notably, it does not give Flutter the ability to dynamically choose an asset based on flavor using a single asset key. For example, one can't use `Image.asset('config.json')` to dynamically choose between different "flavors" of `config.json` (such as `dev-flavor/config.json` or `prod-flavor/config.json`). However, a user could always implement such a mechanism in their project or in a library by examining the flavor at runtime.
### When multiple entries affect the same file and 1) at least one of these entries have a `flavors` list provided and 2) these lists are not equivalent, we always consider the manifest to be ambiguous and will throw a `ToolExit`.
<details>
For example, these manifests would all be considered ambiguous:
```yaml
assets:
- assets/
- path: assets/vanilla.png
flavors:
- vanilla
assets:
- path: assets/vanilla/
flavors:
- vanilla
- path: assets/vanilla/cherry.png
flavor:
- cherry
# Thinking towards the future where we might add glob/regex support and more conditions other than flavor:
assets:
- path: assets/vanilla/**
flavors:
- vanilla
- path: assets/**/ios/**
platforms:
- ios
# Ambiguous in the case of assets like "assets/vanilla/ios/icon.svg" since we
# don't know if flavor `vanilla` and platform `ios` should be combined using or-logic or and-logic.
```
See [this review comment thread](https://github.com/flutter/flutter/pull/132985#discussion_r1381909942) for the full story on how I arrived at this decision.
</details>
### This does not support Android's multidimensional flavors feature (in an intuitive way)
<details>
Conder this excerpt from a Flutter project's android/app/build.gradle file:
```groovy
android {
// ...
flavorDimensions "mode", "api"
productFlavors {
free {
dimension "mode"
applicationIdSuffix ".free"
}
premium {
dimension "mode"
applicationIdSuffix ".premium"
}
minApi23 {
dimension "api"
versionNameSuffix "-minApi23"
}
minApi21 {
dimension "api"
versionNameSuffix "-minApi21"
}
}
}
```
In this setup, the following values are valid `--flavor` are valid `freeMinApi21`, `freeMinApi23`, `premiumMinApi21`, and `premiumMinApi23`. We call these values "flavor combinations". Consider the following from the Android documentation[^1]:
> In addition to the source set directories you can create for each individual product flavor and build variant, you can also create source set directories for each combination of product flavors. For example, you can create and add Java sources to the src/demoMinApi24/java/ directory, and Gradle uses those sources only when building a variant that combines those two product flavors.
>
> Source sets you create for product flavor combinations have a higher priority than source sets that belong to each individual product flavor. To learn more about source sets and how Gradle merges resources, read the section about how to [create source sets](https://developer.android.com/build/build-variants#sourcesets).
This feature will not behave in this way. If a user utilizes this feature and also Android's multidimensional flavors feature, they will have to list out all flavor combinations that contain the flavor they want to limit an asset to:
```yaml
assets:
- assets/free/
flavors:
- freeMinApi21
- freeMinApi23
```
This is mostly due to a technical limitation in the hot-reload feature of `flutter run`. During a hot reload, the tool will try to update the asset bundle on the device, but the tool does not know the flavors contained within the flavor combination (that the user passes to `--flavor`). Gradle is the source of truth of what flavors were involved in the build, and `flutter run` currently does not access to that information since it's an implementation detail of the build process. We could bubble up this information, but it would require a nontrivial amount of engineering work, and it's unclear how desired this functionality is. It might not be worth implementing.
</details>
See https://flutter.dev/go/flavor-specific-assets for the (outdated) design document.
<summary>Pre-launch Checklist</summary>
</details>
[^1]: https://developer.android.com/build/build-variants#flavor-dimensions
Provides support for conditional bundling of assets through the existing `--flavor` option for `flutter build` and `flutter run`. Closes https://github.com/flutter/flutter/issues/21682. Resolves https://github.com/flutter/flutter/issues/136092
## Change
Within the `assets` section pubspec.yaml, the user can now specify one or more `flavors` that an asset belongs to. Consider this example:
```yaml
# pubspec.yaml
flutter:
assets:
- assets/normal-asset.png
- path: assets/vanilla/ice-cream.png
flavors:
- vanilla
- path: assets/strawberry/ice-cream.png
flavors:
- strawberry
```
With this pubspec,
* `flutter run --flavor vanilla` will not include `assets/strawberry/ice-cream.png` in the build output.
* `flutter run --flavor strawberry` will not include `assets/vanilla/ice-cream.png`.
* `flutter run` will only include `assets/normal-asset.png`.
## Open questions
* Should this be supported for all platforms, or should this change be limited to ones with documented `--flavor` support (Android, iOS, and (implicitly) MacOS)? This PR currently only enables this feature for officially supported platforms.
## Design thoughts, what this PR does not do, etc.
### This does not provide an automatic mapping/resolution of asset keys/paths to others based on flavor at runtime.
The implementation in this PR represents a simplest approach. Notably, it does not give Flutter the ability to dynamically choose an asset based on flavor using a single asset key. For example, one can't use `Image.asset('config.json')` to dynamically choose between different "flavors" of `config.json` (such as `dev-flavor/config.json` or `prod-flavor/config.json`). However, a user could always implement such a mechanism in their project or in a library by examining the flavor at runtime.
### When multiple entries affect the same file and 1) at least one of these entries have a `flavors` list provided and 2) these lists are not equivalent, we always consider the manifest to be ambiguous and will throw a `ToolExit`.
<details>
For example, these manifests would all be considered ambiguous:
```yaml
assets:
- assets/
- path: assets/vanilla.png
flavors:
- vanilla
assets:
- path: assets/vanilla/
flavors:
- vanilla
- path: assets/vanilla/cherry.png
flavor:
- cherry
# Thinking towards the future where we might add glob/regex support and more conditions other than flavor:
assets:
- path: assets/vanilla/**
flavors:
- vanilla
- path: assets/**/ios/**
platforms:
- ios
# Ambiguous in the case of assets like "assets/vanilla/ios/icon.svg" since we
# don't know if flavor `vanilla` and platform `ios` should be combined using or-logic or and-logic.
```
See [this review comment thread](https://github.com/flutter/flutter/pull/132985#discussion_r1381909942) for the full story on how I arrived at this decision.
</details>
### This does not support Android's multidimensional flavors feature (in an intuitive way)
<details>
Conder this excerpt from a Flutter project's android/app/build.gradle file:
```groovy
android {
// ...
flavorDimensions "mode", "api"
productFlavors {
free {
dimension "mode"
applicationIdSuffix ".free"
}
premium {
dimension "mode"
applicationIdSuffix ".premium"
}
minApi23 {
dimension "api"
versionNameSuffix "-minApi23"
}
minApi21 {
dimension "api"
versionNameSuffix "-minApi21"
}
}
}
```
In this setup, the following values are valid `--flavor` are valid `freeMinApi21`, `freeMinApi23`, `premiumMinApi21`, and `premiumMinApi23`. We call these values "flavor combinations". Consider the following from the Android documentation[^1]:
> In addition to the source set directories you can create for each individual product flavor and build variant, you can also create source set directories for each combination of product flavors. For example, you can create and add Java sources to the src/demoMinApi24/java/ directory, and Gradle uses those sources only when building a variant that combines those two product flavors.
>
> Source sets you create for product flavor combinations have a higher priority than source sets that belong to each individual product flavor. To learn more about source sets and how Gradle merges resources, read the section about how to [create source sets](https://developer.android.com/build/build-variants#sourcesets).
This feature will not behave in this way. If a user utilizes this feature and also Android's multidimensional flavors feature, they will have to list out all flavor combinations that contain the flavor they want to limit an asset to:
```yaml
assets:
- assets/free/
flavors:
- freeMinApi21
- freeMinApi23
```
This is mostly due to a technical limitation in the hot-reload feature of `flutter run`. During a hot reload, the tool will try to update the asset bundle on the device, but the tool does not know the flavors contained within the flavor combination (that the user passes to `--flavor`). Gradle is the source of truth of what flavors were involved in the build, and `flutter run` currently does not access to that information since it's an implementation detail of the build process. We could bubble up this information, but it would require a nontrivial amount of engineering work, and it's unclear how desired this functionality is. It might not be worth implementing.
</details>
See https://flutter.dev/go/flavor-specific-assets for the (outdated) design document.
<summary>Pre-launch Checklist</summary>
</details>
[^1]: https://developer.android.com/build/build-variants#flavor-dimensions
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
I plan to extend the prepare_package.dart script to upload the flutter preview device ([design doc](https://docs.google.com/document/d/1AzI-_Uk2v1LA2kKKFJ7gVD4xcakXJ6yVZiS5Ek6RHtg/edit#heading=h.byp03plw7mg9)).
However, given that that script is one large >1k line file, I decided to organize it into smaller libraries in this PR. There should be no behavioral change in this PR, this is a cleanup only. I made the following changes:
1. Created a //dev/bots/prepare_package/ directory to contain helper libraries
2. Moved everything but the `main()` function in //dev/bots/prepare_package.dart into one of 4 helper libraries under the new directory from step 1:
a. archive_creator.dart which contains the code that creates archive directory locally on disk
b. archive_publisher.dart which contains the code that uploads the archive to cloud storage
c. common.dart for shared constants and definitions
d. process_runner.dart for an abstraction over running sub-processes
3. Changed all definitions to `File` and `Directory` from `dart:io` to use the testable versions from `package:file`. This allowed me to use the `MemoryFileSystem` in the unit tests, rather than creating real temp file system directories.
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
I previously made a PR (#136140) that used `switch` expressions to make some parts of the Flutter codebase easier to understand. It was assigned to the framework team, and @christopherfujino let me know that it was too large to effectively review and recommended breaking it up into smaller pull requests.
Here's a PR that only targets files in the `dev/` directory. Hopefully this will be easier to work with!
(solves issue https://github.com/flutter/flutter/issues/136139)