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

Added M3 demo app (#1189)

* Added M3 demo app

* Changed to use the centralized lint configuration.

* Moved the app at the root level

* Added to CI

* Added app to beta CI and commented app on stable CI file

* Update README.md

* Addressed comments

* fixed comments

* Reverted app name in CI files.

* Added project to the Pub Dependabot config

* Linting pass for current beta channel standards

Co-authored-by: Qun Cheng <quncheng@google.com>
Co-authored-by: Craig Labenz <craig.labenz@gmail.com>
This commit is contained in:
Qun Cheng
2022-05-06 08:29:31 -07:00
committed by GitHub
parent 9fd13b507c
commit 6694b00dae
127 changed files with 5578 additions and 0 deletions

View File

@@ -0,0 +1,295 @@
import 'package:flutter/material.dart';
const Widget divider = SizedBox(height: 10);
// If screen content width is greater or equal to this value, the light and dark
// color schemes will be displayed in a column. Otherwise, they will
// be displayed in a row.
const double narrowScreenWidthThreshold = 400;
class ColorPalettesScreen extends StatelessWidget {
const ColorPalettesScreen({Key? key}) : super(key: key);
@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);
Widget schemeLabel(String brightness) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Text(
brightness,
style: const TextStyle(fontWeight: FontWeight.bold),
),
);
}
Widget schemeView(ThemeData theme) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ColorSchemeView(
colorScheme: theme.colorScheme,
),
);
}
return Expanded(
child: LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth < narrowScreenWidthThreshold) {
return SingleChildScrollView(
child: Column(
children: [
divider,
schemeLabel("Light Theme"),
schemeView(lightTheme),
divider,
divider,
schemeLabel("Dark Theme"),
schemeView(darkTheme)
],
),
);
} else {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 5),
child: Row(
children: [
Expanded(
child: Column(
children: [
schemeLabel("Light Theme"),
schemeView(lightTheme)
],
),
),
Expanded(
child: Column(
children: [
schemeLabel("Dark Theme"),
schemeView(darkTheme)
],
),
)
],
),
),
);
}
}),
);
}
}
class ColorSchemeView extends StatelessWidget {
const ColorSchemeView({Key? key, required this.colorScheme})
: super(key: key);
final ColorScheme colorScheme;
@override
Widget build(BuildContext context) {
return Column(
children: [
ColorGroup(children: [
ColorChip(
label: "primary",
color: colorScheme.primary,
onColor: colorScheme.onPrimary,
),
ColorChip(
label: "onPrimary",
color: colorScheme.onPrimary,
onColor: colorScheme.primary),
ColorChip(
label: "primaryContainer",
color: colorScheme.primaryContainer,
onColor: colorScheme.onPrimaryContainer,
),
ColorChip(
label: "onPrimaryContainer",
color: colorScheme.onPrimaryContainer,
onColor: colorScheme.primaryContainer,
)
]),
divider,
ColorGroup(children: [
ColorChip(
label: "secondary",
color: colorScheme.secondary,
onColor: colorScheme.onSecondary,
),
ColorChip(
label: "onSecondary",
color: colorScheme.onSecondary,
onColor: colorScheme.secondary,
),
ColorChip(
label: "secondaryContainer",
color: colorScheme.secondaryContainer,
onColor: colorScheme.onSecondaryContainer,
),
ColorChip(
label: "onSecondaryContainer",
color: colorScheme.onSecondaryContainer,
onColor: colorScheme.secondaryContainer)
]),
divider,
ColorGroup(
children: [
ColorChip(
label: 'tertiary',
color: colorScheme.tertiary,
onColor: colorScheme.onTertiary),
ColorChip(
label: 'onTertiary',
color: colorScheme.onTertiary,
onColor: colorScheme.tertiary),
ColorChip(
label: 'tertiaryContainer',
color: colorScheme.tertiaryContainer,
onColor: colorScheme.onTertiaryContainer),
ColorChip(
label: 'onTertiaryContainer',
color: colorScheme.onTertiaryContainer,
onColor: colorScheme.tertiaryContainer),
],
),
divider,
ColorGroup(
children: [
ColorChip(
label: 'error',
color: colorScheme.error,
onColor: colorScheme.onError),
ColorChip(
label: 'onError',
color: colorScheme.onError,
onColor: colorScheme.error),
ColorChip(
label: 'errorContainer',
color: colorScheme.errorContainer,
onColor: colorScheme.onErrorContainer),
ColorChip(
label: 'onErrorContainer',
color: colorScheme.onErrorContainer,
onColor: colorScheme.errorContainer),
],
),
divider,
ColorGroup(
children: [
ColorChip(
label: 'background',
color: colorScheme.background,
onColor: colorScheme.onBackground),
ColorChip(
label: 'onBackground',
color: colorScheme.onBackground,
onColor: colorScheme.background),
],
),
divider,
ColorGroup(
children: [
ColorChip(
label: 'surface',
color: colorScheme.surface,
onColor: colorScheme.onSurface),
ColorChip(
label: 'onSurface',
color: colorScheme.onSurface,
onColor: colorScheme.surface),
ColorChip(
label: 'surfaceVariant',
color: colorScheme.surfaceVariant,
onColor: colorScheme.onSurfaceVariant),
ColorChip(
label: 'onSurfaceVariant',
color: colorScheme.onSurfaceVariant,
onColor: colorScheme.surfaceVariant),
],
),
divider,
ColorGroup(
children: [
ColorChip(label: 'outline', color: colorScheme.outline),
ColorChip(label: 'shadow', color: colorScheme.shadow),
ColorChip(
label: 'inverseSurface',
color: colorScheme.inverseSurface,
onColor: colorScheme.onInverseSurface),
ColorChip(
label: 'onInverseSurface',
color: colorScheme.onInverseSurface,
onColor: colorScheme.inverseSurface),
ColorChip(
label: 'inversePrimary',
color: colorScheme.inversePrimary,
onColor: colorScheme.primary),
],
),
],
);
}
}
class ColorGroup extends StatelessWidget {
const ColorGroup({Key? key, required this.children}) : super(key: key);
final List<Widget> children;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: children,
),
);
}
}
class ColorChip extends StatelessWidget {
const ColorChip({
Key? key,
required this.color,
required this.label,
this.onColor,
}) : super(key: key);
final Color color;
final Color? onColor;
final String label;
static Color contrastColor(Color color) {
final brightness = ThemeData.estimateBrightnessForColor(color);
switch (brightness) {
case Brightness.dark:
return Colors.white;
case Brightness.light:
return Colors.black;
}
}
@override
Widget build(BuildContext context) {
final Color labelColor = onColor ?? contrastColor(color);
return Container(
color: color,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(child: Text(label, style: TextStyle(color: labelColor))),
],
),
)
);
}
}

