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

Flutter 3.7.0 (#1556)

* Update `simplistic_editor` for Flutter 3.4 beta

* Re-enable beta and master CI

* Disable on master

* Added sample code for using plugins and channels from background isolates.

* goderbauer feedback 1

* goderbauer feedback2

* goderbauer feedback 3

* Add `background_isolate_channels` to CI

* Enable beta CI

* Enable all `stable` CI projects

* `dart fix --apply`

* `print` -> `denugPrint`

* Make deps min version not pinned

* Drop `_isDebug`

* Remove unused import

* `dart format`

* Fixup `linting_tool`

* Fixup `form_app`

* Enable all `master` CI

* Basic fixes

* Patch `simplistic_editor`

* Fix nl at eol

* Comment out `simplistic_editor`

* Incorporating @bleroux's latest changes

* Clean up CI scripts

* Copy `experimental/material_3_demo` to top level

* Update `game_template`

* Update `animations`

* Update `desktop_photo_search`

* Update `flutter_maps_firestore`

* Update `form_app`

* Update `infinite_list`

* Update `isolate_example`

* Update `jsonexample`

* Update `navigation_and_routing`

* Update `place_tracker`

* Update `platform_channels`

* Update `platform_design`

* Update `provider_shopper`

* Fixup `context_menus`

* `dart format`

* Update the main `material_3_demo`

* Make `tool/flutter_ci_script_stable.sh` executable again

Co-authored-by: Bruno Leroux <bruno.leroux@gmail.com>
Co-authored-by: Aaron Clarke <aaclarke@google.com>
This commit is contained in:
Brett Morgan
2023-01-25 10:08:51 +11:00
committed by GitHub
parent 4ee2002665
commit 5401bb88b4
337 changed files with 4550 additions and 1516 deletions

View File

@@ -2,9 +2,12 @@
This sample Flutter app showcases Material 3 features in the Flutter Material library. These features include updated components, typography, color system and elevation support. The app supports light and dark themes, different color palettes, as well as the ability to switch between Material 2 and Material 3. For more information about Material 3, the guidance is now live at https://m3.material.io/.
This app also includes new M3 components such as IconButtons, Chips, TextFields, Switches, Checkboxes, Radio buttons and ProgressIndicators.
# Preview
https://user-images.githubusercontent.com/36861262/166358511-43d6a30b-33fe-4680-a4d1-808f087e740e.mp4
<img width="400" alt="Screen Shot 2022-08-12 at 12 00 28 PM" src="https://user-images.githubusercontent.com/36861262/184426137-47b550e1-5c6e-4bb7-b647-b1741f96d42b.png"><img width="400" alt="Screen Shot 2022-08-12 at 12 00 38 PM" src="https://user-images.githubusercontent.com/36861262/184426154-063a39e8-24bd-40be-90cd-984bf81c0fdf.png">
# Features
## Icon Buttons on the Top App Bar
@@ -13,9 +16,9 @@ https://user-images.githubusercontent.com/36861262/166358511-43d6a30b-33fe-4680-
<img src="https://user-images.githubusercontent.com/36861262/166508002-90fce980-d228-4312-a95f-a1919bb79ccc.png" width="25" /> Users can switch between Material 2 and Material 3 for the displayed components with this button.
<img src="https://user-images.githubusercontent.com/36861262/166511137-85dea8df-0017-4649-b913-14d4b7a17c2f.png" width="25" /> This button will bring up a pop-up menu that allows the user to change the base color used for the light and dark themes. This uses a new color seed feature to generate entire color schemes from a single color.
## Component Screen
The default screen displays all the updated components in Material 3: AppBar, common Buttons, Floating Action Button(FAB), Card, Dialog, NavigationBar, and NavigationRail.
The default screen displays all the updated components in Material 3: AppBar, common Buttons, Floating Action Button(FAB), Chips, Card, Checkbox, Dialog, NavigationBar, NavigationRail, ProgressIndicators, Radio buttons, TextFields and Switch.
### Adaptive Layout
Based on the fact that NavigationRail is not recommended on a small screen, the app changes its layout based on the screen width. If it's played on iOS or Android devices which have a narrow screen, a Navigation Bar will show at the bottom and will be used to navigate. But if it's played as a desktop or a web app, a Navigation Rail will show on the left side and at the same time, a Navigation Bar will show as an example but will not have any functionality.
@@ -30,5 +33,3 @@ The Typography Screen displays the text styles used in for the default TextTheme
## Elevation Screen
The Elevation screen shows different ways of elevation with a new supported feature "surfaceTintColor" in the Material library.

View File

@@ -17,10 +17,14 @@ class ColorPalettesScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
Color selectedColor = Theme.of(context).primaryColor;
ThemeData lightTheme =
ThemeData(colorSchemeSeed: selectedColor, brightness: Brightness.light);
ThemeData darkTheme =
ThemeData(colorSchemeSeed: selectedColor, brightness: Brightness.dark);
ThemeData lightTheme = ThemeData(
colorSchemeSeed: selectedColor,
brightness: Brightness.light,
);
ThemeData darkTheme = ThemeData(
colorSchemeSeed: selectedColor,
brightness: Brightness.dark,
);
Widget schemeLabel(String brightness) {
return Padding(
@@ -48,12 +52,12 @@ class ColorPalettesScreen extends StatelessWidget {
child: Column(
children: [
divider,
schemeLabel("Light Theme"),
schemeLabel('Light Theme'),
schemeView(lightTheme),
divider,
divider,
schemeLabel("Dark Theme"),
schemeView(darkTheme)
schemeLabel('Dark Theme'),
schemeView(darkTheme),
],
),
);
@@ -66,19 +70,19 @@ class ColorPalettesScreen extends StatelessWidget {
Expanded(
child: Column(
children: [
schemeLabel("Light Theme"),
schemeView(lightTheme)
schemeLabel('Light Theme'),
schemeView(lightTheme),
],
),
),
Expanded(
child: Column(
children: [
schemeLabel("Dark Theme"),
schemeView(darkTheme)
schemeLabel('Dark Theme'),
schemeView(darkTheme),
],
),
)
),
],
),
),
@@ -100,46 +104,46 @@ class ColorSchemeView extends StatelessWidget {
children: [
ColorGroup(children: [
ColorChip(
label: "primary",
label: 'primary',
color: colorScheme.primary,
onColor: colorScheme.onPrimary,
),
ColorChip(
label: "onPrimary",
label: 'onPrimary',
color: colorScheme.onPrimary,
onColor: colorScheme.primary),
ColorChip(
label: "primaryContainer",
label: 'primaryContainer',
color: colorScheme.primaryContainer,
onColor: colorScheme.onPrimaryContainer,
),
ColorChip(
label: "onPrimaryContainer",
label: 'onPrimaryContainer',
color: colorScheme.onPrimaryContainer,
onColor: colorScheme.primaryContainer,
)
),
]),
divider,
ColorGroup(children: [
ColorChip(
label: "secondary",
label: 'secondary',
color: colorScheme.secondary,
onColor: colorScheme.onSecondary,
),
ColorChip(
label: "onSecondary",
label: 'onSecondary',
color: colorScheme.onSecondary,
onColor: colorScheme.secondary,
),
ColorChip(
label: "secondaryContainer",
label: 'secondaryContainer',
color: colorScheme.secondaryContainer,
onColor: colorScheme.onSecondaryContainer,
),
ColorChip(
label: "onSecondaryContainer",
label: 'onSecondaryContainer',
color: colorScheme.onSecondaryContainer,
onColor: colorScheme.secondaryContainer)
onColor: colorScheme.secondaryContainer),
]),
divider,
ColorGroup(
@@ -248,10 +252,12 @@ class ColorGroup extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: children,
return RepaintBoundary(
child: Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: children,
),
),
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,37 +12,47 @@ class ElevationScreen extends StatelessWidget {
Color shadowColor = Theme.of(context).colorScheme.shadow;
Color surfaceTint = Theme.of(context).colorScheme.primary;
return Expanded(
child: ListView(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 20, 16.0, 0),
child: Text(
'Surface Tint only',
style: Theme.of(context).textTheme.titleLarge,
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 20, 16.0, 0),
child: Text(
'Surface Tint Color Only',
style: Theme.of(context).textTheme.titleLarge,
),
),
),
ElevationGrid(surfaceTintColor: surfaceTint),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 0),
child: Text(
'Surface Tint and Shadow',
style: Theme.of(context).textTheme.titleLarge,
),
SliverList(
delegate: SliverChildListDelegate(<Widget>[
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 0),
child: Text(
'Surface Tint Color and Shadow Color',
style: Theme.of(context).textTheme.titleLarge,
),
),
]),
),
ElevationGrid(
shadowColor: shadowColor,
surfaceTintColor: surfaceTint,
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 0),
child: Text(
'Shadow only',
style: Theme.of(context).textTheme.titleLarge,
),
SliverList(
delegate: SliverChildListDelegate(<Widget>[
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 0),
child: Text(
'Shadow Color Only',
style: Theme.of(context).textTheme.titleLarge,
),
),
]),
),
ElevationGrid(shadowColor: shadowColor)
ElevationGrid(shadowColor: shadowColor),
],
),
);
@@ -72,18 +82,16 @@ class ElevationGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
return SliverPadding(
padding: const EdgeInsets.all(8),
child: LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth < narrowScreenWidthThreshold) {
return GridView.count(
shrinkWrap: true,
sliver: SliverLayoutBuilder(builder: (context, constraints) {
if (constraints.crossAxisExtent < narrowScreenWidthThreshold) {
return SliverGrid.count(
crossAxisCount: 3,
children: elevationCards(shadowColor, surfaceTintColor),
);
} else {
return GridView.count(
shrinkWrap: true,
return SliverGrid.count(
crossAxisCount: 6,
children: elevationCards(shadowColor, surfaceTintColor),
);
@@ -117,7 +125,6 @@ class _ElevationCardState extends State<ElevationCard> {
@override
Widget build(BuildContext context) {
const BorderRadius borderRadius = BorderRadius.all(Radius.circular(4.0));
final bool showOpacity = _elevation == widget.info.elevation;
final Color color = Theme.of(context).colorScheme.surface;
return Padding(
@@ -142,7 +149,7 @@ class _ElevationCardState extends State<ElevationCard> {
'${widget.info.level.toInt()} dp',
style: Theme.of(context).textTheme.labelMedium,
),
if (showOpacity)
if (widget.surfaceTint != null)
Expanded(
child: Align(
alignment: Alignment.bottomRight,

View File

@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'color_palettes_screen.dart';
import 'component_screen.dart';
@@ -10,7 +11,12 @@ import 'elevation_screen.dart';
import 'typography_screen.dart';
void main() {
runApp(const Material3Demo());
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: Material3Demo(),
),
);
}
class Material3Demo extends StatefulWidget {
@@ -24,180 +30,708 @@ class Material3Demo extends StatefulWidget {
// screenWidthThreshold; otherwise, NavigationBar is used for navigation.
const double narrowScreenWidthThreshold = 450;
const Color m3BaseColor = Color(0xff6750a4);
const List<Color> colorOptions = [
m3BaseColor,
Colors.blue,
Colors.teal,
Colors.green,
Colors.yellow,
Colors.orange,
Colors.pink
];
const List<String> colorText = <String>[
"M3 Baseline",
"Blue",
"Teal",
"Green",
"Yellow",
"Orange",
"Pink",
];
const double transitionLength = 500;
class _Material3DemoState extends State<Material3Demo> {
enum ColorSeed {
baseColor('M3 Baseline', Color(0xff6750a4)),
indigo('Indigo', Colors.indigo),
blue('Blue', Colors.blue),
teal('Teal', Colors.teal),
green('Green', Colors.green),
yellow('Yellow', Colors.yellow),
orange('Orange', Colors.orange),
deepOrange('Deep Orange', Colors.deepOrange),
pink('Pink', Colors.pink);
const ColorSeed(this.label, this.color);
final String label;
final Color color;
}
enum ScreenSelected {
component(0),
color(1),
typography(2),
elevation(3);
const ScreenSelected(this.value);
final int value;
}
class _Material3DemoState extends State<Material3Demo>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
late final AnimationController controller;
late final CurvedAnimation railAnimation;
bool controllerInitialized = false;
bool showMediumSizeLayout = false;
bool showLargeSizeLayout = false;
bool useMaterial3 = true;
bool useLightMode = true;
int colorSelected = 0;
int screenIndex = 0;
ThemeMode themeMode = ThemeMode.system;
bool get useLightMode {
switch (themeMode) {
case ThemeMode.system:
return SchedulerBinding.instance.window.platformBrightness ==
Brightness.light;
case ThemeMode.light:
return true;
case ThemeMode.dark:
return false;
}
}
late ThemeData themeData;
ColorSeed colorSelected = ColorSeed.baseColor;
int screenIndex = ScreenSelected.component.value;
@override
initState() {
super.initState();
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
controller = AnimationController(
duration: Duration(milliseconds: transitionLength.toInt() * 2),
value: 0,
vsync: this,
);
railAnimation = CurvedAnimation(
parent: controller,
curve: const Interval(0.5, 1.0),
);
}
ThemeData updateThemes(int colorIndex, bool useMaterial3, bool useLightMode) {
return ThemeData(
colorSchemeSeed: colorOptions[colorSelected],
useMaterial3: useMaterial3,
brightness: useLightMode ? Brightness.light : Brightness.dark);
@override
void dispose() {
controller.dispose();
super.dispose();
}
void handleScreenChanged(int selectedScreen) {
@override
void didChangeDependencies() {
super.didChangeDependencies();
final double width = MediaQuery.of(context).size.width;
final AnimationStatus status = controller.status;
if (width > 1000) {
if (width > 1500) {
showMediumSizeLayout = false;
showLargeSizeLayout = true;
} else {
showMediumSizeLayout = true;
showLargeSizeLayout = false;
}
if (status != AnimationStatus.forward &&
status != AnimationStatus.completed) {
controller.forward();
}
} else {
showMediumSizeLayout = false;
showLargeSizeLayout = false;
if (status != AnimationStatus.reverse &&
status != AnimationStatus.dismissed) {
controller.reverse();
}
}
if (!controllerInitialized) {
controllerInitialized = true;
controller.value = width > 1000 ? 1 : 0;
}
}
void handleScreenChanged(int screenSelected) {
setState(() {
screenIndex = selectedScreen;
screenIndex = screenSelected;
});
}
void handleBrightnessChange() {
void handleBrightnessChange(bool useLightMode) {
setState(() {
useLightMode = !useLightMode;
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
themeMode = useLightMode ? ThemeMode.light : ThemeMode.dark;
});
}
void handleMaterialVersionChange() {
setState(() {
useMaterial3 = !useMaterial3;
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
});
}
void handleColorSelect(int value) {
setState(() {
colorSelected = value;
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
colorSelected = ColorSeed.values[value];
});
}
Widget createScreenFor(int screenIndex, bool showNavBarExample) {
switch (screenIndex) {
case 0:
return ComponentScreen(showNavBottomBar: showNavBarExample);
case 1:
Widget createScreenFor(
ScreenSelected screenSelected, bool showNavBarExample) {
switch (screenSelected) {
case ScreenSelected.component:
return Expanded(
child: OneTwoTransition(
animation: railAnimation,
one: FirstComponentList(
showNavBottomBar: showNavBarExample,
scaffoldKey: scaffoldKey,
showSecondList: showMediumSizeLayout || showLargeSizeLayout),
two: SecondComponentList(
scaffoldKey: scaffoldKey,
),
),
);
case ScreenSelected.color:
return const ColorPalettesScreen();
case 2:
case ScreenSelected.typography:
return const TypographyScreen();
case 3:
case ScreenSelected.elevation:
return const ElevationScreen();
default:
return ComponentScreen(showNavBottomBar: showNavBarExample);
return FirstComponentList(
showNavBottomBar: showNavBarExample,
scaffoldKey: scaffoldKey,
showSecondList: showMediumSizeLayout || showLargeSizeLayout);
}
}
PreferredSizeWidget createAppBar() {
return AppBar(
title: useMaterial3 ? const Text("Material 3") : const Text("Material 2"),
actions: [
IconButton(
icon: useLightMode
? const Icon(Icons.wb_sunny_outlined)
: const Icon(Icons.wb_sunny),
onPressed: handleBrightnessChange,
tooltip: "Toggle brightness",
),
IconButton(
icon: useMaterial3
? const Icon(Icons.filter_3)
: const Icon(Icons.filter_2),
onPressed: handleMaterialVersionChange,
tooltip: "Switch to Material ${useMaterial3 ? 2 : 3}",
),
PopupMenuButton(
icon: const Icon(Icons.more_vert),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
itemBuilder: (context) {
return List.generate(colorOptions.length, (index) {
return PopupMenuItem(
value: index,
child: Wrap(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Icon(
index == colorSelected
? Icons.color_lens
: Icons.color_lens_outlined,
color: colorOptions[index],
),
),
Padding(
padding: const EdgeInsets.only(left: 20),
child: Text(colorText[index]))
],
));
});
},
onSelected: handleColorSelect,
),
],
title: useMaterial3 ? const Text('Material 3') : const Text('Material 2'),
actions: !showMediumSizeLayout && !showLargeSizeLayout
? [
_BrightnessButton(
handleBrightnessChange: handleBrightnessChange,
),
_Material3Button(
handleMaterialVersionChange: handleMaterialVersionChange,
),
_ColorSeedButton(
handleColorSelect: handleColorSelect,
colorSelected: colorSelected,
),
]
: [Container()],
);
}
Widget _expandedTrailingActions() => Container(
constraints: const BoxConstraints.tightFor(width: 250),
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
const Text('Brightness'),
Expanded(child: Container()),
Switch(
value: useLightMode,
onChanged: (value) {
handleBrightnessChange(value);
})
],
),
Row(
children: [
useMaterial3
? const Text('Material 3')
: const Text('Material 2'),
Expanded(child: Container()),
Switch(
value: useMaterial3,
onChanged: (_) {
handleMaterialVersionChange();
})
],
),
const Divider(),
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200.0),
child: GridView.count(
crossAxisCount: 3,
children: List.generate(
ColorSeed.values.length,
(i) => IconButton(
icon: const Icon(Icons.radio_button_unchecked),
color: ColorSeed.values[i].color,
isSelected:
colorSelected.color == ColorSeed.values[i].color,
selectedIcon: const Icon(Icons.circle),
onPressed: () {
handleColorSelect(i);
},
)),
),
),
],
),
);
Widget _trailingActions() => Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
child: _BrightnessButton(
handleBrightnessChange: handleBrightnessChange,
showTooltipBelow: false,
),
),
Flexible(
child: _Material3Button(
handleMaterialVersionChange: handleMaterialVersionChange,
showTooltipBelow: false,
),
),
Flexible(
child: _ColorSeedButton(
handleColorSelect: handleColorSelect,
colorSelected: colorSelected,
),
),
],
);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material 3',
themeMode: useLightMode ? ThemeMode.light : ThemeMode.dark,
theme: themeData,
home: LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth < narrowScreenWidthThreshold) {
return Scaffold(
themeMode: themeMode,
theme: ThemeData(
colorSchemeSeed: colorSelected.color,
useMaterial3: useMaterial3,
brightness: Brightness.light,
),
darkTheme: ThemeData(
colorSchemeSeed: colorSelected.color,
useMaterial3: useMaterial3,
brightness: Brightness.dark,
),
home: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return NavigationTransition(
scaffoldKey: scaffoldKey,
animationController: controller,
railAnimation: railAnimation,
appBar: createAppBar(),
body: Row(children: <Widget>[
createScreenFor(screenIndex, false),
]),
bottomNavigationBar: NavigationBars(
onSelectItem: handleScreenChanged,
body: createScreenFor(
ScreenSelected.values[screenIndex], controller.value == 1),
navigationRail: NavigationRail(
extended: showLargeSizeLayout,
destinations: navRailDestinations,
selectedIndex: screenIndex,
onDestinationSelected: (index) {
setState(() {
screenIndex = index;
handleScreenChanged(screenIndex);
});
},
trailing: Expanded(
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: showLargeSizeLayout
? _expandedTrailingActions()
: _trailingActions(),
),
),
),
navigationBar: NavigationBars(
onSelectItem: (index) {
setState(() {
screenIndex = index;
handleScreenChanged(screenIndex);
});
},
selectedIndex: screenIndex,
isExampleBar: false,
),
);
} else {
return Scaffold(
appBar: createAppBar(),
body: SafeArea(
bottom: false,
top: false,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: NavigationRailSection(
onSelectItem: handleScreenChanged,
selectedIndex: screenIndex)),
const VerticalDivider(thickness: 1, width: 1),
createScreenFor(screenIndex, true),
],
),
),
);
}
}),
},
),
);
}
}
class _BrightnessButton extends StatelessWidget {
const _BrightnessButton({
required this.handleBrightnessChange,
this.showTooltipBelow = true,
});
final Function handleBrightnessChange;
final bool showTooltipBelow;
@override
Widget build(BuildContext context) {
final isBright = Theme.of(context).brightness == Brightness.light;
return Tooltip(
preferBelow: showTooltipBelow,
message: 'Toggle brightness',
child: IconButton(
icon: isBright
? const Icon(Icons.dark_mode_outlined)
: const Icon(Icons.light_mode_outlined),
onPressed: () => handleBrightnessChange(!isBright),
),
);
}
}
class _Material3Button extends StatelessWidget {
const _Material3Button({
required this.handleMaterialVersionChange,
this.showTooltipBelow = true,
});
final void Function() handleMaterialVersionChange;
final bool showTooltipBelow;
@override
Widget build(BuildContext context) {
final useMaterial3 = Theme.of(context).useMaterial3;
return Tooltip(
preferBelow: showTooltipBelow,
message: 'Switch to Material ${useMaterial3 ? 2 : 3}',
child: IconButton(
icon: useMaterial3
? const Icon(Icons.filter_2)
: const Icon(Icons.filter_3),
onPressed: handleMaterialVersionChange,
),
);
}
}
class _ColorSeedButton extends StatelessWidget {
const _ColorSeedButton({
required this.handleColorSelect,
required this.colorSelected,
});
final void Function(int) handleColorSelect;
final ColorSeed colorSelected;
@override
Widget build(BuildContext context) {
return PopupMenuButton(
icon: Icon(
Icons.palette_outlined,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
tooltip: 'Select a seed color',
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
itemBuilder: (context) {
return List.generate(ColorSeed.values.length, (index) {
ColorSeed currentColor = ColorSeed.values[index];
return PopupMenuItem(
value: index,
enabled: currentColor != colorSelected,
child: Wrap(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Icon(
currentColor == colorSelected
? Icons.color_lens
: Icons.color_lens_outlined,
color: currentColor.color,
),
),
Padding(
padding: const EdgeInsets.only(left: 20),
child: Text(currentColor.label),
),
],
),
);
});
},
onSelected: handleColorSelect,
);
}
}
class NavigationTransition extends StatefulWidget {
const NavigationTransition(
{super.key,
required this.scaffoldKey,
required this.animationController,
required this.railAnimation,
required this.navigationRail,
required this.navigationBar,
required this.appBar,
required this.body});
final GlobalKey<ScaffoldState> scaffoldKey;
final AnimationController animationController;
final CurvedAnimation railAnimation;
final Widget navigationRail;
final Widget navigationBar;
final PreferredSizeWidget appBar;
final Widget body;
@override
State<NavigationTransition> createState() => _NavigationTransitionState();
}
class _NavigationTransitionState extends State<NavigationTransition> {
late final AnimationController controller;
late final CurvedAnimation railAnimation;
late final ReverseAnimation barAnimation;
bool controllerInitialized = false;
bool showDivider = false;
@override
void initState() {
super.initState();
controller = widget.animationController;
railAnimation = widget.railAnimation;
barAnimation = ReverseAnimation(
CurvedAnimation(
parent: controller,
curve: const Interval(0.0, 0.5),
),
);
}
@override
Widget build(BuildContext context) {
final ColorScheme colorScheme = Theme.of(context).colorScheme;
return Scaffold(
key: widget.scaffoldKey,
appBar: widget.appBar,
body: Row(
children: <Widget>[
RailTransition(
animation: railAnimation,
backgroundColor: colorScheme.surface,
child: widget.navigationRail,
),
widget.body,
],
),
bottomNavigationBar: BarTransition(
animation: barAnimation,
backgroundColor: colorScheme.surface,
child: widget.navigationBar,
),
endDrawer: const NavigationDrawerSection(),
);
}
}
final List<NavigationRailDestination> navRailDestinations = appBarDestinations
.map(
(destination) => NavigationRailDestination(
icon: Tooltip(
message: destination.label,
child: destination.icon,
),
selectedIcon: Tooltip(
message: destination.label,
child: destination.selectedIcon,
),
label: Text(destination.label),
),
)
.toList();
class SizeAnimation extends CurvedAnimation {
SizeAnimation(Animation<double> parent)
: super(
parent: parent,
curve: const Interval(
0.2,
0.8,
curve: Curves.easeInOutCubicEmphasized,
),
reverseCurve: Interval(
0,
0.2,
curve: Curves.easeInOutCubicEmphasized.flipped,
),
);
}
class OffsetAnimation extends CurvedAnimation {
OffsetAnimation(Animation<double> parent)
: super(
parent: parent,
curve: const Interval(
0.4,
1.0,
curve: Curves.easeInOutCubicEmphasized,
),
reverseCurve: Interval(
0,
0.2,
curve: Curves.easeInOutCubicEmphasized.flipped,
),
);
}
class RailTransition extends StatefulWidget {
const RailTransition(
{super.key,
required this.animation,
required this.backgroundColor,
required this.child});
final Animation<double> animation;
final Widget child;
final Color backgroundColor;
@override
State<RailTransition> createState() => _RailTransition();
}
class _RailTransition extends State<RailTransition> {
late Animation<Offset> offsetAnimation;
late Animation<double> widthAnimation;
@override
void didChangeDependencies() {
super.didChangeDependencies();
// The animations are only rebuilt by this method when the text
// direction changes because this widget only depends on Directionality.
final bool ltr = Directionality.of(context) == TextDirection.ltr;
widthAnimation = Tween<double>(
begin: 0,
end: 1,
).animate(SizeAnimation(widget.animation));
offsetAnimation = Tween<Offset>(
begin: ltr ? const Offset(-1, 0) : const Offset(1, 0),
end: Offset.zero,
).animate(OffsetAnimation(widget.animation));
}
@override
Widget build(BuildContext context) {
return ClipRect(
child: DecoratedBox(
decoration: BoxDecoration(color: widget.backgroundColor),
child: Align(
alignment: Alignment.topLeft,
widthFactor: widthAnimation.value,
child: FractionalTranslation(
translation: offsetAnimation.value,
child: widget.child,
),
),
),
);
}
}
class BarTransition extends StatefulWidget {
const BarTransition(
{super.key,
required this.animation,
required this.backgroundColor,
required this.child});
final Animation<double> animation;
final Color backgroundColor;
final Widget child;
@override
State<BarTransition> createState() => _BarTransition();
}
class _BarTransition extends State<BarTransition> {
late final Animation<Offset> offsetAnimation;
late final Animation<double> heightAnimation;
@override
void initState() {
super.initState();
offsetAnimation = Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(OffsetAnimation(widget.animation));
heightAnimation = Tween<double>(
begin: 0,
end: 1,
).animate(SizeAnimation(widget.animation));
}
@override
Widget build(BuildContext context) {
return ClipRect(
child: DecoratedBox(
decoration: BoxDecoration(color: widget.backgroundColor),
child: Align(
alignment: Alignment.topLeft,
heightFactor: heightAnimation.value,
child: FractionalTranslation(
translation: offsetAnimation.value,
child: widget.child,
),
),
),
);
}
}
class OneTwoTransition extends StatefulWidget {
const OneTwoTransition({
super.key,
required this.animation,
required this.one,
required this.two,
});
final Animation<double> animation;
final Widget one;
final Widget two;
@override
State<OneTwoTransition> createState() => _OneTwoTransitionState();
}
class _OneTwoTransitionState extends State<OneTwoTransition> {
late final Animation<Offset> offsetAnimation;
late final Animation<double> widthAnimation;
@override
void initState() {
super.initState();
offsetAnimation = Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(OffsetAnimation(widget.animation));
widthAnimation = Tween<double>(
begin: 0,
end: 1000,
).animate(SizeAnimation(widget.animation));
}
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Flexible(
flex: 1000,
child: widget.one,
),
if (widthAnimation.value.toInt() > 0) ...[
Flexible(
flex: widthAnimation.value.toInt(),
child: FractionalTranslation(
translation: offsetAnimation.value,
child: widget.two,
),
)
],
],
);
}
}

