import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:relationship_saver/core/config/app_theme.dart'; import 'package:relationship_saver/features/dashboard/dashboard_view.dart'; import 'package:relationship_saver/features/ideas/ideas_view.dart'; import 'package:relationship_saver/features/moments/moments_view.dart'; import 'package:relationship_saver/features/people/people_view.dart'; import 'package:relationship_saver/features/reminders/reminders_view.dart'; import 'package:relationship_saver/features/settings/settings_view.dart'; import 'package:relationship_saver/features/signals/signals_view.dart'; import 'package:relationship_saver/features/sync/sync_view.dart'; class AppShell extends ConsumerStatefulWidget { const AppShell({super.key}); @override ConsumerState createState() => _AppShellState(); } class _AppShellState extends ConsumerState { int _index = 0; static const List<_ShellDestination> _destinations = <_ShellDestination>[ _ShellDestination('Dashboard', Icons.dashboard_rounded, 0), _ShellDestination('People', Icons.people_alt_rounded, 1), _ShellDestination('Moments', Icons.auto_awesome_rounded, 2), _ShellDestination('Signals', Icons.tips_and_updates_rounded, 3), _ShellDestination('Ideas', Icons.lightbulb_outline_rounded, 4), _ShellDestination('Reminders', Icons.notifications_active_outlined, 5), _ShellDestination('Sync', Icons.sync_rounded, 6), _ShellDestination('Settings', Icons.settings_rounded, 7), ]; static const List<_ShellDestination> _compactPrimaryDestinations = <_ShellDestination>[ _ShellDestination('Dashboard', Icons.dashboard_rounded, 0), _ShellDestination('People', Icons.people_alt_rounded, 1), _ShellDestination('Moments', Icons.auto_awesome_rounded, 2), _ShellDestination('Signals', Icons.tips_and_updates_rounded, 3), ]; @override Widget build(BuildContext context) { return DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( colors: [ Color(0xFFF3F8FB), Color(0xFFEAF1F8), Color(0xFFF8FAFC), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), child: Scaffold( backgroundColor: Colors.transparent, body: SafeArea( child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { if (constraints.maxWidth < 1000) { final int compactIndex = _compactSelectedIndex(_index); final _ShellDestination active = _compactPrimaryDestinations[compactIndex]; return _CompactShell( title: active.label, index: compactIndex, body: _screenFor(active.screenIndex), destinations: _compactPrimaryDestinations, onChange: (int value) { setState(() { _index = _compactPrimaryDestinations[value].screenIndex; }); }, onOpenSecondary: (_SecondaryDestination destination) { final int screenIndex = switch (destination) { _SecondaryDestination.ideas => 4, _SecondaryDestination.reminders => 5, _SecondaryDestination.sync => 6, _SecondaryDestination.settings => 7, }; Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { return _CompactSecondaryScreen( title: _destinations[screenIndex].label, child: _screenFor(screenIndex), ); }, ), ); }, ); } return Row( children: [ _Sidebar( index: _index, destinations: _destinations, onChange: (int value) { setState(() { _index = value; }); }, ), Expanded( child: AnimatedSwitcher( duration: const Duration(milliseconds: 250), child: KeyedSubtree( key: ValueKey(_index), child: _screenFor(_index), ), ), ), ], ); }, ), ), ), ); } int _compactSelectedIndex(int fullIndex) { for (int i = 0; i < _compactPrimaryDestinations.length; i += 1) { if (_compactPrimaryDestinations[i].screenIndex == fullIndex) { return i; } } return 0; } Widget _screenFor(int index) { switch (index) { case 0: return const DashboardView(); case 1: return const PeopleView(); case 2: return const MomentsView(); case 3: return const SignalsView(); case 4: return const IdeasView(); case 5: return const RemindersView(); case 6: return const SyncView(); case 7: return const SettingsView(); default: return const DashboardView(); } } } class _Sidebar extends StatelessWidget { const _Sidebar({ required this.index, required this.destinations, required this.onChange, }); final int index; final List<_ShellDestination> destinations; final ValueChanged onChange; @override Widget build(BuildContext context) { return Container( width: 290, margin: const EdgeInsets.fromLTRB(20, 20, 8, 20), padding: const EdgeInsets.fromLTRB(18, 20, 18, 18), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.78), borderRadius: BorderRadius.circular(28), border: Border.all(color: Colors.white.withValues(alpha: 0.7)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 34, height: 34, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), gradient: const LinearGradient( colors: [Color(0xFF1AB6C8), Color(0xFF286DE0)], ), ), child: const Icon( Icons.favorite_rounded, color: Colors.white, size: 20, ), ), const SizedBox(width: 10), Text( 'Relationship Saver', style: Theme.of(context).textTheme.titleMedium, ), ], ), const SizedBox(height: 22), ...destinations.indexed.map(((int, _ShellDestination) tuple) { final int destinationIndex = tuple.$1; final _ShellDestination destination = tuple.$2; final bool selected = destinationIndex == index; return Padding( padding: const EdgeInsets.only(bottom: 6), child: InkWell( onTap: () => onChange(destinationIndex), borderRadius: BorderRadius.circular(14), child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), decoration: BoxDecoration( color: selected ? const Color(0xFFEAF7FB) : Colors.transparent, borderRadius: BorderRadius.circular(14), ), child: Row( children: [ Icon( destination.icon, size: 20, color: selected ? AppTheme.primary : AppTheme.textSecondary, ), const SizedBox(width: 10), Text( destination.label, style: Theme.of(context).textTheme.titleMedium ?.copyWith( color: selected ? AppTheme.primary : AppTheme.textSecondary, ), ), ], ), ), ), ); }), const Spacer(), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFF2F8FB), borderRadius: BorderRadius.circular(16), ), child: Text( 'Tip: Use Signals daily and convert at least one into a concrete plan.', style: Theme.of( context, ).textTheme.bodyMedium?.copyWith(color: AppTheme.textSecondary), ), ), ], ), ); } } class _CompactShell extends StatelessWidget { const _CompactShell({ required this.title, required this.index, required this.body, required this.destinations, required this.onChange, required this.onOpenSecondary, }); final String title; final int index; final Widget body; final List<_ShellDestination> destinations; final ValueChanged onChange; final ValueChanged<_SecondaryDestination> onOpenSecondary; @override Widget build(BuildContext context) { return Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), child: Row( children: [ Expanded( child: Text( title, style: Theme.of(context).textTheme.titleLarge, ), ), PopupMenuButton<_SecondaryDestination>( tooltip: 'More', icon: const Icon(Icons.more_horiz_rounded), onSelected: onOpenSecondary, itemBuilder: (BuildContext context) => >[ const PopupMenuItem<_SecondaryDestination>( value: _SecondaryDestination.ideas, child: Text('Ideas'), ), const PopupMenuItem<_SecondaryDestination>( value: _SecondaryDestination.reminders, child: Text('Reminders'), ), const PopupMenuItem<_SecondaryDestination>( value: _SecondaryDestination.sync, child: Text('Sync'), ), const PopupMenuItem<_SecondaryDestination>( value: _SecondaryDestination.settings, child: Text('Settings'), ), ], ), ], ), ), Expanded( child: AnimatedSwitcher( duration: const Duration(milliseconds: 220), child: KeyedSubtree(key: ValueKey(index), child: body), ), ), NavigationBar( selectedIndex: index, onDestinationSelected: onChange, destinations: destinations .map( (_ShellDestination destination) => NavigationDestination( icon: Icon(destination.icon), label: destination.label, ), ) .toList(growable: false), ), ], ); } } class _CompactSecondaryScreen extends StatelessWidget { const _CompactSecondaryScreen({required this.title, required this.child}); final String title; final Widget child; @override Widget build(BuildContext context) { return DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFFF3F8FB), Color(0xFFEAF1F8)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), child: Scaffold( backgroundColor: Colors.transparent, appBar: AppBar(title: Text(title)), body: child, ), ); } } enum _SecondaryDestination { ideas, reminders, sync, settings } class _ShellDestination { const _ShellDestination(this.label, this.icon, this.screenIndex); final String label; final IconData icon; final int screenIndex; }