View File

@@ -0,0 +1,509 @@
import 'package:flutter/material.dart';
class ComponentScreen extends StatelessWidget {
const ComponentScreen({Key? key, required this.showNavBottomBar})
: super(key: key);
final bool showNavBottomBar;
@override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: ListView(
shrinkWrap: true,
children: [
_colDivider,
_colDivider,
const Buttons(),
_colDivider,
const FloatingActionButtons(),
_colDivider,
const Cards(),
_colDivider,
const Dialogs(),
_colDivider,
showNavBottomBar
? const NavigationBars(
selectedIndex: 0,
isExampleBar: true,
)
: Container(),
],
),
),
);
}
}
const _rowDivider = SizedBox(width: 10);
const _colDivider = SizedBox(height: 10);
const double _cardWidth = 115;
void Function()? handlePressed(BuildContext context, bool isDisabled, String buttonName) {
return isDisabled
? null
: () {
final snackBar = SnackBar(
content: Text(
'Yay! $buttonName is clicked!',
style: TextStyle(color: Theme.of(context).colorScheme.surface),
),
action: SnackBarAction(
textColor: Theme.of(context).colorScheme.surface,
label: 'Close',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
};
}
class Buttons extends StatefulWidget {
const Buttons({Key? key}) : super(key: key);
@override
State<Buttons> createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> {
@override
Widget build(BuildContext context) {
return Wrap(
alignment: WrapAlignment.spaceEvenly,
children: const <Widget>[
ButtonsWithoutIcon(isDisabled: false),
_rowDivider,
ButtonsWithIcon(),
_rowDivider,
ButtonsWithoutIcon(isDisabled: true),
],
);
}
}
class ButtonsWithoutIcon extends StatelessWidget {
final bool isDisabled;
const ButtonsWithoutIcon({Key? key, required this.isDisabled})
: super(key: key);
@override
Widget build(BuildContext context) {
return IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ElevatedButton(
onPressed: handlePressed(context, isDisabled, "ElevatedButton"),
child: const Text("Elevated"),
),
_colDivider,
ElevatedButton(
style: ElevatedButton.styleFrom(
// Foreground color
onPrimary: Theme.of(context).colorScheme.onPrimary,
// Background color
primary: Theme.of(context).colorScheme.primary,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: handlePressed(context, isDisabled, "FilledButton"),
child: const Text('Filled'),
),
_colDivider,
ElevatedButton(
style: ElevatedButton.styleFrom(
// Foreground color
onPrimary: Theme.of(context).colorScheme.onSecondaryContainer,
// Background color
primary: Theme.of(context).colorScheme.secondaryContainer,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: handlePressed(context, isDisabled, "FilledTonalButton"),
child: const Text('Filled Tonal'),
),
_colDivider,
OutlinedButton(
onPressed: handlePressed(context, isDisabled, "OutlinedButton"),
child: const Text("Outlined"),
),
_colDivider,
TextButton(
onPressed: handlePressed(context, isDisabled, "TextButton"),
child: const Text("Text")),
],
),
);
}
}
class ButtonsWithIcon extends StatelessWidget {
const ButtonsWithIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ElevatedButton.icon(
onPressed:
handlePressed(context, false, "ElevatedButton with Icon"),
icon: const Icon(Icons.add),
label: const Text("Icon"),
),
_colDivider,
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
// Foreground color
onPrimary: Theme.of(context).colorScheme.onPrimary,
// Background color
primary: Theme.of(context).colorScheme.primary,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: handlePressed(context, false, "FilledButton with Icon"),
label: const Text('Icon'),
icon: const Icon(Icons.add),
),
_colDivider,
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
// Foreground color
onPrimary: Theme.of(context).colorScheme.onSecondaryContainer,
// Background color
primary: Theme.of(context).colorScheme.secondaryContainer,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed:
handlePressed(context, false, "FilledTonalButton with Icon"),
label: const Text('Icon'),
icon: const Icon(Icons.add),
),
_colDivider,
OutlinedButton.icon(
onPressed:
handlePressed(context, false, "OutlinedButton with Icon"),
icon: const Icon(Icons.add),
label: const Text("Icon"),
),
_colDivider,
TextButton.icon(
onPressed: handlePressed(context, false, "TextButton with Icon"),
icon: const Icon(Icons.add),
label: const Text("Icon"),
)
],
),
);
}
}
class FloatingActionButtons extends StatelessWidget {
const FloatingActionButtons({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
FloatingActionButton.small(
onPressed: () {},
child: const Icon(Icons.add),
),
_rowDivider,
FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
_rowDivider,
FloatingActionButton.extended(
onPressed: () {},
icon: const Icon(Icons.add),
label: const Text("Create"),
),
_rowDivider,
FloatingActionButton.large(
onPressed: () {},
child: const Icon(Icons.add),
),
],
),
);
}
}
class Cards extends StatelessWidget {
const Cards({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
children: [
SizedBox(
width: _cardWidth,
child: Card(
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
children: const [
Align(
alignment: Alignment.topRight,
child: Icon(Icons.more_vert),
),
_colDivider,
_colDivider,
Align(
alignment: Alignment.bottomLeft,
child: Text("Elevated"),
)
],
),
),
),
),
SizedBox(
width: _cardWidth,
child: Card(
color: Theme.of(context).colorScheme.surfaceVariant,
elevation: 0,
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
children: const [
Align(
alignment: Alignment.topRight,
child: Icon(Icons.more_vert),
),
_colDivider,
_colDivider,
Align(
alignment: Alignment.bottomLeft,
child: Text("Filled"),
)
],
),
),
),
),
SizedBox(
width: _cardWidth,
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).colorScheme.outline,
),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
children: const [
Align(
alignment: Alignment.topRight,
child: Icon(Icons.more_vert),
),
_colDivider,
_colDivider,
Align(
alignment: Alignment.bottomLeft,
child: Text("Outlined"),
)
],
),
),
),
),
],
),
);
}
}
class Dialogs extends StatefulWidget {
const Dialogs({Key? key}) : super(key: key);
@override
State<Dialogs> createState() => _DialogsState();
}
class _DialogsState extends State<Dialogs> {
void openDialog(BuildContext context) {
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text("Basic Dialog Title"),
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(),
),
TextButton(
child: const Text('Action'),
onPressed: () => Navigator.of(context).pop(),
),
],
));
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextButton(
child: const Text(
"Open Dialog",
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: () => openDialog(context),
),
);
}
}
const List<NavigationDestination> appBarDestinations = [
NavigationDestination(
tooltip: "",
icon: Icon(Icons.widgets_outlined),
label: 'Components',
selectedIcon: Icon(Icons.widgets),
),
NavigationDestination(
tooltip: "",
icon: Icon(Icons.format_paint_outlined),
label: 'Color',
selectedIcon: Icon(Icons.format_paint),
),
NavigationDestination(
tooltip: "",
icon: Icon(Icons.text_snippet_outlined),
label: 'Typography',
selectedIcon: Icon(Icons.text_snippet),
),
NavigationDestination(
tooltip: "",
icon: Icon(Icons.invert_colors_on_outlined),
label: 'Elevation',
selectedIcon: Icon(Icons.opacity),
)
];
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();
const List<Widget> exampleBarDestinations = [
NavigationDestination(
tooltip: "",
icon: Icon(Icons.explore_outlined),
label: 'Explore',
selectedIcon: Icon(Icons.explore),
),
NavigationDestination(
tooltip: "",
icon: Icon(Icons.pets_outlined),
label: 'Pets',
selectedIcon: Icon(Icons.pets),
),
NavigationDestination(
tooltip: "",
icon: Icon(Icons.account_box_outlined),
label: 'Account',
selectedIcon: Icon(Icons.account_box),
)
];
class NavigationBars extends StatefulWidget {
final void Function(int)? onSelectItem;
final int selectedIndex;
final bool isExampleBar;
const NavigationBars(
{Key? key,
this.onSelectItem,
required this.selectedIndex,
required this.isExampleBar})
: super(key: key);
@override
State<NavigationBars> createState() => _NavigationBarsState();
}
class _NavigationBarsState extends State<NavigationBars> {
int _selectedIndex = 0;
@override
void initState() {
super.initState();
_selectedIndex = widget.selectedIndex;
}
@override
Widget build(BuildContext context) {
return NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() {
_selectedIndex = index;
});
if (!widget.isExampleBar) widget.onSelectItem!(index);
},
destinations:
widget.isExampleBar ? exampleBarDestinations : appBarDestinations,
);
}
}
class NavigationRailSection extends StatefulWidget {
final void Function(int) onSelectItem;
final int selectedIndex;
const NavigationRailSection(
{Key? key, required this.onSelectItem, required this.selectedIndex})
: super(key: key);
@override
State<NavigationRailSection> createState() => _NavigationRailSectionState();
}
class _NavigationRailSectionState extends State<NavigationRailSection> {
int _selectedIndex = 0;
@override
void initState() {
super.initState();
_selectedIndex = widget.selectedIndex;
}
@override
Widget build(BuildContext context) {
return NavigationRail(
minWidth: 50,
destinations: navRailDestinations,
selectedIndex: _selectedIndex,
useIndicator: true,
onDestinationSelected: (index) {
setState(() {
_selectedIndex = index;
});
widget.onSelectItem(index);
},
);
}
}

