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

Update for Flutter 3.10 beta (#1746)

## Pre-launch Checklist

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I updated/added relevant documentation (doc comments with `///`).
- [ ] All existing and new tests are passing.

---------

Co-authored-by: David Iglesias <ditman@gmail.com>
Co-authored-by: Mark Thompson <2554588+MarkTechson@users.noreply.github.com>
Co-authored-by: John Ryan <ryjohn@google.com>
This commit is contained in:
Brett Morgan
2023-05-11 06:16:31 +10:00
committed by GitHub
parent 474756ce04
commit 36e7a6ab04
188 changed files with 1779 additions and 1968 deletions

View File

@@ -140,8 +140,8 @@ class Navigation extends StatelessWidget {
),
NavigationDrawers(scaffoldKey: scaffoldKey),
const NavigationRails(),
// TODO: Add Search https://github.com/flutter/flutter/issues/117483
const Tabs(),
const SearchAnchors(),
const TopAppBars(),
]);
}
@@ -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<Buttons> {
@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<Buttons> {
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const <Widget>[
children: <Widget>[
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 <Widget>[
children: <Widget>[
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 <Widget>[
children: <Widget>[
SwitchRow(isEnabled: true),
SwitchRow(isEnabled: false),
],
@@ -1091,73 +1091,105 @@ class IconToggleButtons extends StatefulWidget {
}
class _IconToggleButtonsState extends State<IconToggleButtons> {
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: <Widget>[
Column(
// Standard IconButton
children: const <Widget>[
IconToggleButton(
isEnabled: true,
tooltip: 'Standard',
children: <Widget>[
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 <Widget>[
children: <Widget>[
// 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 <Widget>[
children: <Widget>[
// 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 <Widget>[
children: <Widget>[
// 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<IconToggleButtons> {
}
}
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<IconToggleButton> createState() => _IconToggleButtonState();
}
class _IconToggleButtonState extends State<IconToggleButton> {
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<Chips> {
}
}
class DatePickers extends StatefulWidget {
const DatePickers({super.key});
@override
State<DatePickers> createState() => _DatePickersState();
}
class _DatePickersState extends State<DatePickers> {
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<TimePickers> createState() => _TimePickersState();
}
class _TimePickersState extends State<TimePickers> {
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<T>',
child: Column(
children: const <Widget>[
children: <Widget>[
SingleChoice(),
colDivider,
MultipleChoice(),
@@ -1561,6 +1546,7 @@ class _BottomSheetSectionState extends State<BottomSheetSection> {
),
onPressed: () {
showModalBottomSheet<void>(
showDragHandle: true,
context: context,
// TODO: Remove when this is in the framework https://github.com/flutter/flutter/issues/118619
constraints: const BoxConstraints(maxWidth: 640),
@@ -2075,9 +2061,9 @@ class _MenusState extends State<Menus> {
tooltipMessage: 'Use MenuAnchor or DropdownMenu<T>',
child: Column(
children: [
Row(
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
children: <Widget>[
ButtonAnchorExample(),
rowDivider,
IconButtonAnchorExample(),
@@ -2197,6 +2183,101 @@ class _SlidersState extends State<Sliders> {
}
}
class SearchAnchors extends StatefulWidget {
const SearchAnchors({super.key});
@override
State<SearchAnchors> createState() => _SearchAnchorsState();
}
class _SearchAnchorsState extends State<SearchAnchors> {
String? selectedColor;
List<ColorItem> searchHistory = <ColorItem>[];
Iterable<Widget> getHistoryList(SearchController controller) {
return searchHistory.map((color) => ListTile(
leading: const Icon(Icons.history),
title: Text(color.label),
trailing: IconButton(
icon: const Icon(Icons.call_missed),
onPressed: () {
controller.text = color.label;
controller.selection =
TextSelection.collapsed(offset: controller.text.length);
}),
onTap: () {
controller.closeView(color.label);
handleSelection(color);
},
));
}
Iterable<Widget> getSuggestions(SearchController controller) {
final String input = controller.value.text;
return ColorItem.values
.where((color) => color.label.contains(input))
.map((filteredColor) => ListTile(
leading: CircleAvatar(backgroundColor: filteredColor.color),
title: Text(filteredColor.label),
trailing: IconButton(
icon: const Icon(Icons.call_missed),
onPressed: () {
controller.text = filteredColor.label;
controller.selection =
TextSelection.collapsed(offset: controller.text.length);
}),
onTap: () {
controller.closeView(filteredColor.label);
handleSelection(filteredColor);
},
));
}
void handleSelection(ColorItem color) {
setState(() {
selectedColor = color.label;
if (searchHistory.length >= 5) {
searchHistory.removeLast();
}
searchHistory.insert(0, color);
});
}
@override
Widget build(BuildContext context) {
return ComponentDecoration(
label: 'Search',
tooltipMessage: 'Use SearchAnchor or SearchAnchor.bar',
child: Column(
children: <Widget>[
SearchAnchor.bar(
barHintText: 'Search colors',
suggestionsBuilder: (context, controller) {
if (controller.text.isEmpty) {
if (searchHistory.isNotEmpty) {
return getHistoryList(controller);
}
return <Widget>[
const Center(
child: Text('No search history.',
style: TextStyle(color: Colors.grey)),
)
];
}
return getSuggestions(controller);
},
),
const SizedBox(height: 20),
if (selectedColor == null)
const Text('Select a color')
else
Text('Last selected color is $selectedColor')
],
),
);
}
}
class ComponentDecoration extends StatefulWidget {
const ComponentDecoration({
super.key,
@@ -2306,3 +2387,26 @@ class ComponentGroupDecoration extends StatelessWidget {
);
}
}
enum ColorItem {
red('red', Colors.red),
orange('orange', Colors.orange),
yellow('yellow', Colors.yellow),
green('green', Colors.green),
blue('blue', Colors.blue),
indigo('indigo', Colors.indigo),
violet('violet', Color(0xFF8F00FF)),
purple('purple', Colors.purple),
pink('pink', Colors.pink),
silver('silver', Color(0xFF808080)),
gold('gold', Color(0xFFFFD700)),
beige('beige', Color(0xFFF5F5DC)),
brown('brown', Colors.brown),
grey('grey', Colors.grey),
black('black', Colors.black),
white('white', Colors.white);
const ColorItem(this.label, this.color);
final String label;
final Color color;
}