1
0
mirror of https://github.com/flutter/samples.git synced 2025-11-08 13:58:47 +00:00

Dart 3.9 / Flutter 3.35 [first LLM release] (#2714)

I got carried away with Gemini and basically rewrote CI and the release
process for the new LLM reality. This work was largely completed by
Gemini.

- Bump all SDK versions to the current beta (3.9.0-0)
- Run `flutter channel beta`
- Wrote `ci_script.dart` to replace the bash scripts
- Converted repository to pub workspace #2499 
- Added llm.md and release.md
- Added redirect for deprecated Samples Index

## Pre-launch Checklist

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I have added sample code updates to the [changelog].
- [x] I updated/added relevant documentation (doc comments with `///`).
This commit is contained in:
Eric Windmill
2025-08-14 12:26:24 -07:00
committed by GitHub
parent 0aa5415d5e
commit 2999d738b8
410 changed files with 28166 additions and 27661 deletions

View File

@@ -75,20 +75,19 @@ class _AppState extends State<App> {
theme: ThemeData(
colorSchemeSeed:
_colorSelectionMethod == ColorSelectionMethod.colorSeed
? _colorSelected.color
: null,
colorScheme:
_colorSelectionMethod == ColorSelectionMethod.image
? _imageColorScheme
: null,
? _colorSelected.color
: null,
colorScheme: _colorSelectionMethod == ColorSelectionMethod.image
? _imageColorScheme
: null,
useMaterial3: _useMaterial3,
brightness: Brightness.light,
),
darkTheme: ThemeData(
colorSchemeSeed:
_colorSelectionMethod == ColorSelectionMethod.colorSeed
? _colorSelected.color
: _imageColorScheme!.primary,
? _colorSelected.color
: _imageColorScheme!.primary,
useMaterial3: _useMaterial3,
brightness: Brightness.dark,
),

View File

@@ -8,7 +8,11 @@ class SizeAnimation extends CurvedAnimation {
SizeAnimation(Animation<double> parent)
: super(
parent: parent,
curve: const Interval(0.2, 0.8, curve: Curves.easeInOutCubicEmphasized),
curve: const Interval(
0.2,
0.8,
curve: Curves.easeInOutCubicEmphasized,
),
reverseCurve: Interval(
0,
0.2,
@@ -21,7 +25,11 @@ class OffsetAnimation extends CurvedAnimation {
OffsetAnimation(Animation<double> parent)
: super(
parent: parent,
curve: const Interval(0.4, 1.0, curve: Curves.easeInOutCubicEmphasized),
curve: const Interval(
0.4,
1.0,
curve: Curves.easeInOutCubicEmphasized,
),
reverseCurve: Interval(
0,
0.2,

View File

@@ -23,10 +23,9 @@ class BrightnessButton extends StatelessWidget {
preferBelow: showTooltipBelow,
message: 'Toggle brightness',
child: IconButton(
icon:
isBright
? const Icon(Icons.dark_mode_outlined)
: const Icon(Icons.light_mode_outlined),
icon: isBright
? const Icon(Icons.dark_mode_outlined)
: const Icon(Icons.light_mode_outlined),
onPressed: () => handleBrightnessChange(!isBright),
),
);
@@ -50,10 +49,9 @@ class Material3Button extends StatelessWidget {
preferBelow: showTooltipBelow,
message: 'Switch to Material ${useMaterial3 ? 2 : 3}',
child: IconButton(
icon:
useMaterial3
? const Icon(Icons.filter_2)
: const Icon(Icons.filter_3),
icon: useMaterial3
? const Icon(Icons.filter_2)
: const Icon(Icons.filter_3),
onPressed: handleMaterialVersionChange,
),
);
@@ -77,7 +75,9 @@ class ColorSeedButton extends StatelessWidget {
return PopupMenuButton(
icon: const Icon(Icons.palette_outlined),
tooltip: 'Select a seed color',
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
itemBuilder: (context) {
return List.generate(ColorSeed.values.length, (index) {
ColorSeed currentColor = ColorSeed.values[index];
@@ -130,7 +130,9 @@ class ColorImageButton extends StatelessWidget {
return PopupMenuButton(
icon: const Icon(Icons.image_outlined),
tooltip: 'Select a color extraction image',
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
itemBuilder: (context) {
return List.generate(ColorImageProvider.values.length, (index) {
final currentImageProvider = ColorImageProvider.values[index];

View File

@@ -77,7 +77,9 @@ class _ColorBoxState extends State<ColorBox> {
await Clipboard.setData(data);
messenger.hideCurrentSnackBar();
messenger.showSnackBar(
SnackBar(content: Text('Copied $hex to clipboard')),
SnackBar(
content: Text('Copied $hex to clipboard'),
),
);
},
),

View File

@@ -61,16 +61,15 @@ class ColorPalettesScreen extends StatelessWidget {
TextSpan(
text: 'dynamic_color',
style: const TextStyle(decoration: TextDecoration.underline),
recognizer:
TapGestureRecognizer()
..onTap = () async {
final url = Uri.parse(
'https://pub.dev/packages/dynamic_color',
);
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = Uri.parse(
'https://pub.dev/packages/dynamic_color',
);
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
),
const TextSpan(text: ' package.'),
],
@@ -454,7 +453,9 @@ class ColorChip extends StatelessWidget {
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(child: Text(label, style: TextStyle(color: labelColor))),
Expanded(
child: Text(label, style: TextStyle(color: labelColor)),
),
],
),
),

View File

@@ -49,10 +49,9 @@ class FirstComponentList extends StatelessWidget {
child: CustomScrollView(
slivers: [
SliverPadding(
padding:
showSecondList
? const EdgeInsetsDirectional.only(end: smallSpacing)
: EdgeInsets.zero,
padding: showSecondList
? const EdgeInsetsDirectional.only(end: smallSpacing)
: EdgeInsets.zero,
sliver: SliverList(
delegate: BuildSlivers(
heights: heights,
@@ -123,7 +122,11 @@ class SecondComponentList extends StatelessWidget {
// as the contents of the list are exposed for the first time, and
// then remain fixed.
class _CacheHeight extends SingleChildRenderObjectWidget {
const _CacheHeight({super.child, required this.heights, required this.index});
const _CacheHeight({
super.child,
required this.heights,
required this.index,
});
final List<double?> heights;
final int index;
@@ -537,7 +540,9 @@ class Cards extends StatelessWidget {
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(color: Theme.of(context).colorScheme.outline),
side: BorderSide(
color: Theme.of(context).colorScheme.outline,
),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Container(
@@ -626,7 +631,9 @@ class _TextFieldsState extends State<TextFields> {
controller: _controllerFilled,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
suffixIcon: _ClearButton(controller: _controllerFilled),
suffixIcon: _ClearButton(
controller: _controllerFilled,
),
labelText: 'Filled',
hintText: 'hint text',
helperText: 'supporting text',
@@ -645,7 +652,9 @@ class _TextFieldsState extends State<TextFields> {
enabled: false,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
suffixIcon: _ClearButton(controller: _controllerFilled),
suffixIcon: _ClearButton(
controller: _controllerFilled,
),
labelText: 'Disabled',
hintText: 'hint text',
helperText: 'supporting text',
@@ -737,51 +746,49 @@ class _DialogsState extends State<Dialogs> {
void openDialog(BuildContext context) {
showDialog<void>(
context: context,
builder:
(context) => AlertDialog(
title: const Text('What is a dialog?'),
content: const Text(
'A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made.',
),
actions: <Widget>[
TextButton(
child: const Text('Dismiss'),
onPressed: () => Navigator.of(context).pop(),
),
FilledButton(
child: const Text('Okay'),
onPressed: () => Navigator.of(context).pop(),
),
],
builder: (context) => AlertDialog(
title: const Text('What is a dialog?'),
content: const Text(
'A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made.',
),
actions: <Widget>[
TextButton(
child: const Text('Dismiss'),
onPressed: () => Navigator.of(context).pop(),
),
FilledButton(
child: const Text('Okay'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
}
void openFullscreenDialog(BuildContext context) {
showDialog<void>(
context: context,
builder:
(context) => Dialog.fullscreen(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Scaffold(
appBar: AppBar(
title: const Text('Full-screen dialog'),
centerTitle: false,
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
actions: [
TextButton(
child: const Text('Close'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
builder: (context) => Dialog.fullscreen(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Scaffold(
appBar: AppBar(
title: const Text('Full-screen dialog'),
centerTitle: false,
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
actions: [
TextButton(
child: const Text('Close'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
),
),
);
}
@@ -874,26 +881,24 @@ class _SwitchRowState extends State<SwitchRow> {
// TODO: use SwitchListTile when thumbIcon is available https://github.com/flutter/flutter/issues/118616
Switch(
value: value0,
onChanged:
widget.isEnabled
? (value) {
setState(() {
value0 = value;
});
}
: null,
onChanged: widget.isEnabled
? (value) {
setState(() {
value0 = value;
});
}
: null,
),
Switch(
thumbIcon: thumbIcon,
value: value1,
onChanged:
widget.isEnabled
? (value) {
setState(() {
value1 = value;
});
}
: null,
onChanged: widget.isEnabled
? (value) {
setState(() {
value1 = value;
});
}
: null,
),
],
);
@@ -1132,7 +1137,10 @@ List<Widget> barWithBadgeDestinations = [
),
NavigationDestination(
tooltip: '',
icon: Badge.count(count: 3, child: const Icon(Icons.videocam_outlined)),
icon: Badge.count(
count: 3,
child: const Icon(Icons.videocam_outlined),
),
label: 'Meet',
selectedIcon: Badge.count(count: 3, child: const Icon(Icons.videocam)),
),
@@ -1186,12 +1194,11 @@ class _NavigationBarsState extends State<NavigationBars> {
});
if (!widget.isExampleBar) widget.onSelectItem!(index);
},
destinations:
widget.isExampleBar && widget.isBadgeExample
? barWithBadgeDestinations
: widget.isExampleBar
? exampleBarDestinations
: appBarDestinations,
destinations: widget.isExampleBar && widget.isBadgeExample
? barWithBadgeDestinations
: widget.isExampleBar
? exampleBarDestinations
: appBarDestinations,
),
);
@@ -1369,7 +1376,10 @@ class _ChipsState extends State<Chips> {
onPressed: () {},
onDeleted: () {},
),
ActionChip(label: const Text('Suggestion'), onPressed: () {}),
ActionChip(
label: const Text('Suggestion'),
onPressed: () {},
),
],
),
colDivider,
@@ -1645,9 +1655,18 @@ class _BottomSheetSectionState extends State<BottomSheetSection> {
IconButton(onPressed: () {}, icon: const Icon(Icons.share_outlined)),
IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
IconButton(onPressed: () {}, icon: const Icon(Icons.delete_outline)),
IconButton(onPressed: () {}, icon: const Icon(Icons.archive_outlined)),
IconButton(onPressed: () {}, icon: const Icon(Icons.settings_outlined)),
IconButton(onPressed: () {}, icon: const Icon(Icons.favorite_border)),
IconButton(
onPressed: () {},
icon: const Icon(Icons.archive_outlined),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.settings_outlined),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.favorite_border),
),
];
List<Text> labelList = const <Text>[
Text('Share'),
@@ -1690,7 +1709,9 @@ class _BottomSheetSectionState extends State<BottomSheetSection> {
return SizedBox(
height: 150,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
padding: const EdgeInsets.symmetric(
horizontal: 32.0,
),
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
@@ -1731,7 +1752,9 @@ class _BottomSheetSectionState extends State<BottomSheetSection> {
return SizedBox(
height: 150,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
padding: const EdgeInsets.symmetric(
horizontal: 32.0,
),
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
@@ -1817,9 +1840,18 @@ class IconButtonAnchorExample extends StatelessWidget {
MenuItemButton(child: const Text('Menu 2'), onPressed: () {}),
SubmenuButton(
menuChildren: <Widget>[
MenuItemButton(onPressed: () {}, child: const Text('Menu 3.1')),
MenuItemButton(onPressed: () {}, child: const Text('Menu 3.2')),
MenuItemButton(onPressed: () {}, child: const Text('Menu 3.3')),
MenuItemButton(
onPressed: () {},
child: const Text('Menu 3.1'),
),
MenuItemButton(
onPressed: () {},
child: const Text('Menu 3.2'),
),
MenuItemButton(
onPressed: () {},
child: const Text('Menu 3.3'),
),
],
child: const Text('Menu 3'),
),
@@ -1920,7 +1952,10 @@ class _NavigationDrawerSectionState extends State<NavigationDrawerSection> {
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(28, 16, 16, 10),
child: Text('Mail', style: Theme.of(context).textTheme.titleSmall),
child: Text(
'Mail',
style: Theme.of(context).textTheme.titleSmall,
),
),
...destinations.map((destination) {
return NavigationDrawerDestination(
@@ -1932,7 +1967,10 @@ class _NavigationDrawerSectionState extends State<NavigationDrawerSection> {
const Divider(indent: 28, endIndent: 28),
Padding(
padding: const EdgeInsets.fromLTRB(28, 16, 16, 10),
child: Text('Labels', style: Theme.of(context).textTheme.titleSmall),
child: Text(
'Labels',
style: Theme.of(context).textTheme.titleSmall,
),
),
...labelDestinations.map((destination) {
return NavigationDrawerDestination(
@@ -1955,14 +1993,26 @@ class ExampleDestination {
}
const List<ExampleDestination> destinations = <ExampleDestination>[
ExampleDestination('Inbox', Icon(Icons.inbox_outlined), Icon(Icons.inbox)),
ExampleDestination('Outbox', Icon(Icons.send_outlined), Icon(Icons.send)),
ExampleDestination(
'Inbox',
Icon(Icons.inbox_outlined),
Icon(Icons.inbox),
),
ExampleDestination(
'Outbox',
Icon(Icons.send_outlined),
Icon(Icons.send),
),
ExampleDestination(
'Favorites',
Icon(Icons.favorite_outline),
Icon(Icons.favorite),
),
ExampleDestination('Trash', Icon(Icons.delete_outline), Icon(Icons.delete)),
ExampleDestination(
'Trash',
Icon(Icons.delete_outline),
Icon(Icons.delete),
),
];
const List<ExampleDestination> labelDestinations = <ExampleDestination>[
@@ -1976,7 +2026,11 @@ const List<ExampleDestination> labelDestinations = <ExampleDestination>[
Icon(Icons.bookmark_border),
Icon(Icons.bookmark),
),
ExampleDestination('Work', Icon(Icons.bookmark_border), Icon(Icons.bookmark)),
ExampleDestination(
'Work',
Icon(Icons.bookmark_border),
Icon(Icons.bookmark),
),
];
class NavigationRails extends StatelessWidget {
@@ -2217,7 +2271,9 @@ class _MenusState extends State<Menus> {
label: const Text('Color'),
enableFilter: true,
dropdownMenuEntries: colorEntries,
inputDecorationTheme: const InputDecorationTheme(filled: true),
inputDecorationTheme: const InputDecorationTheme(
filled: true,
),
onSelected: (color) {
setState(() {
selectedColor = color;
@@ -2441,7 +2497,9 @@ class Carousels extends StatelessWidget {
child: CarouselView(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Theme.of(context).colorScheme.outline),
side: BorderSide(
color: Theme.of(context).colorScheme.outline,
),
),
shrinkExtent: 100,
itemExtent: 180,
@@ -2461,7 +2519,9 @@ class Carousels extends StatelessWidget {
itemSnapping: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Theme.of(context).colorScheme.outline),
side: BorderSide(
color: Theme.of(context).colorScheme.outline,
),
),
shrinkExtent: 100,
itemExtent: 180,
@@ -2536,9 +2596,13 @@ class _ComponentDecorationState extends State<ComponentDecoration> {
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).colorScheme.outlineVariant,
color: Theme.of(
context,
).colorScheme.outlineVariant,
),
borderRadius: const BorderRadius.all(
Radius.circular(12),
),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Padding(
padding: const EdgeInsets.symmetric(

View File

@@ -65,7 +65,11 @@ class ElevationScreen extends StatelessWidget {
const double narrowScreenWidthThreshold = 450;
class ElevationGrid extends StatelessWidget {
const ElevationGrid({super.key, this.shadowColor, this.surfaceTintColor});
const ElevationGrid({
super.key,
this.shadowColor,
this.surfaceTintColor,
});
final Color? shadowColor;
final Color? surfaceTintColor;
@@ -135,7 +139,9 @@ class _ElevationCardState extends State<ElevationCard> {
@override
Widget build(BuildContext context) {
const BorderRadius borderRadius = BorderRadius.all(Radius.circular(4.0));
const BorderRadius borderRadius = BorderRadius.all(
Radius.circular(4.0),
);
final Color color = Theme.of(context).colorScheme.surface;
return Padding(

View File

@@ -32,9 +32,9 @@ class ExpandedImageColorAction extends StatelessWidget {
index: i,
select:
imageSelected == ColorImageProvider.values[i] &&
colorSelectionMethod == ColorSelectionMethod.image
? null
: () => handleImageSelect(i),
colorSelectionMethod == ColorSelectionMethod.image
? null
: () => handleImageSelect(i),
),
),
),
@@ -66,7 +66,9 @@ class _ImageButton extends StatelessWidget {
child: ClipRRect(
borderRadius: BorderRadius.circular(4.0),
child: Image(
image: NetworkImage(ColorImageProvider.values[index].url),
image: NetworkImage(
ColorImageProvider.values[index].url,
),
),
),
),

View File

@@ -133,32 +133,30 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
PreferredSizeWidget _createAppBar() {
return AppBar(
title:
widget.useMaterial3
? const Text('Material 3')
: const Text('Material 2'),
actions:
!showMediumSizeLayout && !showLargeSizeLayout
? [
BrightnessButton(
handleBrightnessChange: widget.handleBrightnessChange,
),
Material3Button(
handleMaterialVersionChange:
widget.handleMaterialVersionChange,
),
ColorSeedButton(
handleColorSelect: widget.handleColorSelect,
colorSelected: widget.colorSelected,
colorSelectionMethod: widget.colorSelectionMethod,
),
ColorImageButton(
handleImageSelect: widget.handleImageSelect,
imageSelected: widget.imageSelected,
colorSelectionMethod: widget.colorSelectionMethod,
),
]
: [Container()],
title: widget.useMaterial3
? const Text('Material 3')
: const Text('Material 2'),
actions: !showMediumSizeLayout && !showLargeSizeLayout
? [
BrightnessButton(
handleBrightnessChange: widget.handleBrightnessChange,
),
Material3Button(
handleMaterialVersionChange:
widget.handleMaterialVersionChange,
),
ColorSeedButton(
handleColorSelect: widget.handleColorSelect,
colorSelected: widget.colorSelected,
colorSelectionMethod: widget.colorSelectionMethod,
),
ColorImageButton(
handleImageSelect: widget.handleImageSelect,
imageSelected: widget.imageSelected,
colorSelectionMethod: widget.colorSelectionMethod,
),
]
: [Container()],
);
}
@@ -221,21 +219,20 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
trailing: Expanded(
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child:
showLargeSizeLayout
? ExpandedTrailingActions(
useLightMode: widget.useLightMode,
handleBrightnessChange: widget.handleBrightnessChange,
useMaterial3: widget.useMaterial3,
handleMaterialVersionChange:
widget.handleMaterialVersionChange,
handleImageSelect: widget.handleImageSelect,
handleColorSelect: widget.handleColorSelect,
colorSelectionMethod: widget.colorSelectionMethod,
imageSelected: widget.imageSelected,
colorSelected: widget.colorSelected,
)
: _trailingActions(),
child: showLargeSizeLayout
? ExpandedTrailingActions(
useLightMode: widget.useLightMode,
handleBrightnessChange: widget.handleBrightnessChange,
useMaterial3: widget.useMaterial3,
handleMaterialVersionChange:
widget.handleMaterialVersionChange,
handleImageSelect: widget.handleImageSelect,
handleColorSelect: widget.handleColorSelect,
colorSelectionMethod: widget.colorSelectionMethod,
imageSelected: widget.imageSelected,
colorSelected: widget.colorSelected,
)
: _trailingActions(),
),
),
),
@@ -258,7 +255,10 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
final List<NavigationRailDestination> _navRailDestinations = appBarDestinations
.map(
(destination) => NavigationRailDestination(
icon: Tooltip(message: destination.label, child: destination.icon),
icon: Tooltip(
message: destination.label,
child: destination.icon,
),
selectedIcon: Tooltip(
message: destination.label,
child: destination.selectedIcon,

View File

@@ -46,10 +46,9 @@ class _SchemePreviewState extends State<SchemePreview> {
color: scheme.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color:
theme.brightness == widget.brightness
? colors.outlineVariant
: Colors.transparent,
color: theme.brightness == widget.brightness
? colors.outlineVariant
: Colors.transparent,
),
),
padding: const EdgeInsets.only(top: 16, left: 16, right: 16),

View File

@@ -9,9 +9,12 @@ class TypographyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(
context,
).textTheme.apply(displayColor: Theme.of(context).colorScheme.onSurface);
final textTheme =
Theme.of(
context,
).textTheme.apply(
displayColor: Theme.of(context).colorScheme.onSurface,
);
return Expanded(
child: ListView(
children: <Widget>[
@@ -40,15 +43,42 @@ class TypographyScreen extends StatelessWidget {
name: 'Headline Small',
style: textTheme.headlineSmall!,
),
TextStyleExample(name: 'Title Large', style: textTheme.titleLarge!),
TextStyleExample(name: 'Title Medium', style: textTheme.titleMedium!),
TextStyleExample(name: 'Title Small', style: textTheme.titleSmall!),
TextStyleExample(name: 'Label Large', style: textTheme.labelLarge!),
TextStyleExample(name: 'Label Medium', style: textTheme.labelMedium!),
TextStyleExample(name: 'Label Small', style: textTheme.labelSmall!),
TextStyleExample(name: 'Body Large', style: textTheme.bodyLarge!),
TextStyleExample(name: 'Body Medium', style: textTheme.bodyMedium!),
TextStyleExample(name: 'Body Small', style: textTheme.bodySmall!),
TextStyleExample(
name: 'Title Large',
style: textTheme.titleLarge!,
),
TextStyleExample(
name: 'Title Medium',
style: textTheme.titleMedium!,
),
TextStyleExample(
name: 'Title Small',
style: textTheme.titleSmall!,
),
TextStyleExample(
name: 'Label Large',
style: textTheme.labelLarge!,
),
TextStyleExample(
name: 'Label Medium',
style: textTheme.labelMedium!,
),
TextStyleExample(
name: 'Label Small',
style: textTheme.labelSmall!,
),
TextStyleExample(
name: 'Body Large',
style: textTheme.bodyLarge!,
),
TextStyleExample(
name: 'Body Medium',
style: textTheme.bodyMedium!,
),
TextStyleExample(
name: 'Body Small',
style: textTheme.bodySmall!,
),
],
),
);
@@ -56,7 +86,11 @@ class TypographyScreen extends StatelessWidget {
}
class TextStyleExample extends StatelessWidget {
const TextStyleExample({super.key, required this.name, required this.style});
const TextStyleExample({
super.key,
required this.name,
required this.style,
});
final String name;
final TextStyle style;

View File

@@ -2,18 +2,16 @@ name: material_3_demo
description:
A Flutter project showcasing supported Material 3 components, typography, color system and elevation.
Supports different light/dark mode, color seed, and comparison to Material 2.
publish_to: "none"
version: 1.0.0+1
resolution: workspace
environment:
sdk: ^3.7.0-0
sdk: ^3.9.0-0
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
url_launcher: ^6.1.8
@@ -24,6 +22,5 @@ dev_dependencies:
sdk: flutter
integration_test:
sdk: flutter
flutter:
uses-material-design: true

View File

@@ -90,7 +90,10 @@ void main() {
);
expect(find.text('Light ColorScheme'), findsOneWidget);
expect(find.text('Dark ColorScheme'), findsOneWidget);
expect(find.byType(SchemePreview, skipOffstage: false), findsNWidgets(2));
expect(
find.byType(SchemePreview, skipOffstage: false),
findsNWidgets(2),
);
});
testWidgets(
@@ -107,7 +110,9 @@ void main() {
const testLabel = 'Test Label';
const testTone = '50';
final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
final gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
// Wrap in MaterialApp + Scaffold so we can show SnackBars
await tester.pumpWidget(

View File

@@ -20,14 +20,26 @@ void main() {
findsOneWidget,
);
expect(find.widgetWithIcon(AppBar, Icons.filter_2), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.palette_outlined), findsOneWidget);
expect(
find.widgetWithIcon(AppBar, Icons.palette_outlined),
findsOneWidget,
);
// Elements on the component screen
// Common buttons
expect(find.widgetWithText(ElevatedButton, 'Elevated'), findsNWidgets(2));
expect(
find.widgetWithText(ElevatedButton, 'Elevated'),
findsNWidgets(2),
);
expect(find.widgetWithText(FilledButton, 'Filled'), findsNWidgets(2));
expect(find.widgetWithText(FilledButton, 'Filled tonal'), findsNWidgets(2));
expect(find.widgetWithText(OutlinedButton, 'Outlined'), findsNWidgets(2));
expect(
find.widgetWithText(FilledButton, 'Filled tonal'),
findsNWidgets(2),
);
expect(
find.widgetWithText(OutlinedButton, 'Outlined'),
findsNWidgets(2),
);
expect(find.widgetWithText(TextButton, 'Text'), findsNWidgets(2));
expect(find.widgetWithText(Buttons, 'Icon'), findsNWidgets(5));
@@ -36,7 +48,10 @@ void main() {
find.byType(FloatingActionButton),
findsNWidgets(6),
); // 2 more show up in the bottom app bar. 1 more in the navigation rail
expect(find.widgetWithText(FloatingActionButton, 'Create'), findsOneWidget);
expect(
find.widgetWithText(FloatingActionButton, 'Create'),
findsOneWidget,
);
// Icon buttons
expect(
@@ -52,13 +67,18 @@ void main() {
expect(find.byType(Badge), findsNWidgets(4));
// Progress indicators
Finder circularProgressIndicator = find.byType(CircularProgressIndicator);
Finder circularProgressIndicator = find.byType(
CircularProgressIndicator,
);
expect(circularProgressIndicator, findsOneWidget);
Finder linearProgressIndicator = find.byType(LinearProgressIndicator);
expect(linearProgressIndicator, findsOneWidget);
// Snackbar
expect(find.widgetWithText(TextButton, 'Show snackbar'), findsOneWidget);
expect(
find.widgetWithText(TextButton, 'Show snackbar'),
findsOneWidget,
);
// Bottom sheet
expect(
@@ -128,8 +148,14 @@ void main() {
expect(find.byType(InputChip), findsNWidgets(2));
// Date and time pickers
expect(find.widgetWithText(DatePicker, 'Show date picker'), findsOneWidget);
expect(find.widgetWithText(TimePicker, 'Show time picker'), findsOneWidget);
expect(
find.widgetWithText(DatePicker, 'Show date picker'),
findsOneWidget,
);
expect(
find.widgetWithText(TimePicker, 'Show time picker'),
findsOneWidget,
);
// Menus
expect(find.byType(MenuAnchor), findsNWidgets(5));
@@ -152,175 +178,221 @@ void main() {
expect(find.widgetWithText(TextField, 'Outlined'), findsNWidgets(2));
});
testWidgets('NavigationRail doesn\'t show when width value is small than 1000 '
'(in Portrait mode or narrow screen)', (tester) async {
widgetSetup(tester, 999, windowHeight: 7000);
await tester.pumpWidget(const App());
await tester.pumpAndSettle();
testWidgets(
'NavigationRail doesn\'t show when width value is small than 1000 '
'(in Portrait mode or narrow screen)',
(tester) async {
widgetSetup(tester, 999, windowHeight: 7000);
await tester.pumpWidget(const App());
await tester.pumpAndSettle();
// When screen width is less than 1000, NavigationBar will show. At the same
// time, the NavigationBar example still show up in the navigation group.
expect(
find.byType(NavigationBars),
findsNWidgets(3),
); // The real navBar, badges example and navBar example
expect(find.widgetWithText(NavigationBar, 'Components'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Color'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Typography'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Elevation'), findsOneWidget);
// When screen width is less than 1000, NavigationBar will show. At the same
// time, the NavigationBar example still show up in the navigation group.
expect(
find.byType(NavigationBars),
findsNWidgets(3),
); // The real navBar, badges example and navBar example
expect(
find.widgetWithText(NavigationBar, 'Components'),
findsOneWidget,
);
expect(find.widgetWithText(NavigationBar, 'Color'), findsOneWidget);
expect(
find.widgetWithText(NavigationBar, 'Typography'),
findsOneWidget,
);
expect(
find.widgetWithText(NavigationBar, 'Elevation'),
findsOneWidget,
);
expect(find.widgetWithText(NavigationBar, 'Explore'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Pets'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Account'), findsOneWidget);
});
expect(
find.widgetWithText(NavigationBar, 'Explore'),
findsOneWidget,
);
expect(find.widgetWithText(NavigationBar, 'Pets'), findsOneWidget);
expect(
find.widgetWithText(NavigationBar, 'Account'),
findsOneWidget,
);
},
);
testWidgets('NavigationRail shows when width value is greater than or equal '
'to 1000 (in Landscape mode or wider screen)', (tester) async {
widgetSetup(tester, 1001, windowHeight: 3000);
await tester.pumpWidget(const App());
await tester.pumpAndSettle();
testWidgets(
'NavigationRail shows when width value is greater than or equal '
'to 1000 (in Landscape mode or wider screen)',
(tester) async {
widgetSetup(tester, 1001, windowHeight: 3000);
await tester.pumpWidget(const App());
await tester.pumpAndSettle();
// When screen width is greater than or equal to 1000, NavigationRail will show.
// At the same time, the NavigationBar will NOT show.
expect(find.byType(NavigationRail), findsNWidgets(2));
expect(find.byType(Tooltip, skipOffstage: false), findsWidgets);
expect(find.widgetWithText(NavigationRail, 'Components'), findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Color'), findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Typography'), findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Elevation'), findsOneWidget);
// When screen width is greater than or equal to 1000, NavigationRail will show.
// At the same time, the NavigationBar will NOT show.
expect(find.byType(NavigationRail), findsNWidgets(2));
expect(find.byType(Tooltip, skipOffstage: false), findsWidgets);
expect(
find.widgetWithText(NavigationRail, 'Components'),
findsOneWidget,
);
expect(find.widgetWithText(NavigationRail, 'Color'), findsOneWidget);
expect(
find.widgetWithText(NavigationRail, 'Typography'),
findsOneWidget,
);
expect(
find.widgetWithText(NavigationRail, 'Elevation'),
findsOneWidget,
);
expect(find.widgetWithText(NavigationBar, 'Explore'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Pets'), findsOneWidget);
expect(find.widgetWithText(NavigationBar, 'Account'), findsOneWidget);
expect(
find.widgetWithText(NavigationBar, 'Explore'),
findsOneWidget,
);
expect(find.widgetWithText(NavigationBar, 'Pets'), findsOneWidget);
expect(
find.widgetWithText(NavigationBar, 'Account'),
findsOneWidget,
);
// the Navigation bar should be out of screen.
final RenderBox box = tester.renderObject(
find.widgetWithText(NavigationBar, 'Components'),
);
expect(box.localToGlobal(Offset.zero), const Offset(0.0, 3080.0));
});
// the Navigation bar should be out of screen.
final RenderBox box = tester.renderObject(
find.widgetWithText(NavigationBar, 'Components'),
);
expect(box.localToGlobal(Offset.zero), const Offset(0.0, 3080.0));
},
);
testWidgets('Material version switches between Material3 and Material2 when '
'the version icon is clicked', (tester) async {
widgetSetup(tester, 450, windowHeight: 7000);
await tester.pumpWidget(const App());
BuildContext defaultElevatedButton = tester.firstElement(
find.byType(ElevatedButton),
);
BuildContext defaultIconButton = tester.firstElement(
find.byType(IconButton),
);
BuildContext defaultFAB = tester.firstElement(
find.byType(FloatingActionButton),
);
BuildContext defaultCard = tester.firstElement(
find.widgetWithText(Card, 'Elevated'),
);
BuildContext defaultChip = tester.firstElement(
find.widgetWithText(ActionChip, 'Assist'),
);
Finder dialog = find.text('Show dialog');
await tester.tap(dialog);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext defaultAlertDialog = tester.element(find.byType(AlertDialog));
expect(Theme.of(defaultAlertDialog).useMaterial3, true);
Finder dismiss = find.text('Okay');
await tester.tap(dismiss);
await tester.pumpAndSettle(const Duration(microseconds: 500));
testWidgets(
'Material version switches between Material3 and Material2 when '
'the version icon is clicked',
(tester) async {
widgetSetup(tester, 450, windowHeight: 7000);
await tester.pumpWidget(const App());
BuildContext defaultElevatedButton = tester.firstElement(
find.byType(ElevatedButton),
);
BuildContext defaultIconButton = tester.firstElement(
find.byType(IconButton),
);
BuildContext defaultFAB = tester.firstElement(
find.byType(FloatingActionButton),
);
BuildContext defaultCard = tester.firstElement(
find.widgetWithText(Card, 'Elevated'),
);
BuildContext defaultChip = tester.firstElement(
find.widgetWithText(ActionChip, 'Assist'),
);
Finder dialog = find.text('Show dialog');
await tester.tap(dialog);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext defaultAlertDialog = tester.element(
find.byType(AlertDialog),
);
expect(Theme.of(defaultAlertDialog).useMaterial3, true);
Finder dismiss = find.text('Okay');
await tester.tap(dismiss);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(find.widgetWithIcon(AppBar, Icons.filter_2), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.filter_3), findsNothing);
expect(find.text('Material 3'), findsOneWidget);
expect(Theme.of(defaultElevatedButton).useMaterial3, true);
expect(Theme.of(defaultIconButton).useMaterial3, true);
expect(Theme.of(defaultFAB).useMaterial3, true);
expect(Theme.of(defaultCard).useMaterial3, true);
expect(Theme.of(defaultChip).useMaterial3, true);
expect(find.widgetWithIcon(AppBar, Icons.filter_2), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.filter_3), findsNothing);
expect(find.text('Material 3'), findsOneWidget);
expect(Theme.of(defaultElevatedButton).useMaterial3, true);
expect(Theme.of(defaultIconButton).useMaterial3, true);
expect(Theme.of(defaultFAB).useMaterial3, true);
expect(Theme.of(defaultCard).useMaterial3, true);
expect(Theme.of(defaultChip).useMaterial3, true);
Finder appbarM3Icon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.filter_2),
);
await tester.tap(appbarM3Icon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext updatedElevatedButton = tester.firstElement(
find.byType(ElevatedButton),
);
BuildContext updatedIconButton = tester.firstElement(
find.byType(IconButton),
);
BuildContext updatedFAB = tester.firstElement(
find.byType(FloatingActionButton),
);
BuildContext updatedCard = tester.firstElement(find.byType(Card));
BuildContext updatedChip = tester.firstElement(
find.widgetWithText(ActionChip, 'Assist'),
);
Finder updatedDialog = find.text('Show dialog');
await tester.tap(updatedDialog);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext updatedAlertDialog = tester.firstElement(
find.byType(AlertDialog),
);
expect(Theme.of(updatedAlertDialog).useMaterial3, false);
Finder updatedDismiss = find.text('Dismiss');
await tester.tap(updatedDismiss);
await tester.pumpAndSettle(const Duration(microseconds: 500));
Finder appbarM3Icon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.filter_2),
);
await tester.tap(appbarM3Icon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext updatedElevatedButton = tester.firstElement(
find.byType(ElevatedButton),
);
BuildContext updatedIconButton = tester.firstElement(
find.byType(IconButton),
);
BuildContext updatedFAB = tester.firstElement(
find.byType(FloatingActionButton),
);
BuildContext updatedCard = tester.firstElement(find.byType(Card));
BuildContext updatedChip = tester.firstElement(
find.widgetWithText(ActionChip, 'Assist'),
);
Finder updatedDialog = find.text('Show dialog');
await tester.tap(updatedDialog);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext updatedAlertDialog = tester.firstElement(
find.byType(AlertDialog),
);
expect(Theme.of(updatedAlertDialog).useMaterial3, false);
Finder updatedDismiss = find.text('Dismiss');
await tester.tap(updatedDismiss);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(find.widgetWithIcon(AppBar, Icons.filter_3), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.filter_2), findsNothing);
expect(find.text('Material 2'), findsOneWidget);
expect(Theme.of(updatedElevatedButton).useMaterial3, false);
expect(Theme.of(updatedIconButton).useMaterial3, false);
expect(Theme.of(updatedFAB).useMaterial3, false);
expect(Theme.of(updatedCard).useMaterial3, false);
expect(Theme.of(updatedChip).useMaterial3, false);
});
expect(find.widgetWithIcon(AppBar, Icons.filter_3), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.filter_2), findsNothing);
expect(find.text('Material 2'), findsOneWidget);
expect(Theme.of(updatedElevatedButton).useMaterial3, false);
expect(Theme.of(updatedIconButton).useMaterial3, false);
expect(Theme.of(updatedFAB).useMaterial3, false);
expect(Theme.of(updatedCard).useMaterial3, false);
expect(Theme.of(updatedChip).useMaterial3, false);
},
);
testWidgets('Other screens become Material2 mode after changing mode from '
'main screen', (tester) async {
await tester.pumpWidget(const App());
Finder appbarM2Icon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.filter_2),
);
await tester.tap(appbarM2Icon);
Finder secondScreenIcon = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.format_paint_outlined,
),
);
await tester.tap(secondScreenIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext lightThemeText = tester.element(
find.text('Light ColorScheme'),
);
expect(Theme.of(lightThemeText).useMaterial3, false);
Finder thirdScreenIcon = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.text_snippet_outlined,
),
);
await tester.tap(thirdScreenIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext displayLargeText = tester.element(find.text('Display Large'));
expect(Theme.of(displayLargeText).useMaterial3, false);
Finder fourthScreenIcon = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.invert_colors_on_outlined,
),
);
await tester.tap(fourthScreenIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext material = tester.firstElement(find.byType(Material));
expect(Theme.of(material).useMaterial3, false);
});
testWidgets(
'Other screens become Material2 mode after changing mode from '
'main screen',
(tester) async {
await tester.pumpWidget(const App());
Finder appbarM2Icon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.filter_2),
);
await tester.tap(appbarM2Icon);
Finder secondScreenIcon = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.format_paint_outlined,
),
);
await tester.tap(secondScreenIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext lightThemeText = tester.element(
find.text('Light ColorScheme'),
);
expect(Theme.of(lightThemeText).useMaterial3, false);
Finder thirdScreenIcon = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.text_snippet_outlined,
),
);
await tester.tap(thirdScreenIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext displayLargeText = tester.element(
find.text('Display Large'),
);
expect(Theme.of(displayLargeText).useMaterial3, false);
Finder fourthScreenIcon = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.invert_colors_on_outlined,
),
);
await tester.tap(fourthScreenIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext material = tester.firstElement(find.byType(Material));
expect(Theme.of(material).useMaterial3, false);
},
);
testWidgets('Brightness mode switches between dark and light when'
'the brightness icon is clicked', (tester) async {

View File

@@ -34,7 +34,10 @@ void main() {
expect(tintIconOnBar, findsNothing);
Finder selectedTintIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(NavigationDestination, Icons.opacity),
matching: find.widgetWithIcon(
NavigationDestination,
Icons.opacity,
),
);
expect(selectedTintIconOnBar, findsOneWidget);
expect(find.text('Surface Tint Color Only'), findsOneWidget);
@@ -69,14 +72,19 @@ void main() {
},
);
testWidgets('Surface Tones screen shows correct content', (tester) async {
testWidgets('Surface Tones screen shows correct content', (
tester,
) async {
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(body: Row(children: [ElevationScreen()])),
),
);
expect(find.text('Surface Tint Color Only'), findsOneWidget);
expect(find.text('Surface Tint Color and Shadow Color'), findsOneWidget);
expect(
find.text('Surface Tint Color and Shadow Color'),
findsOneWidget,
);
expect(find.text('Shadow Color Only'), findsOneWidget);
expect(find.byType(ElevationGrid), findsNWidgets(3));
expect(find.byType(ElevationCard), findsNWidgets(18));