View File

@@ -0,0 +1,175 @@
import 'package:flutter/material.dart';
class ElevationScreen extends StatelessWidget {
const ElevationScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
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,
),
),
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,
),
),
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,
),
),
ElevationGrid(shadowColor: shadowColor)
],
),
);
}
}
const double narrowScreenWidthThreshold = 450;
class ElevationGrid extends StatelessWidget {
const ElevationGrid({Key? key, this.shadowColor, this.surfaceTintColor})
: super(key: key);
final Color? shadowColor;
final Color? surfaceTintColor;
List<ElevationCard> elevationCards(
Color? shadowColor, Color? surfaceTintColor) {
return elevations
.map(
(elevationInfo) => ElevationCard(
info: elevationInfo,
shadowColor: shadowColor,
surfaceTint: surfaceTintColor,
),
)
.toList();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8),
child: LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth < narrowScreenWidthThreshold) {
return GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
children: elevationCards(shadowColor, surfaceTintColor),
);
} else {
return GridView.count(
shrinkWrap: true,
crossAxisCount: 6,
children: elevationCards(shadowColor, surfaceTintColor),
);
}
}),
);
}
}
class ElevationCard extends StatefulWidget {
const ElevationCard(
{Key? key, required this.info, this.shadowColor, this.surfaceTint})
: super(key: key);
final ElevationInfo info;
final Color? shadowColor;
final Color? surfaceTint;
@override
State<ElevationCard> createState() => _ElevationCardState();
}
class _ElevationCardState extends State<ElevationCard> {
late double _elevation;
@override
void initState() {
super.initState();
_elevation = widget.info.elevation;
}
@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(
padding: const EdgeInsets.all(8.0),
child: Material(
borderRadius: borderRadius,
elevation: _elevation,
color: color,
shadowColor: widget.shadowColor,
surfaceTintColor: widget.surfaceTint,
type: MaterialType.card,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Level ${widget.info.level}',
style: Theme.of(context).textTheme.labelMedium,
),
Text(
'${widget.info.level.toInt()} dp',
style: Theme.of(context).textTheme.labelMedium,
),
if (showOpacity)
Expanded(
child: Align(
alignment: Alignment.bottomRight,
child: Text(
'${widget.info.overlayPercent}%',
style: Theme.of(context).textTheme.bodySmall,
),
),
),
],
),
),
),
);
}
}
class ElevationInfo {
const ElevationInfo(this.level, this.elevation, this.overlayPercent);
final int level;
final double elevation;
final int overlayPercent;
}
const List<ElevationInfo> elevations = <ElevationInfo>[
ElevationInfo(0, 0.0, 0),
ElevationInfo(1, 1.0, 5),
ElevationInfo(2, 3.0, 8),
ElevationInfo(3, 6.0, 11),
ElevationInfo(4, 8.0, 12),
ElevationInfo(5, 12.0, 14),
];

