Files
recipe-app/flutter/lib/core/ui/app_shell.dart
T

204 lines
6.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../features/auth/data/auth_providers.dart';
import '../../features/recipes/data/recipes_grid_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
const _adminDestination = _AppDestination(
path: '/admin',
title: 'Admin',
icon: Icons.admin_panel_settings_outlined,
label: 'Admin',
);
class AppShell extends ConsumerWidget {
final String location;
final Widget child;
const AppShell({
super.key,
required this.location,
required this.child,
});
static const _baseDestinations = [
_AppDestination(
path: '/recipes',
title: 'Recept',
icon: Icons.restaurant_menu,
label: 'Recept',
),
_AppDestination(
path: '/inventory',
title: 'Inventarie',
icon: Icons.inventory_2_outlined,
label: 'Inventarie',
),
_AppDestination(
path: '/matsedel',
title: 'Matsedel',
icon: Icons.calendar_month_outlined,
label: 'Matsedel',
),
_AppDestination(
path: '/baslager',
title: 'Baslager',
icon: Icons.storefront_outlined,
label: 'Baslager',
),
_AppDestination(
path: '/import',
title: 'Importera',
icon: Icons.upload_file_outlined,
label: 'Importera',
),
_AppDestination(
path: '/profile',
title: 'Profil',
icon: Icons.person,
label: 'Profil',
),
];
List<_AppDestination> _destinations(bool isAdmin) => [
..._baseDestinations,
if (isAdmin) _adminDestination,
];
int _selectedIndex(List<_AppDestination> destinations) {
final index = destinations.indexWhere(
(destination) => location.startsWith(destination.path),
);
return index < 0 ? 0 : index;
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final isAdmin = ref.watch(isAdminProvider);
final dests = _destinations(isAdmin);
final selectedIndex = _selectedIndex(dests);
final selectedDestination = dests[selectedIndex];
final isWide = MediaQuery.of(context).size.width >= 900;
Future<void> logout() async {
await ref.read(authStateProvider.notifier).logout();
if (context.mounted) {
context.go('/login');
}
}
void navigateTo(int index) {
final target = dests[index].path;
if (target != location && context.mounted) {
context.go(target);
}
}
final isRecipesRoute = location.startsWith('/recipes') &&
!location.startsWith('/recipes/');
return Scaffold(
appBar: AppBar(
title: Text(selectedDestination.title),
actions: [
if (isRecipesRoute)
Consumer(
builder: (context, ref, child) {
final view = ref.watch(recipesViewProvider).maybeWhen(
data: (v) => v,
orElse: () => (mode: RecipesViewMode.grid, columns: 2),
);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
tooltip: view.mode == RecipesViewMode.grid
? 'Visa som lista'
: 'Visa som grid',
icon: Icon(view.mode == RecipesViewMode.grid
? Icons.view_list
: Icons.grid_view),
onPressed: () =>
ref.read(recipesViewProvider.notifier).toggleMode(),
),
if (view.mode == RecipesViewMode.grid)
PopupMenuButton<int>(
icon: const Icon(Icons.grid_view),
tooltip: 'Välj antal kolumner',
onSelected: (columns) =>
ref.read(recipesViewProvider.notifier).setColumns(columns),
itemBuilder: (context) => const [
PopupMenuItem(value: 2, child: Text('2 kolumner')),
PopupMenuItem(value: 4, child: Text('4 kolumner')),
PopupMenuItem(value: 6, child: Text('6 kolumner')),
PopupMenuItem(value: 8, child: Text('8 kolumner')),
],
),
],
);
},
),
IconButton(
tooltip: 'Logga ut',
icon: const Icon(Icons.logout),
onPressed: logout,
),
],
),
body: isWide
? Row(
children: [
NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: navigateTo,
labelType: NavigationRailLabelType.all,
destinations: dests
.map(
(destination) => NavigationRailDestination(
icon: Icon(destination.icon),
label: Text(destination.label),
),
)
.toList(),
),
const VerticalDivider(width: 1),
Expanded(child: child),
],
)
: child,
bottomNavigationBar: isWide
? null
: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: navigateTo,
destinations: dests
.map(
(destination) => NavigationDestination(
icon: Icon(destination.icon),
label: destination.label,
),
)
.toList(),
),
);
}
}
class _AppDestination {
final String path;
final String title;
final IconData icon;
final String label;
const _AppDestination({
required this.path,
required this.title,
required this.icon,
required this.label,
});
}