2024-02-15 15:23:02 +03:30
|
|
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
2024-01-16 14:32:30 +03:30
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
2026-01-17 13:09:20 +03:00
|
|
|
import 'package:umbrix/utils/platform_utils.dart';
|
2024-01-16 14:32:30 +03:30
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
|
import 'package:wolt_modal_sheet/wolt_modal_sheet.dart';
|
|
|
|
|
|
|
|
|
|
typedef AdaptiveMenuBuilder = Widget Function(
|
|
|
|
|
BuildContext context,
|
|
|
|
|
void Function() toggleVisibility,
|
|
|
|
|
Widget? child,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
class AdaptiveMenuItem<T> {
|
|
|
|
|
AdaptiveMenuItem({
|
|
|
|
|
required this.title,
|
|
|
|
|
this.icon,
|
|
|
|
|
this.onTap,
|
|
|
|
|
this.isSelected,
|
|
|
|
|
this.subItems,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final String title;
|
|
|
|
|
final IconData? icon;
|
|
|
|
|
final T Function()? onTap;
|
|
|
|
|
final bool? isSelected;
|
|
|
|
|
final List<AdaptiveMenuItem>? subItems;
|
|
|
|
|
|
|
|
|
|
(String, IconData?, T Function()?, bool?, List<AdaptiveMenuItem>?)
|
|
|
|
|
_equality() => (title, icon, onTap, isSelected, subItems);
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
bool operator ==(covariant AdaptiveMenuItem other) {
|
|
|
|
|
if (identical(this, other)) return true;
|
|
|
|
|
return other._equality() == _equality();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
int get hashCode => _equality().hashCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class AdaptiveMenu extends HookConsumerWidget {
|
|
|
|
|
const AdaptiveMenu({
|
|
|
|
|
super.key,
|
|
|
|
|
required this.items,
|
|
|
|
|
required this.builder,
|
|
|
|
|
required this.child,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final Iterable<AdaptiveMenuItem> items;
|
|
|
|
|
final AdaptiveMenuBuilder builder;
|
|
|
|
|
final Widget? child;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
|
|
|
if (PlatformUtils.isDesktop) {
|
|
|
|
|
List<Widget> buildMenuItems(Iterable<AdaptiveMenuItem> scopeItems) {
|
|
|
|
|
final menuItems = <Widget>[];
|
|
|
|
|
for (final item in scopeItems) {
|
|
|
|
|
if (item.subItems != null) {
|
|
|
|
|
final subItems = buildMenuItems(item.subItems!);
|
|
|
|
|
menuItems.add(
|
|
|
|
|
SubmenuButton(
|
|
|
|
|
menuChildren: subItems,
|
|
|
|
|
leadingIcon: item.icon != null ? Icon(item.icon) : null,
|
|
|
|
|
child: Text(item.title),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
menuItems.add(
|
|
|
|
|
MenuItemButton(
|
|
|
|
|
leadingIcon: item.icon != null ? Icon(item.icon) : null,
|
|
|
|
|
onPressed: item.onTap,
|
|
|
|
|
child: Text(item.title),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return menuItems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MenuAnchor(
|
|
|
|
|
builder: (context, controller, child) => builder(
|
|
|
|
|
context,
|
|
|
|
|
() {
|
|
|
|
|
if (controller.isOpen) {
|
|
|
|
|
controller.close();
|
|
|
|
|
} else {
|
|
|
|
|
controller.open();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
child,
|
|
|
|
|
),
|
|
|
|
|
menuChildren: buildMenuItems(items),
|
|
|
|
|
child: child,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final pageIndexNotifier = useValueNotifier(0);
|
|
|
|
|
final nestedSheets = <SliverWoltModalSheetPage>[];
|
|
|
|
|
int pageIndex = 0;
|
|
|
|
|
|
|
|
|
|
void popSheets() {
|
|
|
|
|
if (context.mounted) {
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
}
|
|
|
|
|
Future.delayed(const Duration(milliseconds: 200))
|
|
|
|
|
.then((_) => pageIndexNotifier.value = 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Widget> buildSheetItems(
|
|
|
|
|
Iterable<AdaptiveMenuItem> menuItems,
|
|
|
|
|
int index,
|
|
|
|
|
) {
|
|
|
|
|
final sheetItems = <Widget>[];
|
|
|
|
|
for (final item in menuItems) {
|
|
|
|
|
if (item.subItems != null) {
|
|
|
|
|
final subItems = buildSheetItems(item.subItems!, index + 1);
|
|
|
|
|
final subSheetIndex = ++pageIndex;
|
|
|
|
|
sheetItems.add(
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Text(item.title),
|
|
|
|
|
leading: item.icon != null ? Icon(item.icon) : null,
|
2024-02-15 15:23:02 +03:30
|
|
|
trailing:
|
|
|
|
|
const Icon(FluentIcons.chevron_right_20_regular, size: 20),
|
2024-01-16 14:32:30 +03:30
|
|
|
onTap: () {
|
|
|
|
|
pageIndexNotifier.value = subSheetIndex;
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
nestedSheets.add(
|
|
|
|
|
SliverWoltModalSheetPage(
|
|
|
|
|
hasTopBarLayer: false,
|
|
|
|
|
isTopBarLayerAlwaysVisible: true,
|
|
|
|
|
topBarTitle: Text(item.title),
|
|
|
|
|
mainContentSlivers: [
|
|
|
|
|
SliverList.list(children: subItems),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
sheetItems.add(
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Text(item.title),
|
|
|
|
|
leading: item.icon != null ? Icon(item.icon) : null,
|
|
|
|
|
onTap: () async {
|
|
|
|
|
popSheets();
|
|
|
|
|
await item.onTap!();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sheetItems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return builder(
|
|
|
|
|
context,
|
|
|
|
|
() async {
|
|
|
|
|
await WoltModalSheet.show(
|
|
|
|
|
context: context,
|
|
|
|
|
pageIndexNotifier: pageIndexNotifier,
|
|
|
|
|
onModalDismissedWithDrag: popSheets,
|
|
|
|
|
onModalDismissedWithBarrierTap: popSheets,
|
|
|
|
|
useSafeArea: true,
|
|
|
|
|
showDragHandle: false,
|
|
|
|
|
pageListBuilder: (context) => [
|
|
|
|
|
SliverWoltModalSheetPage(
|
|
|
|
|
hasTopBarLayer: false,
|
|
|
|
|
mainContentSlivers: [
|
|
|
|
|
SliverList.list(children: buildSheetItems(items, 0)),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
...nestedSheets,
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
child,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|