View File

@@ -17,26 +17,26 @@ class TypographyScreen extends StatelessWidget {
children: <Widget>[
const SizedBox(height: 7),
TextStyleExample(
name: "Display Large", style: textTheme.displayLarge!),
name: 'Display Large', style: textTheme.displayLarge!),
TextStyleExample(
name: "Display Medium", style: textTheme.displayMedium!),
name: 'Display Medium', style: textTheme.displayMedium!),
TextStyleExample(
name: "Display Small", style: textTheme.displaySmall!),
name: 'Display Small', style: textTheme.displaySmall!),
TextStyleExample(
name: "Headline Large", style: textTheme.headlineLarge!),
name: 'Headline Large', style: textTheme.headlineLarge!),
TextStyleExample(
name: "Headline Medium", style: textTheme.headlineMedium!),
name: 'Headline Medium', style: textTheme.headlineMedium!),
TextStyleExample(
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!),
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!),
],
),
);

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
@@ -235,6 +235,7 @@
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -344,7 +345,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -423,7 +424,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@@ -470,7 +471,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;

View File

@@ -8,7 +8,7 @@ publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.17.0-0 <3.0.0"
sdk: ">=2.18.0-0 <3.0.0"
dependencies:
flutter:

View File

@@ -16,46 +16,61 @@ void main() {
'on NavigationBar', (tester) async {
widgetSetup(tester, 449);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(const Material3Demo());
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
expect(find.text("Light Theme"), findsNothing);
expect(find.text("Dark Theme"), findsNothing);
expect(find.text('Light Theme'), findsNothing);
expect(find.text('Dark Theme'), findsNothing);
expect(find.byType(NavigationBar), findsOneWidget);
Finder colorIconOnBar = find.byIcon(Icons.format_paint_outlined);
Finder colorIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination, Icons.format_paint_outlined));
expect(colorIconOnBar, findsOneWidget);
await tester.tap(colorIconOnBar);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(colorIconOnBar, findsNothing);
expect(find.byIcon(Icons.format_paint), findsOneWidget);
expect(find.text("Light Theme"), findsOneWidget);
expect(find.text("Dark Theme"), findsOneWidget);
Finder selectedColorIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching:
find.widgetWithIcon(NavigationDestination, Icons.format_paint));
expect(selectedColorIconOnBar, findsOneWidget);
expect(find.text('Light Theme'), findsOneWidget);
expect(find.text('Dark Theme'), findsOneWidget);
});
testWidgets(
'Color palettes screen shows correctly when color icon is clicked '
'on NavigationRail', (tester) async {
widgetSetup(tester, 450); // NavigationRail shows only when width is >= 450.
widgetSetup(
tester, 1200); // NavigationRail shows only when width is > 1000.
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(const Material3Demo());
expect(find.text("Light Theme"), findsNothing);
expect(find.text("Dark Theme"), findsNothing);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
await tester.pumpAndSettle();
expect(find.text('Light Theme'), findsNothing);
expect(find.text('Dark Theme'), findsNothing);
expect(find.byType(NavigationRail), findsOneWidget);
Finder colorIconOnRail = find.byIcon(Icons.format_paint_outlined);
Finder colorIconOnRail = find.descendant(
of: find.byType(NavigationRail),
matching: find.byIcon(Icons.format_paint_outlined));
expect(colorIconOnRail, findsOneWidget);
await tester.tap(colorIconOnRail);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(colorIconOnRail, findsNothing);
expect(find.byIcon(Icons.format_paint), findsOneWidget);
expect(find.text("Light Theme"), findsOneWidget);
expect(find.text("Dark Theme"), findsOneWidget);
Finder selectedColorIconOnRail = find.descendant(
of: find.byType(NavigationRail),
matching: find.byIcon(Icons.format_paint));
expect(selectedColorIconOnRail, findsOneWidget);
expect(find.text('Light Theme'), findsOneWidget);
expect(find.text('Dark Theme'), findsOneWidget);
});
testWidgets('Color screen shows correct content', (tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(body: Row(children: const [ColorPalettesScreen()])),
));
expect(find.text("Light Theme"), findsOneWidget);
expect(find.text("Dark Theme"), findsOneWidget);
expect(find.text('Light Theme'), findsOneWidget);
expect(find.text('Dark Theme'), findsOneWidget);
expect(find.byType(ColorGroup, skipOffstage: false), findsNWidgets(14));
});
}

