Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update all popupmenus to material 3 menus #2785

Merged
merged 2 commits into from
Feb 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
316 changes: 133 additions & 183 deletions pkgs/sketch_pad/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ class _DartPadAppState extends State<DartPadApp> {
color: lightSurfaceColor,
),
scaffoldBackgroundColor: Colors.white,
menuButtonTheme: MenuButtonThemeData(
style: MenuItemButton.styleFrom(
minimumSize: const Size.fromHeight(56),
),
),
),
darkTheme: ThemeData(
useMaterial3: true,
Expand All @@ -163,6 +168,11 @@ class _DartPadAppState extends State<DartPadApp> {
),
),
scaffoldBackgroundColor: darkScaffoldColor,
menuButtonTheme: MenuButtonThemeData(
style: MenuItemButton.styleFrom(
minimumSize: const Size.fromHeight(56),
),
),
),
);
}
Expand Down Expand Up @@ -665,120 +675,88 @@ class SectionWidget extends StatelessWidget {
class NewSnippetWidget extends StatelessWidget {
final AppServices appServices;

static final _menuItems = [
(
label: 'New Dart snippet',
icon: dartLogo(),
kind: 'dart',
),
(label: 'New Flutter snippet', icon: flutterLogo(), kind: 'flutter'),
];

const NewSnippetWidget({
required this.appServices,
super.key,
});

@override
Widget build(BuildContext context) {
return SizedBox(
height: toolbarItemHeight,
child: TextButton.icon(
icon: const Icon(Icons.add_circle),
label: const Text('New'),
onPressed: () async {
final selection =
await _showMenu(context, calculatePopupMenuPosition(context));
if (selection != null) {
_handleSelection(appServices, selection);
}
},
),
);
}

Future<bool?> _showMenu(BuildContext context, RelativeRect position) {
return showMenu<bool>(
context: context,
position: position,
items: <PopupMenuEntry<bool>>[
PopupMenuItem(
value: true,
child: PointerInterceptor(
child: ListTile(
leading: dartLogo(),
title: const Text('New Dart snippet'),
),
),
),
PopupMenuItem(
value: false,
child: PointerInterceptor(
child: ListTile(
leading: flutterLogo(),
title: const Text('New Flutter snippet'),
return MenuAnchor(
builder: (context, MenuController controller, Widget? child) {
return TextButton.icon(
onPressed: () => controller.toggleMenuState(),
icon: const Icon(Icons.add_circle),
label: const Text('New'),
);
},
menuChildren: [
for (final item in _menuItems)
PointerInterceptor(
child: MenuItemButton(
leadingIcon: item.icon,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 32, 0),
child: Text(item.label),
),
onPressed: () => appServices.resetTo(type: item.kind),
),
),
),
)
],
);
}

void _handleSelection(AppServices appServices, bool dartSample) {
appServices.resetTo(type: dartSample ? 'dart' : 'flutter');
}
}

class ListSamplesWidget extends StatelessWidget {
const ListSamplesWidget({
super.key,
});
const ListSamplesWidget({super.key});

@override
Widget build(BuildContext context) {
return SizedBox(
height: toolbarItemHeight,
child: TextButton.icon(
icon: const Icon(Icons.playlist_add_outlined),
label: const Text('Samples'),
onPressed: () async {
final selection =
await _showMenu(context, calculatePopupMenuPosition(context));
if (selection != null && context.mounted) {
_handleSelection(context, selection);
}
},
),
return MenuAnchor(
builder: (context, MenuController controller, Widget? child) {
return TextButton.icon(
onPressed: () => controller.toggleMenuState(),
icon: const Icon(Icons.playlist_add_outlined),
label: const Text('Samples'),
);
},
menuChildren: _buildMenuItems(context),
);
}

Future<String?> _showMenu(BuildContext context, RelativeRect position) {
List<Widget> _buildMenuItems(BuildContext context) {
final categories = Samples.categories.keys;

final menuItems = <PopupMenuEntry<String?>>[
final menuItems = [
for (final category in categories) ...[
const PopupMenuDivider(),
PopupMenuItem(
value: null,
enabled: false,
child: PointerInterceptor(
child: ListTile(title: Text(category)),
),
MenuItemButton(
onPressed: null,
child: Text(category),
),
...Samples.categories[category]!.map((sample) {
return PopupMenuItem(
value: sample.id,
child: PointerInterceptor(
child: ListTile(
leading: sample.isDart ? dartLogo() : flutterLogo(),
title: Text(sample.name),
),
for (final sample in Samples.categories[category]!)
MenuItemButton(
leadingIcon: sample.isDart ? dartLogo() : flutterLogo(),
onPressed: () =>
GoRouter.of(context).replaceQueryParam('sample', sample.id),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 32, 0),
child: Text(sample.name),
),
);
}),
],
),
const Divider(),
]
];

return showMenu<String?>(
context: context,
position: position,
items: menuItems.skip(1).toList(),
);
}

void _handleSelection(BuildContext context, String sampleId) {
GoRouter.of(context).replaceQueryParam('sample', sampleId);
return menuItems.map((e) => PointerInterceptor(child: e)).toList();
}
}

Expand All @@ -790,58 +768,33 @@ class SelectChannelWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appServices = Provider.of<AppServices>(context);
final channels = Channel.valuesWithoutLocalhost;

return ValueListenableBuilder<Channel>(
valueListenable: appServices.channel,
builder: (context, Channel value, _) {
return SizedBox(
height: toolbarItemHeight,
child: TextButton.icon(
builder: (context, Channel value, _) => MenuAnchor(
builder: (context, MenuController controller, Widget? child) {
return TextButton.icon(
onPressed: () => controller.toggleMenuState(),
icon: const Icon(Icons.tune, size: smallIconSize),
label: Container(
constraints: const BoxConstraints(minWidth: 95),
child: Text('${value.displayName} channel'),
),
onPressed: () async {
final selection = await _showMenu(
context,
calculatePopupMenuPosition(context, growUpwards: true),
value,
);
if (selection != null && context.mounted) {
_handleSelection(context, selection);
}
},
),
);
},
);
}

Future<Channel?> _showMenu(
BuildContext context, RelativeRect position, Channel current) {
const itemHeight = 46.0;

final menuItems = <PopupMenuEntry<Channel>>[
for (final channel in Channel.valuesWithoutLocalhost)
PopupMenuItem<Channel>(
value: channel,
child: PointerInterceptor(
child: ListTile(
title: Text(channel.displayName),
label: Text('${value.displayName} channel'),
);
},
menuChildren: List<MenuItemButton>.generate(
channels.length,
(int index) => MenuItemButton(
onPressed: () => _onTap(context, channels[index]),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 32, 0),
child: Text('${channels[index].displayName} channel'),
),
),
)
];

return showMenu<Channel>(
context: context,
position: position.shift(Offset(0, -1 * menuItems.length * itemHeight)),
items: menuItems,
),
),
);
}

void _handleSelection(BuildContext context, Channel channel) async {
void _onTap(BuildContext context, Channel channel) async {
final appServices = Provider.of<AppServices>(context, listen: false);

// update the url
Expand All @@ -859,66 +812,53 @@ class SelectChannelWidget extends StatelessWidget {
class OverflowMenu extends StatelessWidget {
const OverflowMenu({super.key});

static const _menuItems = [
(
label: 'dart.dev',
uri: 'https://dart.dev',
),
(
label: 'flutter.dec',
uri: 'https://flutter.dev',
),
(
label: 'Sharing guide',
uri: 'https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide'
),
(
label: 'DartPad on GitHub',
uri: 'https://github.com/dart-lang/dart-pad',
),
];

@override
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.more_vert),
splashRadius: defaultSplashRadius,
onPressed: () async {
final selection =
await _showMenu(context, calculatePopupMenuPosition(context));
if (selection != null) {
url_launcher.launchUrl(Uri.parse(selection));
}
return MenuAnchor(
builder: (context, MenuController controller, Widget? child) {
return IconButton(
onPressed: () => controller.toggleMenuState(),
icon: const Icon(Icons.more_vert),
);
},
);
}

Future<String?> _showMenu(BuildContext context, RelativeRect position) {
return showMenu<String?>(
context: context,
position: position,
items: <PopupMenuEntry<String?>>[
PopupMenuItem(
value: 'https://dart.dev',
child: PointerInterceptor(
child: const ListTile(
title: Text('dart.dev'),
trailing: Icon(Icons.launch),
),
),
),
PopupMenuItem(
value: 'https://flutter.dev',
child: PointerInterceptor(
child: const ListTile(
title: Text('flutter.dev'),
trailing: Icon(Icons.launch),
),
),
),
const PopupMenuDivider(),
PopupMenuItem(
value: 'https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide',
child: PointerInterceptor(
child: const ListTile(
title: Text('Sharing guide'),
trailing: Icon(Icons.launch),
),
),
),
PopupMenuItem(
value: 'https://github.com/dart-lang/dart-pad',
child: PointerInterceptor(
child: const ListTile(
title: Text('DartPad on GitHub'),
trailing: Icon(Icons.launch),
menuChildren: [
for (final item in _menuItems)
PointerInterceptor(
child: MenuItemButton(
trailingIcon: const Icon(Icons.launch),
onPressed: () => _onSelected(context, item.uri),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 32, 0),
child: Text(item.label),
),
),
),
),
)
],
);
}

void _onSelected(BuildContext context, String uri) {
url_launcher.launchUrl(Uri.parse(uri));
}
}

class KeyBindingsTable extends StatelessWidget {
Expand Down Expand Up @@ -1036,3 +976,13 @@ class _BrightnessButton extends StatelessWidget {
);
}
}

extension MenuControllerToggleMenu on MenuController {
void toggleMenuState() {
if (isOpen) {
close();
} else {
open();
}
}
}