feat: refactor RecipesScreen and RecipesViewNotifier to support dynamic view modes and column selection
This commit is contained in:
@@ -4,6 +4,9 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
import '../../features/auth/data/auth_providers.dart';
|
import '../../features/auth/data/auth_providers.dart';
|
||||||
import '../../features/recipes/data/recipes_grid_provider.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(
|
const _adminDestination = _AppDestination(
|
||||||
path: '/admin',
|
path: '/admin',
|
||||||
@@ -102,12 +105,30 @@ class AppShell extends ConsumerWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(selectedDestination.title),
|
title: Text(selectedDestination.title),
|
||||||
actions: [
|
actions: [
|
||||||
if (isRecipesRoute)
|
if (isRecipesRoute) ...existing code...
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final view = ref.watch(recipesViewProvider).valueOrNull ??
|
||||||
|
(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>(
|
PopupMenuButton<int>(
|
||||||
icon: const Icon(Icons.grid_view),
|
icon: const Icon(Icons.grid_view),
|
||||||
tooltip: 'Välj antal kolumner',
|
tooltip: 'Välj antal kolumner',
|
||||||
onSelected: (columns) =>
|
onSelected: (columns) =>
|
||||||
ref.read(recipesGridProvider.notifier).setColumns(columns),
|
ref.read(recipesViewProvider.notifier).setColumns(columns),
|
||||||
itemBuilder: (context) => const [
|
itemBuilder: (context) => const [
|
||||||
PopupMenuItem(value: 2, child: Text('2 kolumner')),
|
PopupMenuItem(value: 2, child: Text('2 kolumner')),
|
||||||
PopupMenuItem(value: 4, child: Text('4 kolumner')),
|
PopupMenuItem(value: 4, child: Text('4 kolumner')),
|
||||||
@@ -115,6 +136,10 @@ class AppShell extends ConsumerWidget {
|
|||||||
PopupMenuItem(value: 8, child: Text('8 kolumner')),
|
PopupMenuItem(value: 8, child: Text('8 kolumner')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: 'Logga ut',
|
tooltip: 'Logga ut',
|
||||||
icon: const Icon(Icons.logout),
|
icon: const Icon(Icons.logout),
|
||||||
|
|||||||
@@ -1,21 +1,36 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
const _prefsKey = 'recipes_grid_columns';
|
enum RecipesViewMode { grid, list }
|
||||||
|
|
||||||
|
class RecipesViewNotifier extends AsyncNotifier<({RecipesViewMode mode, int columns})> {
|
||||||
|
static const _modeKey = 'recipes_view_mode';
|
||||||
|
static const _columnsKey = 'recipes_grid_columns';
|
||||||
|
|
||||||
class RecipesGridNotifier extends AsyncNotifier<int> {
|
|
||||||
@override
|
@override
|
||||||
Future<int> build() async {
|
Future<({RecipesViewMode mode, int columns})> build() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
return prefs.getInt(_prefsKey) ?? 2;
|
final mode = RecipesViewMode.values[prefs.getInt(_modeKey) ?? 0];
|
||||||
|
final columns = prefs.getInt(_columnsKey) ?? 2;
|
||||||
|
return (mode: mode, columns: columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> toggleMode() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final current = state.valueOrNull ?? (mode: RecipesViewMode.grid, columns: 2);
|
||||||
|
final newMode = current.mode == RecipesViewMode.grid ? RecipesViewMode.list : RecipesViewMode.grid;
|
||||||
|
await prefs.setInt(_modeKey, newMode.index);
|
||||||
|
state = AsyncData((mode: newMode, columns: current.columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setColumns(int columns) async {
|
Future<void> setColumns(int columns) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setInt(_prefsKey, columns);
|
final current = state.valueOrNull ?? (mode: RecipesViewMode.grid, columns: 2);
|
||||||
state = AsyncData(columns);
|
await prefs.setInt(_columnsKey, columns);
|
||||||
|
state = AsyncData((mode: current.mode, columns: columns));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final recipesGridProvider =
|
final recipesViewProvider =
|
||||||
AsyncNotifierProvider<RecipesGridNotifier, int>(RecipesGridNotifier.new);
|
AsyncNotifierProvider<RecipesViewNotifier, ({RecipesViewMode mode, int columns})>(
|
||||||
|
RecipesViewNotifier.new);
|
||||||
|
|||||||
@@ -13,10 +13,8 @@ class RecipesScreen extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final recipesAsync = ref.watch(recipesProvider);
|
final recipesAsync = ref.watch(recipesProvider);
|
||||||
final columns = ref.watch(recipesGridProvider).maybeWhen(
|
final view = ref.watch(recipesViewProvider).valueOrNull ??
|
||||||
data: (v) => v,
|
(mode: RecipesViewMode.grid, columns: 2);
|
||||||
orElse: () => 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -34,10 +32,11 @@ class RecipesScreen extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view.mode == RecipesViewMode.grid) {
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
padding: const EdgeInsets.only(bottom: 88),
|
padding: const EdgeInsets.only(bottom: 88),
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: columns,
|
crossAxisCount: view.columns,
|
||||||
crossAxisSpacing: 4,
|
crossAxisSpacing: 4,
|
||||||
mainAxisSpacing: 4,
|
mainAxisSpacing: 4,
|
||||||
),
|
),
|
||||||
@@ -64,6 +63,25 @@ class RecipesScreen extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.only(bottom: 88),
|
||||||
|
itemCount: recipes.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final recipe = recipes[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: recipe.imageUrl != null
|
||||||
|
? CircleAvatar(
|
||||||
|
backgroundImage: NetworkImage(recipe.imageUrl!),
|
||||||
|
)
|
||||||
|
: const CircleAvatar(child: Icon(Icons.restaurant)),
|
||||||
|
title: Text(recipe.name),
|
||||||
|
subtitle: Text(recipe.description ?? ''),
|
||||||
|
onTap: () => context.push('/recipes/${recipe.id}'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
|||||||
Reference in New Issue
Block a user