From 1b195e5dcc3b7cdbd63b0a9e173f22bfcfc5c838 Mon Sep 17 00:00:00 2001 From: Qun Cheng <36861262+QuncCccccc@users.noreply.github.com> Date: Mon, 6 Mar 2023 15:03:58 -0800 Subject: [PATCH] Update experimental M3 demo (#1692) --- .../material_3_demo/lib/component_screen.dart | 335 +++++++++--------- .../test/component_screen_test.dart | 8 +- tool/flutter_ci_script_stable.sh | 3 +- 3 files changed, 169 insertions(+), 177 deletions(-) diff --git a/experimental/material_3_demo/lib/component_screen.dart b/experimental/material_3_demo/lib/component_screen.dart index 826d50873..ff3b9beab 100644 --- a/experimental/material_3_demo/lib/component_screen.dart +++ b/experimental/material_3_demo/lib/component_screen.dart @@ -155,12 +155,12 @@ class Selection extends StatelessWidget { return const ComponentGroupDecoration(label: 'Selection', children: [ Checkboxes(), Chips(), - // TODO: Add Date pickers https://github.com/flutter/flutter/issues/101481 + DatePickers(), Menus(), Radios(), Sliders(), Switches(), - // TODO: Add Time pickers https://github.com/flutter/flutter/issues/101480 + TimePickers(), ]); } } @@ -187,7 +187,7 @@ class Buttons extends StatefulWidget { class _ButtonsState extends State { @override Widget build(BuildContext context) { - return ComponentDecoration( + return const ComponentDecoration( label: 'Common buttons', tooltipMessage: 'Use ElevatedButton, FilledButton, FilledButton.tonal, OutlinedButton, or TextButton', @@ -195,7 +195,7 @@ class _ButtonsState extends State { scrollDirection: Axis.horizontal, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - children: const [ + children: [ ButtonsWithoutIcon(isDisabled: false), ButtonsWithIcon(), ButtonsWithoutIcon(isDisabled: true), @@ -681,11 +681,11 @@ class Dividers extends StatelessWidget { @override Widget build(BuildContext context) { - return ComponentDecoration( + return const ComponentDecoration( label: 'Dividers', tooltipMessage: 'Use Divider or VerticalDivider', child: Column( - children: const [ + children: [ Divider(key: Key('divider')), ], ), @@ -698,11 +698,11 @@ class Switches extends StatelessWidget { @override Widget build(BuildContext context) { - return ComponentDecoration( + return const ComponentDecoration( label: 'Switches', tooltipMessage: 'Use SwitchListTile or Switch', child: Column( - children: const [ + children: [ SwitchRow(isEnabled: true), SwitchRow(isEnabled: false), ], @@ -1091,73 +1091,105 @@ class IconToggleButtons extends StatefulWidget { } class _IconToggleButtonsState extends State { + bool standardSelected = false; + bool filledSelected = false; + bool tonalSelected = false; + bool outlinedSelected = false; + @override Widget build(BuildContext context) { return ComponentDecoration( label: 'Icon buttons', - tooltipMessage: 'Use IconButton', + tooltipMessage: + 'Use IconButton, IconButton.filled, IconButton.filledTonal, and IconButton.outlined', child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Column( // Standard IconButton - children: const [ - IconToggleButton( - isEnabled: true, - tooltip: 'Standard', + children: [ + IconButton( + isSelected: standardSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: () { + setState(() { + standardSelected = !standardSelected; + }); + }, ), colDivider, - IconToggleButton( - isEnabled: false, - tooltip: 'Standard (disabled)', + IconButton( + isSelected: standardSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: null, ), ], ), Column( - children: const [ + children: [ // Filled IconButton - IconToggleButton( - isEnabled: true, - tooltip: 'Filled', - getDefaultStyle: enabledFilledButtonStyle, + IconButton.filled( + isSelected: filledSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: () { + setState(() { + filledSelected = !filledSelected; + }); + }, ), colDivider, - IconToggleButton( - isEnabled: false, - tooltip: 'Filled (disabled)', - getDefaultStyle: disabledFilledButtonStyle, + IconButton.filled( + isSelected: filledSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: null, ), ], ), Column( - children: const [ + children: [ // Filled Tonal IconButton - IconToggleButton( - isEnabled: true, - tooltip: 'Filled tonal', - getDefaultStyle: enabledFilledTonalButtonStyle, + IconButton.filledTonal( + isSelected: tonalSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: () { + setState(() { + tonalSelected = !tonalSelected; + }); + }, ), colDivider, - IconToggleButton( - isEnabled: false, - tooltip: 'Filled tonal (disabled)', - getDefaultStyle: disabledFilledTonalButtonStyle, + IconButton.filledTonal( + isSelected: tonalSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: null, ), ], ), Column( - children: const [ + children: [ // Outlined IconButton - IconToggleButton( - isEnabled: true, - tooltip: 'Outlined', - getDefaultStyle: enabledOutlinedButtonStyle, + IconButton.outlined( + isSelected: outlinedSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: () { + setState(() { + outlinedSelected = !outlinedSelected; + }); + }, ), colDivider, - IconToggleButton( - isEnabled: false, - tooltip: 'Outlined (disabled)', - getDefaultStyle: disabledOutlinedButtonStyle, + IconButton.outlined( + isSelected: outlinedSelected, + icon: const Icon(Icons.settings_outlined), + selectedIcon: const Icon(Icons.settings), + onPressed: null, ), ], ), @@ -1167,134 +1199,6 @@ class _IconToggleButtonsState extends State { } } -class IconToggleButton extends StatefulWidget { - const IconToggleButton({ - required this.isEnabled, - required this.tooltip, - this.getDefaultStyle, - super.key, - }); - - final bool isEnabled; - final String tooltip; - final ButtonStyle? Function(bool, ColorScheme)? getDefaultStyle; - - @override - State createState() => _IconToggleButtonState(); -} - -class _IconToggleButtonState extends State { - bool selected = false; - - @override - Widget build(BuildContext context) { - final ColorScheme colors = Theme.of(context).colorScheme; - final VoidCallback? onPressed = widget.isEnabled - ? () { - setState(() { - selected = !selected; - }); - } - : null; - ButtonStyle? style = widget.getDefaultStyle?.call(selected, colors); - - return IconButton( - visualDensity: VisualDensity.standard, - isSelected: selected, - tooltip: widget.tooltip, - icon: const Icon(Icons.settings_outlined), - selectedIcon: const Icon(Icons.settings), - onPressed: onPressed, - style: style, - ); - } -} - -ButtonStyle enabledFilledButtonStyle(bool selected, ColorScheme colors) { - return IconButton.styleFrom( - foregroundColor: selected ? colors.onPrimary : colors.primary, - backgroundColor: selected ? colors.primary : colors.surfaceVariant, - disabledForegroundColor: colors.onSurface.withOpacity(0.38), - disabledBackgroundColor: colors.onSurface.withOpacity(0.12), - hoverColor: selected - ? colors.onPrimary.withOpacity(0.08) - : colors.primary.withOpacity(0.08), - focusColor: selected - ? colors.onPrimary.withOpacity(0.12) - : colors.primary.withOpacity(0.12), - highlightColor: selected - ? colors.onPrimary.withOpacity(0.12) - : colors.primary.withOpacity(0.12), - ); -} - -ButtonStyle disabledFilledButtonStyle(bool selected, ColorScheme colors) { - return IconButton.styleFrom( - disabledForegroundColor: colors.onSurface.withOpacity(0.38), - disabledBackgroundColor: colors.onSurface.withOpacity(0.12), - ); -} - -ButtonStyle enabledFilledTonalButtonStyle(bool selected, ColorScheme colors) { - return IconButton.styleFrom( - foregroundColor: - selected ? colors.onSecondaryContainer : colors.onSurfaceVariant, - backgroundColor: - selected ? colors.secondaryContainer : colors.surfaceVariant, - hoverColor: selected - ? colors.onSecondaryContainer.withOpacity(0.08) - : colors.onSurfaceVariant.withOpacity(0.08), - focusColor: selected - ? colors.onSecondaryContainer.withOpacity(0.12) - : colors.onSurfaceVariant.withOpacity(0.12), - highlightColor: selected - ? colors.onSecondaryContainer.withOpacity(0.12) - : colors.onSurfaceVariant.withOpacity(0.12), - ); -} - -ButtonStyle disabledFilledTonalButtonStyle(bool selected, ColorScheme colors) { - return IconButton.styleFrom( - disabledForegroundColor: colors.onSurface.withOpacity(0.38), - disabledBackgroundColor: colors.onSurface.withOpacity(0.12), - ); -} - -ButtonStyle enabledOutlinedButtonStyle(bool selected, ColorScheme colors) { - return IconButton.styleFrom( - backgroundColor: selected ? colors.inverseSurface : null, - hoverColor: selected - ? colors.onInverseSurface.withOpacity(0.08) - : colors.onSurfaceVariant.withOpacity(0.08), - focusColor: selected - ? colors.onInverseSurface.withOpacity(0.12) - : colors.onSurfaceVariant.withOpacity(0.12), - highlightColor: selected - ? colors.onInverseSurface.withOpacity(0.12) - : colors.onSurface.withOpacity(0.12), - side: BorderSide(color: colors.outline), - ).copyWith( - foregroundColor: MaterialStateProperty.resolveWith((states) { - if (states.contains(MaterialState.selected)) { - return colors.onInverseSurface; - } - if (states.contains(MaterialState.pressed)) { - return colors.onSurface; - } - return null; - }), - ); -} - -ButtonStyle disabledOutlinedButtonStyle(bool selected, ColorScheme colors) { - return IconButton.styleFrom( - disabledForegroundColor: colors.onSurface.withOpacity(0.38), - disabledBackgroundColor: - selected ? colors.onSurface.withOpacity(0.12) : null, - side: selected ? null : BorderSide(color: colors.outline.withOpacity(0.12)), - ); -} - class Chips extends StatefulWidget { const Chips({super.key}); @@ -1371,16 +1275,97 @@ class _ChipsState extends State { } } +class DatePickers extends StatefulWidget { + const DatePickers({super.key}); + + @override + State createState() => _DatePickersState(); +} + +class _DatePickersState extends State { + DateTime? selectedDate; + final DateTime _firstDate = DateTime(DateTime.now().year - 2); + final DateTime _lastDate = DateTime(DateTime.now().year + 1); + + @override + Widget build(BuildContext context) { + return ComponentDecoration( + label: 'Date picker', + tooltipMessage: 'Use showDatePicker', + child: TextButton( + onPressed: () async { + DateTime? date = await showDatePicker( + context: context, + initialDate: selectedDate ?? DateTime.now(), + firstDate: _firstDate, + lastDate: _lastDate, + ); + setState(() { + selectedDate = date; + if (selectedDate != null) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + 'Selected Date: ${selectedDate!.day}/${selectedDate!.month}/${selectedDate!.year}'), + )); + } + }); + }, + child: const Text( + 'Show date picker', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ); + } +} + +class TimePickers extends StatefulWidget { + const TimePickers({super.key}); + + @override + State createState() => _TimePickersState(); +} + +class _TimePickersState extends State { + TimeOfDay? selectedTime; + + @override + Widget build(BuildContext context) { + return ComponentDecoration( + label: 'Time picker', + tooltipMessage: 'Use showTimePicker', + child: TextButton( + onPressed: () async { + final TimeOfDay? time = await showTimePicker( + context: context, + initialTime: selectedTime ?? TimeOfDay.now(), + ); + setState(() { + selectedTime = time; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Selected time: ${selectedTime!.format(context)}'), + )); + }); + }, + child: const Text( + 'Show time picker', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ); + } +} + class SegmentedButtons extends StatelessWidget { const SegmentedButtons({super.key}); @override Widget build(BuildContext context) { - return ComponentDecoration( + return const ComponentDecoration( label: 'Segmented buttons', tooltipMessage: 'Use SegmentedButton', child: Column( - children: const [ + children: [ SingleChoice(), colDivider, MultipleChoice(), @@ -2075,9 +2060,9 @@ class _MenusState extends State { tooltipMessage: 'Use MenuAnchor or DropdownMenu', child: Column( children: [ - Row( + const Row( mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ ButtonAnchorExample(), rowDivider, IconButtonAnchorExample(), diff --git a/experimental/material_3_demo/test/component_screen_test.dart b/experimental/material_3_demo/test/component_screen_test.dart index 550547fad..c027ce1b1 100644 --- a/experimental/material_3_demo/test/component_screen_test.dart +++ b/experimental/material_3_demo/test/component_screen_test.dart @@ -37,7 +37,7 @@ void main() { expect(find.widgetWithText(FloatingActionButton, 'Create'), findsOneWidget); // Icon buttons - expect(find.byType(IconToggleButton), findsNWidgets(8)); + expect(find.widgetWithIcon(IconButton, Icons.settings_outlined), findsNWidgets(8)); // Segmented buttons expect(find.byType(SegmentedButton), findsOneWidget); @@ -106,6 +106,9 @@ void main() { expect(find.byType(FilterChip), findsNWidgets(2)); expect(find.byType(InputChip), findsNWidgets(2)); + // Date pickers + expect(find.widgetWithText(TextButton, 'Show date picker'), findsOneWidget); + // Menus expect(find.byType(MenuAnchor), findsNWidgets(5)); expect(find.byType(DropdownMenu), findsOneWidget); @@ -121,6 +124,9 @@ void main() { // Switches expect(find.byType(Switch), findsNWidgets(4)); + // Time pickers + expect(find.widgetWithText(TextButton, 'Show time picker'), findsOneWidget); + // TextFields expect(find.widgetWithText(TextField, 'Disabled'), findsNWidgets(2)); expect(find.widgetWithText(TextField, 'Filled'), findsNWidgets(2)); diff --git a/tool/flutter_ci_script_stable.sh b/tool/flutter_ci_script_stable.sh index 4414b884a..d6e9c6b06 100755 --- a/tool/flutter_ci_script_stable.sh +++ b/tool/flutter_ci_script_stable.sh @@ -31,7 +31,8 @@ declare -ar PROJECT_NAMES=( "experimental/federated_plugin/federated_plugin_web" "experimental/federated_plugin/federated_plugin_windows" "experimental/linting_tool" - "experimental/material_3_demo" + # TODO(DomesticMouse): uncomment on next Flutter stable increment + # "experimental/material_3_demo" "experimental/pedometer" "experimental/varfont_shader_puzzle" "experimental/web_dashboard"