import 'package:flutter/material.dart' show Icons; import 'package:flutter/widgets.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart' as shad; import '../../core/utils/breakpoints.dart'; import '../theme/app_palette.dart'; import 'secondary_navigation_pane.dart'; class ResponsiveMenuItem { const ResponsiveMenuItem({required this.value, required this.label, required this.icon, this.trailing}); final T value; final String label; final IconData icon; final String? trailing; } class ResponsiveMenuLayout extends StatefulWidget { const ResponsiveMenuLayout({ super.key, required this.title, required this.items, required this.selectedValue, required this.onSelected, required this.contentBuilder, this.mobileContentTitleBuilder, this.mobileContentVisible = false, this.onMobileBack, this.sidebarFooter, this.mobileBreakpoint = Breakpoints.mobile, }); final String title; final List> items; final T selectedValue; final ValueChanged onSelected; final Widget Function(BuildContext context, T selectedValue) contentBuilder; final String Function(T selectedValue)? mobileContentTitleBuilder; final bool mobileContentVisible; final VoidCallback? onMobileBack; final Widget? sidebarFooter; final double mobileBreakpoint; @override State> createState() => _ResponsiveMenuLayoutState(); } class _ResponsiveMenuLayoutState extends State> { @override Widget build(BuildContext context) { final width = MediaQuery.sizeOf(context).width; final mobile = width < widget.mobileBreakpoint; if (!mobile) { return Row( children: [ SecondaryNavigationPane( title: widget.title, items: widget.items .map( (item) => SecondaryNavigationPaneItem( value: item.value, label: item.label, icon: item.icon, trailing: item.trailing, ), ) .toList(), selectedValue: widget.selectedValue, onSelected: widget.onSelected, footer: widget.sidebarFooter, ), Expanded(child: widget.contentBuilder(context, widget.selectedValue)), ], ); } if (!widget.mobileContentVisible) { return SafeArea( bottom: false, child: SecondaryNavigationPane( title: widget.title, items: widget.items .map( (item) => SecondaryNavigationPaneItem( value: item.value, label: item.label, icon: item.icon, trailing: item.trailing, ), ) .toList(), selectedValue: widget.selectedValue, onSelected: widget.onSelected, footer: widget.sidebarFooter, mobile: true, showDisclosure: true, ), ); } return SafeArea( bottom: false, child: Column( children: [ _MobileContentHeader( title: widget.mobileContentTitleBuilder?.call(widget.selectedValue) ?? widget.title, onBack: widget.onMobileBack ?? () {}, ), Expanded(child: widget.contentBuilder(context, widget.selectedValue)), ], ), ); } } class _MobileContentHeader extends StatelessWidget { const _MobileContentHeader({required this.title, required this.onBack}); final String title; final VoidCallback onBack; @override Widget build(BuildContext context) { final palette = AppPalette.of(context); return Container( key: const ValueKey('mobile-content-header'), height: 56, decoration: BoxDecoration( color: palette.bg, border: Border(bottom: BorderSide(color: palette.border)), ), padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( children: [ shad.GhostButton( density: shad.ButtonDensity.icon, onPressed: onBack, child: Icon(Icons.arrow_back, size: 18, color: palette.textPrimary), ), const SizedBox(width: 6), Expanded( child: Text( title, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: palette.textPrimary, fontSize: 16, fontWeight: FontWeight.w800), ), ), ], ), ); } }