View File

@@ -10,186 +10,216 @@ import 'package:material_3_demo/main.dart';
void main() {
testWidgets('Default main page shows all M3 components', (tester) async {
await tester.pumpWidget(const Material3Demo());
widgetSetup(tester, 800, windowHeight: 7000);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
// Elements on the app bar
expect(find.text('Material 3'), findsOneWidget);
expect(find.widgetWithIcon(IconButton, Icons.wb_sunny_outlined),
findsOneWidget);
expect(find.widgetWithIcon(IconButton, Icons.filter_3), findsOneWidget);
expect(find.widgetWithIcon(IconButton, Icons.more_vert), findsOneWidget);
expect(
find.widgetWithIcon(AppBar, Icons.dark_mode_outlined), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.filter_2), findsOneWidget);
expect(find.widgetWithIcon(AppBar, Icons.palette_outlined), findsOneWidget);
// Elements on the component screen
// Buttons
expect(find.widgetWithText(ElevatedButton, "Elevated"), findsNWidgets(2));
expect(find.widgetWithText(ElevatedButton, "Filled"), findsNWidgets(2));
expect(
find.widgetWithText(ElevatedButton, "Filled Tonal"), findsNWidgets(2));
expect(find.widgetWithText(OutlinedButton, "Outlined"), findsNWidgets(2));
expect(find.widgetWithText(TextButton, "Text"), findsNWidgets(2));
expect(find.text("Icon"), findsNWidgets(5));
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(TextButton, 'Text'), findsNWidgets(2));
expect(find.widgetWithText(Buttons, 'Icon'), findsNWidgets(5));
// IconButtons
expect(find.byType(IconToggleButton), findsNWidgets(8));
// FABs
Finder floatingActionButton = find.text("Create");
await tester.scrollUntilVisible(
floatingActionButton,
500.0,
);
expect(
find.widgetWithIcon(FloatingActionButton, Icons.add), findsNWidgets(4));
expect(find.widgetWithText(FloatingActionButton, "Create"), findsOneWidget);
expect(find.byType(FloatingActionButton),
findsNWidgets(5)); // 2 more shows up in the bottom app bar.
expect(find.widgetWithText(FloatingActionButton, 'Create'), findsOneWidget);
// Chips
expect(find.byType(ActionChip),
findsNWidgets(4)); // includes Assist and Suggestion chip.
expect(find.byType(FilterChip), findsNWidgets(2));
expect(find.byType(InputChip), findsNWidgets(2));
// Cards
expect(find.widgetWithText(Card, "Elevated"), findsOneWidget);
expect(find.widgetWithText(Card, "Filled"), findsOneWidget);
expect(find.widgetWithText(Card, "Outlined"), findsOneWidget);
expect(find.widgetWithText(Cards, 'Elevated'), findsOneWidget);
expect(find.widgetWithText(Cards, 'Filled'), findsOneWidget);
expect(find.widgetWithText(Cards, 'Outlined'), findsOneWidget);
// TextFields
expect(find.widgetWithText(TextField, 'Disabled'), findsNWidgets(2));
expect(find.widgetWithText(TextField, 'Filled'), findsNWidgets(2));
expect(find.widgetWithText(TextField, 'Outlined'), findsNWidgets(2));
// Alert Dialog
Finder dialogExample = find.widgetWithText(TextButton, "Open Dialog");
await tester.scrollUntilVisible(
dialogExample,
500.0,
);
Finder dialogExample = find.widgetWithText(TextButton, 'Show dialog');
expect(dialogExample, findsOneWidget);
// Switches
Finder switchExample = find.byType(Switch);
expect(switchExample, findsNWidgets(4));
// Checkboxes
Finder checkboxExample = find.byType(CheckboxListTile);
expect(checkboxExample, findsNWidgets(4));
// Radios
// TODO(guidezpl): Figure out why this isn't working
// Finder radioExample = find.byType(RadioListTile<Value>);
// expect(radioExample, findsNWidgets(4));
// ProgressIndicator
Finder circularProgressIndicator = find.byType(CircularProgressIndicator);
expect(circularProgressIndicator, findsOneWidget);
Finder linearProgressIndicator = find.byType(LinearProgressIndicator);
expect(linearProgressIndicator, findsOneWidget);
});
testWidgets(
'NavigationRail doesn\'t show when width value is small than 450 '
'NavigationRail doesn\'t show when width value is small than 1000 '
'(in Portrait mode or narrow screen)', (tester) async {
widgetSetup(tester, 449);
await tester.pumpWidget(const Material3Demo());
widgetSetup(tester, 999, windowHeight: 7000);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
await tester.pumpAndSettle();
// When screen width is less than 450, NavigationBar will show. At the same
// time, the NavigationRail and the NavigationBar example will NOT show.
expect(find.byType(NavigationBars), findsOneWidget);
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.byType(NavigationRailSection), findsNothing);
expect(find.widgetWithText(NavigationBar, "Explore"), findsNothing);
expect(find.widgetWithText(NavigationBar, "Pets"), findsNothing);
expect(find.widgetWithText(NavigationBar, "Account"), findsNothing);
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 450 (in Landscape mode or wider screen)', (tester) async {
widgetSetup(tester, 450);
await tester.pumpWidget(const Material3Demo());
'to 1000 (in Landscape mode or wider screen)', (tester) async {
widgetSetup(tester, 1001, windowHeight: 3000);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
await tester.pumpAndSettle();
// When screen width is greater than or equal to 450, NavigationRail and
// NavigationBar example will show. At the same time, the NavigationBar
// will NOT show.
expect(find.byType(NavigationRailSection), 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), findsOneWidget);
expect(find.byType(Tooltip, skipOffstage: false), findsWidgets);
expect(find.widgetWithText(NavigationRailSection, "Components"),
findsOneWidget);
expect(find.widgetWithText(NavigationRailSection, "Color"), findsOneWidget);
expect(find.widgetWithText(NavigationRailSection, "Typography"),
findsOneWidget);
expect(find.widgetWithText(NavigationRailSection, "Elevation"),
findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Components'), findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Color'), findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Typography'), findsOneWidget);
expect(find.widgetWithText(NavigationRail, 'Elevation'), findsOneWidget);
final navbarExample = find.byType(NavigationBars);
await tester.scrollUntilVisible(
navbarExample,
500.0,
);
expect(find.byType(NavigationBars), 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);
expect(find.widgetWithText(NavigationBar, "Components"), findsNothing);
expect(find.widgetWithText(NavigationBar, "Colors"), findsNothing);
expect(find.widgetWithText(NavigationBar, "Typography"), findsNothing);
expect(find.widgetWithText(NavigationBar, "Elevation"), findsNothing);
// 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 {
await tester.pumpWidget(const Material3Demo());
Finder m3Icon = find.widgetWithIcon(IconButton, Icons.filter_3);
Finder m2Icon = find.widgetWithIcon(IconButton, Icons.filter_2);
widgetSetup(tester, 450, windowHeight: 7000);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
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.byType(Card));
Finder dialog = find.text("Open Dialog");
await tester.scrollUntilVisible(
dialog,
500.0,
);
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("Dismiss");
await tester.scrollUntilVisible(
dismiss,
500.0,
);
Finder dismiss = find.text('Okay');
await tester.tap(dismiss);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(m3Icon, findsOneWidget);
expect(m2Icon, findsNothing);
expect(find.text("Material 3"), findsOneWidget);
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);
await tester.tap(m3Icon);
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));
Finder updatedDialog = find.text("Open Dialog");
await tester.scrollUntilVisible(
updatedDialog,
500.0,
);
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.scrollUntilVisible(
updatedDismiss,
500.0,
);
Finder updatedDismiss = find.text('Dismiss');
await tester.tap(updatedDismiss);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(m3Icon, findsNothing);
expect(m2Icon, findsOneWidget);
expect(find.text("Material 2"), findsOneWidget);
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 Material3Demo());
await tester.tap(find.widgetWithIcon(IconButton, Icons.filter_3));
await tester.tap(find.byIcon(Icons.format_paint_outlined));
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
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 Theme"));
BuildContext lightThemeText = tester.element(find.text('Light Theme'));
expect(Theme.of(lightThemeText).useMaterial3, false);
await tester.tap(find.byIcon(Icons.text_snippet_outlined));
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"));
BuildContext displayLargeText = tester.element(find.text('Display Large'));
expect(Theme.of(displayLargeText).useMaterial3, false);
await tester.tap(find.byIcon(Icons.invert_colors_on_outlined));
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);
@@ -198,25 +228,30 @@ void main() {
testWidgets(
'Brightness mode switches between dark and light when'
'the brightness icon is clicked', (tester) async {
await tester.pumpWidget(const Material3Demo());
Finder lightIcon = find.widgetWithIcon(IconButton, Icons.wb_sunny_outlined);
Finder darkIcon = find.widgetWithIcon(IconButton, Icons.wb_sunny);
BuildContext appBar = tester.element(find.byType(AppBar));
BuildContext body = tester.element(find.byType(Scaffold));
BuildContext navigationRail = tester.element(find.byType(NavigationRail));
expect(lightIcon, findsOneWidget);
expect(darkIcon, findsNothing);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
Finder lightIcon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.light_mode_outlined));
Finder darkIcon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.dark_mode_outlined));
BuildContext appBar = tester.element(find.byType(AppBar).first);
BuildContext body = tester.firstElement(find.byType(Scaffold).first);
BuildContext navigationRail = tester.element(
find.widgetWithIcon(NavigationRail, Icons.format_paint_outlined));
expect(darkIcon, findsOneWidget);
expect(lightIcon, findsNothing);
expect(Theme.of(appBar).brightness, Brightness.light);
expect(Theme.of(body).brightness, Brightness.light);
expect(Theme.of(navigationRail).brightness, Brightness.light);
await tester.tap(lightIcon);
await tester.tap(darkIcon);
await tester.pumpAndSettle(const Duration(microseconds: 500));
BuildContext appBar2 = tester.element(find.byType(AppBar));
BuildContext body2 = tester.element(find.byType(Scaffold));
BuildContext appBar2 = tester.element(find.byType(AppBar).first);
BuildContext body2 = tester.element(find.byType(Scaffold).first);
BuildContext navigationRail2 = tester.element(find.byType(NavigationRail));
expect(lightIcon, findsNothing);
expect(darkIcon, findsOneWidget);
expect(darkIcon, findsNothing);
expect(lightIcon, findsOneWidget);
expect(Theme.of(appBar2).brightness, Brightness.dark);
expect(Theme.of(body2).brightness, Brightness.dark);
expect(Theme.of(navigationRail2).brightness, Brightness.dark);
@@ -224,10 +259,15 @@ void main() {
testWidgets('Color theme changes when a color is selected from menu',
(tester) async {
await tester.pumpWidget(const Material3Demo());
Finder menuIcon = find.widgetWithIcon(IconButton, Icons.more_vert);
BuildContext appBar = tester.element(find.byType(AppBar));
BuildContext body = tester.element(find.byType(Scaffold));
Color m3BaseColor = const Color(0xff6750a4);
await tester.pumpWidget(Container());
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
await tester.pump();
Finder menuIcon = find.descendant(
of: find.byType(AppBar),
matching: find.widgetWithIcon(IconButton, Icons.palette_outlined));
BuildContext appBar = tester.element(find.byType(AppBar).first);
BuildContext body = tester.element(find.byType(Scaffold).first);
BuildContext navigationRail = tester.element(find.byType(NavigationRail));
expect(Theme.of(appBar).primaryColor, m3BaseColor);
@@ -235,11 +275,11 @@ void main() {
expect(Theme.of(navigationRail).primaryColor, m3BaseColor);
await tester.tap(menuIcon);
await tester.pumpAndSettle();
await tester.tap(find.text("Blue"));
await tester.tap(find.text('Blue').last);
await tester.pumpAndSettle();
BuildContext appBar2 = tester.element(find.byType(AppBar));
BuildContext body2 = tester.element(find.byType(Scaffold));
BuildContext appBar2 = tester.element(find.byType(AppBar).first);
BuildContext body2 = tester.element(find.byType(Scaffold).first);
BuildContext navigationRail2 = tester.element(find.byType(NavigationRail));
ThemeData expectedTheme = ThemeData(colorSchemeSeed: Colors.blue);
expect(Theme.of(appBar2).primaryColor, expectedTheme.primaryColor);
@@ -248,9 +288,11 @@ void main() {
});
}
void widgetSetup(WidgetTester tester, double width) {
const height = 846;
void widgetSetup(WidgetTester tester, double windowWidth,
{double? windowHeight}) {
final height = windowHeight ?? 846;
tester.binding.window.devicePixelRatioTestValue = (2);
final dpi = tester.binding.window.devicePixelRatio;
tester.binding.window.physicalSizeTestValue = Size(width * dpi, height * dpi);
tester.binding.window.physicalSizeTestValue =
Size(windowWidth * dpi, height * dpi);
}

View File

@@ -16,43 +16,54 @@ void main() {
'selected on NavigationBar', (tester) async {
widgetSetup(tester, 449);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(const Material3Demo());
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
expect(find.text("Surface Tint only"), findsNothing);
expect(find.text('Surface Tint Color Only'), findsNothing);
expect(find.byType(NavigationBar), findsOneWidget);
Finder tintIconOnBar = find.byIcon(Icons.invert_colors_on_outlined);
Finder tintIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(
NavigationDestination, Icons.invert_colors_on_outlined));
expect(tintIconOnBar, findsOneWidget);
await tester.tap(tintIconOnBar);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(tintIconOnBar, findsNothing);
expect(find.byIcon(Icons.opacity), findsOneWidget);
expect(find.text("Surface Tint only"), findsOneWidget);
Finder selectedTintIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching: find.widgetWithIcon(NavigationDestination, Icons.opacity));
expect(selectedTintIconOnBar, findsOneWidget);
expect(find.text('Surface Tint Color Only'), findsOneWidget);
});
testWidgets(
'Surface Tones screen shows correctly when the corresponding icon is '
'selected on NavigationRail', (tester) async {
widgetSetup(tester, 450); // NavigationRail shows only when width is >= 450.
widgetSetup(
tester, 1200); // NavigationRail shows only when width is > 1000.
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(const Material3Demo());
expect(find.text("Surface Tint only"), findsNothing);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
expect(find.text('Surface Tint Color Only'), findsNothing);
expect(find.byType(NavigationRail), findsOneWidget);
Finder tintIconOnRail = find.byIcon(Icons.invert_colors_on_outlined);
Finder tintIconOnRail = find.descendant(
of: find.byType(NavigationRail),
matching: find.byIcon(Icons.invert_colors_on_outlined));
expect(tintIconOnRail, findsOneWidget);
await tester.tap(tintIconOnRail);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(tintIconOnRail, findsNothing);
expect(find.byIcon(Icons.opacity), findsOneWidget);
expect(find.text("Surface Tint only"), findsOneWidget);
Finder selectedTintIconOnRail = find.descendant(
of: find.byType(NavigationRail), matching: find.byIcon(Icons.opacity));
expect(selectedTintIconOnRail, findsOneWidget);
expect(find.text('Surface Tint Color Only'), findsOneWidget);
});
testWidgets('Surface Tones screen shows correct content', (tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(body: Row(children: const [ElevationScreen()])),
));
expect(find.text("Surface Tint only"), findsOneWidget);
expect(find.text("Surface Tint and Shadow"), findsOneWidget);
expect(find.text("Shadow only"), findsOneWidget);
expect(find.text('Surface Tint Color Only'), 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));
});