View File

@@ -0,0 +1,197 @@
import 'package:flutter/material.dart';
import 'color_palettes_screen.dart';
import 'component_screen.dart';
import 'elevation_screen.dart';
import 'typography_screen.dart';
void main() {
runApp(const Material3Demo());
}
class Material3Demo extends StatefulWidget {
const Material3Demo({Key? key}) : super(key: key);
@override
State<Material3Demo> createState() => _Material3DemoState();
}
// NavigationRail shows if the screen width is greater or equal to
// 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",
];
class _Material3DemoState extends State<Material3Demo> {
bool useMaterial3 = true;
bool useLightMode = true;
int colorSelected = 0;
int screenIndex = 0;
late ThemeData themeData;
@override
initState() {
super.initState();
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
}
ThemeData updateThemes(int colorIndex, bool useMaterial3, bool useLightMode) {
return ThemeData(
colorSchemeSeed: colorOptions[colorSelected],
useMaterial3: useMaterial3,
brightness: useLightMode ? Brightness.light : Brightness.dark);
}
void handleScreenChanged(int selectedScreen) {
setState(() {
screenIndex = selectedScreen;
});
}
void handleBrightnessChange() {
setState(() {
useLightMode = !useLightMode;
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
});
}
void handleMaterialVersionChange() {
setState(() {
useMaterial3 = !useMaterial3;
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
});
}
void handleColorSelect(int value) {
setState(() {
colorSelected = value;
themeData = updateThemes(colorSelected, useMaterial3, useLightMode);
});
}
Widget createScreenFor(int screenIndex, bool showNavBarExample) {
switch (screenIndex) {
case 0:
return ComponentScreen(showNavBottomBar: showNavBarExample);
case 1:
return const ColorPalettesScreen();
case 2:
return const TypographyScreen();
case 3:
return const ElevationScreen();
default:
return ComponentScreen(showNavBottomBar: showNavBarExample);
}
}
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,
),
],
);
}
@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(
appBar: createAppBar(),
body: Row(children: <Widget>[
createScreenFor(screenIndex, false),
]),
bottomNavigationBar: NavigationBars(
onSelectItem: handleScreenChanged,
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),
]),
),
);
}
}),
);
}
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
class TypographyScreen extends StatelessWidget {
const TypographyScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context)
.textTheme
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
return Expanded(
child: ListView(
children: <Widget>[
const SizedBox(height: 7),
TextStyleExample(
name: "Display Large", style: textTheme.displayLarge!),
TextStyleExample(
name: "Display Medium", style: textTheme.displayMedium!),
TextStyleExample(
name: "Display Small", style: textTheme.displaySmall!),
TextStyleExample(
name: "Headline Large", style: textTheme.headlineLarge!),
TextStyleExample(
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!),
],
),
);
}
}
class TextStyleExample extends StatelessWidget {
const TextStyleExample({
Key? key,
required this.name,
required this.style,
}) : super(key: key);
final String name;
final TextStyle style;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(name, style: style),
);
}
}