View File

@@ -16,59 +16,70 @@ void main() {
'selected on NavigationBar', (tester) async {
widgetSetup(tester, 449);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(const Material3Demo());
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
expect(find.text("Display Large"), findsNothing);
expect(find.text('Display Large'), findsNothing);
expect(find.byType(NavigationBar), findsOneWidget);
Finder textIconOnBar = find.byIcon(Icons.text_snippet_outlined);
Finder textIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching: find.byIcon(Icons.text_snippet_outlined));
expect(textIconOnBar, findsOneWidget);
await tester.tap(textIconOnBar);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(textIconOnBar, findsNothing);
expect(find.byIcon(Icons.text_snippet), findsOneWidget);
expect(find.text("Display Large"), findsOneWidget);
Finder selectedTextIconOnBar = find.descendant(
of: find.byType(NavigationBar),
matching: find.byIcon(Icons.text_snippet));
expect(selectedTextIconOnBar, findsOneWidget);
expect(find.text('Display Large'), findsOneWidget);
});
testWidgets(
'Typography screen shows correctly when the corresponding icon is '
'selected on NavigationRail', (tester) async {
widgetSetup(tester, 450); // NavigationRail shows only when width is >= 450.
widgetSetup(
tester, 1200); // NavigationRail shows only when width is > 1000.
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(const Material3Demo());
expect(find.text("Display Large"), findsNothing);
await tester.pumpWidget(const MaterialApp(home: Material3Demo()));
expect(find.text('Display Large'), findsNothing);
expect(find.byType(NavigationRail), findsOneWidget);
Finder textIconOnRail = find.byIcon(Icons.text_snippet_outlined);
Finder textIconOnRail = find.descendant(
of: find.byType(NavigationRail),
matching: find.byIcon(Icons.text_snippet_outlined));
expect(textIconOnRail, findsOneWidget);
await tester.tap(textIconOnRail);
await tester.pumpAndSettle(const Duration(microseconds: 500));
expect(textIconOnRail, findsNothing);
expect(find.byIcon(Icons.text_snippet), findsOneWidget);
expect(find.text("Display Large"), findsOneWidget);
Finder selectedTextIconOnRail = find.descendant(
of: find.byType(NavigationRail),
matching: find.byIcon(Icons.text_snippet));
expect(selectedTextIconOnRail, findsOneWidget);
expect(find.text('Display Large'), findsOneWidget);
});
testWidgets('Typography screen shows correct content', (tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(body: Row(children: const [TypographyScreen()])),
));
expect(find.text("Display Large"), findsOneWidget);
expect(find.text("Display Medium"), findsOneWidget);
expect(find.text("Display Small"), findsOneWidget);
expect(find.text("Headline Large"), findsOneWidget);
expect(find.text("Headline Medium"), findsOneWidget);
expect(find.text("Headline Small"), findsOneWidget);
expect(find.text("Title Large"), findsOneWidget);
expect(find.text("Title Medium"), findsOneWidget);
expect(find.text("Title Small"), findsOneWidget);
expect(find.text('Display Large'), findsOneWidget);
expect(find.text('Display Medium'), findsOneWidget);
expect(find.text('Display Small'), findsOneWidget);
expect(find.text('Headline Large'), findsOneWidget);
expect(find.text('Headline Medium'), findsOneWidget);
expect(find.text('Headline Small'), findsOneWidget);
expect(find.text('Title Large'), findsOneWidget);
expect(find.text('Title Medium'), findsOneWidget);
expect(find.text('Title Small'), findsOneWidget);
await tester.scrollUntilVisible(
find.text("Body Small"),
find.text('Body Small'),
500.0,
);
expect(find.text("Label Large"), findsOneWidget);
expect(find.text("Label Medium"), findsOneWidget);
expect(find.text("Label Small"), findsOneWidget);
expect(find.text("Body Large"), findsOneWidget);
expect(find.text("Body Medium"), findsOneWidget);
expect(find.text("Body Small"), findsOneWidget);
expect(find.text('Label Large'), findsOneWidget);
expect(find.text('Label Medium'), findsOneWidget);
expect(find.text('Label Small'), findsOneWidget);
expect(find.text('Body Large'), findsOneWidget);
expect(find.text('Body Medium'), findsOneWidget);
expect(find.text('Body Small'), findsOneWidget);
expect(find.byType(TextStyleExample), findsNWidgets(15));
});