diff --git a/lib/cubits/settings_cubit.dart b/lib/cubits/settings_cubit.dart index 169c0b25..b0371ee2 100644 --- a/lib/cubits/settings_cubit.dart +++ b/lib/cubits/settings_cubit.dart @@ -5,6 +5,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kitchenowl/config.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/models/server_settings.dart'; import 'package:kitchenowl/services/api/api_service.dart'; import 'package:kitchenowl/services/storage/storage.dart'; @@ -45,7 +46,8 @@ class SettingsCubit extends Cubit { )); if (ApiService.getInstance().serverSettings.featureExpenses != null || - ApiService.getInstance().serverSettings.featurePlanner != null) { + ApiService.getInstance().serverSettings.featurePlanner != null || + ApiService.getInstance().serverSettings.viewOrdering != null) { serverSettings = ApiService.getInstance().serverSettings; } @@ -68,18 +70,44 @@ class SettingsCubit extends Cubit { emit(state.copyWith(dynamicAccentColor: dynamicAccentColor)); } - void setFeaturePlanner(bool featurePlanner) { + void setView(ViewsEnum view, bool value) { + if (view == ViewsEnum.mealPlanner) { + final settings = state.serverSettings.copyWith(featurePlanner: value); + emit(state.copyWith(serverSettings: settings)); + PreferenceStorage.getInstance() + .write(key: 'serverSettings', value: jsonEncode(settings.toJson())); + ApiService.getInstance() + .setSettings(ServerSettings(featurePlanner: value)); + } + if (view == ViewsEnum.balances) { + final settings = state.serverSettings.copyWith(featureExpenses: value); + emit(state.copyWith(serverSettings: settings)); + PreferenceStorage.getInstance() + .write(key: 'serverSettings', value: jsonEncode(settings.toJson())); + ApiService.getInstance() + .setSettings(ServerSettings(featureExpenses: value)); + } + } + + void reorderView(int oldIndex, int newIndex) { + final l = List.of(state.serverSettings.viewOrdering!); + l.insert(newIndex, l.removeAt(oldIndex)); + final settings = state.serverSettings.copyWith(viewOrdering: l); + emit(state.copyWith(serverSettings: settings)); PreferenceStorage.getInstance() - .writeBool(key: 'featurePlanner', value: featurePlanner); - ApiService.getInstance() - .setSettings(ServerSettings(featurePlanner: featurePlanner)); + .write(key: 'serverSettings', value: jsonEncode(settings.toJson())); + ApiService.getInstance().setSettings(ServerSettings(viewOrdering: l)); } - void setFeatureExpenses(bool featureExpenses) { + void resetViewOrder() { + final settings = + state.serverSettings.copyWith(viewOrdering: ViewsEnum.values); + emit(state.copyWith(serverSettings: settings)); PreferenceStorage.getInstance() - .writeBool(key: 'featureExpenses', value: featureExpenses); - ApiService.getInstance() - .setSettings(ServerSettings(featureExpenses: featureExpenses)); + .write(key: 'serverSettings', value: jsonEncode(settings.toJson())); + ApiService.getInstance().setSettings(const ServerSettings( + viewOrdering: ViewsEnum.values, + )); } } @@ -108,4 +136,15 @@ class SettingsState extends Equatable { @override List get props => [themeMode, serverSettings, dynamicAccentColor]; + + bool isViewActive(ViewsEnum view) { + if (view == ViewsEnum.mealPlanner) { + return serverSettings.featurePlanner ?? true; + } + if (view == ViewsEnum.balances) { + return serverSettings.featureExpenses ?? true; + } + + return true; + } } diff --git a/lib/enums/views_enum.dart b/lib/enums/views_enum.dart new file mode 100644 index 00000000..23cbec25 --- /dev/null +++ b/lib/enums/views_enum.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:kitchenowl/kitchenowl.dart'; + +enum ViewsEnum { + shoppingList, + recipes, + mealPlanner, + balances, + profile; + + String toLocalizedString(BuildContext context) { + final loc = AppLocalizations.of(context)!; + + return [ + loc.shoppingList, + loc.recipes, + loc.mealPlanner, + loc.balances, + loc.profile, + ][index]; + } + + IconData toIcon() { + return const [ + Icons.shopping_bag_outlined, + Icons.receipt, + Icons.calendar_today_rounded, + Icons.account_balance_rounded, + Icons.person, + ][index]; + } + + bool isOptional() { + return const [ + false, + false, + true, + true, + false, + ][index]; + } + + /// Adds all missing views + static List addMissing(Iterable iterable) { + final l = iterable.toList(); + l.addAll(ViewsEnum.values.where((e) => !iterable.contains(e))); + + return l; + } + + static ViewsEnum? parse(String str) { + switch (str) { + case 'shoppingList': + return ViewsEnum.shoppingList; + case 'recipes': + return ViewsEnum.recipes; + case 'mealPlanner': + return ViewsEnum.mealPlanner; + case 'balances': + return ViewsEnum.balances; + case 'profile': + return ViewsEnum.profile; + default: + return null; + } + } + + @override + String toString() { + return name; + } +} diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 34ad8884..829cea65 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -179,6 +179,8 @@ "@mealPlanner": {}, "minutesAbbrev": "Min", "@minutesAbbrev": {}, + "more": "Mehr", + "@more": {}, "name": "Name", "@name": {}, "next": "Weiter", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e306cd01..95230df1 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -185,6 +185,8 @@ "@mealPlanner": {}, "minutesAbbrev": "min", "@minutesAbbrev": {}, + "more": "More", + "@more": {}, "name": "Name", "@name": {}, "next": "Next", diff --git a/lib/models/server_settings.dart b/lib/models/server_settings.dart index 12b8857f..749a7f07 100644 --- a/lib/models/server_settings.dart +++ b/lib/models/server_settings.dart @@ -1,18 +1,35 @@ +import 'package:kitchenowl/enums/views_enum.dart'; + import 'model.dart'; class ServerSettings extends Model { final bool? featurePlanner; final bool? featureExpenses; + final List? viewOrdering; - const ServerSettings({this.featurePlanner, this.featureExpenses}); + const ServerSettings({ + this.featurePlanner, + this.featureExpenses, + this.viewOrdering, + }); - factory ServerSettings.fromJson(Map map) => ServerSettings( - featurePlanner: map['planner_feature'] ?? false, - featureExpenses: map['expenses_feature'] ?? false, - ); + factory ServerSettings.fromJson(Map map) { + List viewOrdering = ViewsEnum.values; + if (map.containsKey('view_ordering')) { + viewOrdering = ViewsEnum.addMissing(List.from(map['view_ordering'] + .map((e) => ViewsEnum.parse(e)) + .where((e) => e != null))); + } + + return ServerSettings( + featurePlanner: map['planner_feature'] ?? false, + featureExpenses: map['expenses_feature'] ?? false, + viewOrdering: viewOrdering, + ); + } @override - List get props => [featurePlanner, featureExpenses]; + List get props => [featurePlanner, featureExpenses, viewOrdering]; @override Map toJson() { @@ -23,6 +40,9 @@ class ServerSettings extends Model { if (featureExpenses != null) { data['expenses_feature'] = featureExpenses; } + if (viewOrdering != null) { + data['view_ordering'] = viewOrdering!.map((e) => e.toString()).toList(); + } return data; } @@ -30,14 +50,17 @@ class ServerSettings extends Model { ServerSettings copyWith({ bool? featurePlanner, bool? featureExpenses, + List? viewOrdering, }) => ServerSettings( featurePlanner: featurePlanner ?? this.featurePlanner, featureExpenses: featureExpenses ?? this.featureExpenses, + viewOrdering: viewOrdering ?? this.viewOrdering, ); ServerSettings copyFrom(ServerSettings serverSettings) => ServerSettings( featurePlanner: serverSettings.featurePlanner ?? featurePlanner, featureExpenses: serverSettings.featureExpenses ?? featureExpenses, + viewOrdering: serverSettings.viewOrdering ?? viewOrdering, ); } diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index da10f917..0646a5c1 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -7,6 +7,7 @@ import 'package:kitchenowl/cubits/planner_cubit.dart'; import 'package:kitchenowl/cubits/recipe_list_cubit.dart'; import 'package:kitchenowl/cubits/settings_cubit.dart'; import 'package:kitchenowl/cubits/shoppinglist_cubit.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/pages/home_page/_export.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -18,6 +19,8 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { + static const int _bottomAppBarSize = 5; + final ShoppinglistCubit shoppingListCubit = ShoppinglistCubit(); final RecipeListCubit recipeListCubit = RecipeListCubit(); final PlannerCubit plannerCubit = PlannerCubit(); @@ -25,7 +28,6 @@ class _HomePageState extends State { late List pages; int _selectedIndex = 0; - int offset = 0; @override void initState() { @@ -73,16 +75,40 @@ class _HomePageState extends State { final _offset = (state.serverSettings.featurePlanner ?? false ? 0 : 1) + (state.serverSettings.featureExpenses ?? false ? 0 : 1); - _selectedIndex = (_selectedIndex * - (pages.length - _offset) / - (pages.length - offset)) - .clamp(0, pages.length - _offset) - .round(); - - offset = _offset; + _selectedIndex = + _selectedIndex.clamp(0, pages.length - 1 - _offset); }, builder: (context, state) { - final _pages = pages.where((e) => e.isActive(context)).toList(); + List _pages = + (state.serverSettings.viewOrdering ?? ViewsEnum.values) + .map((e) { + final i = pages.indexWhere( + (page) => page.type() == e, + ); + + return i >= 0 ? pages[i] : null; + }) + .where((e) => e != null && e.isActive(context)) + .cast() + .toList(); + + final bool useBottomNavigationBar = getValueForScreenType( + context: context, + mobile: true, + tablet: false, + desktop: false, + ); + + if (useBottomNavigationBar && _bottomAppBarSize < _pages.length) { + _selectedIndex = _selectedIndex.clamp(0, _bottomAppBarSize - 1); + _pages.insert( + _bottomAppBarSize - 1, + OverflowPage( + pages: _pages.sublist(_bottomAppBarSize - 1, _pages.length), + ), + ); + _pages = _pages.sublist(0, _bottomAppBarSize); + } Widget body = PageTransitionSwitcher( transitionBuilder: ( @@ -106,13 +132,6 @@ class _HomePageState extends State { ), ); - final bool useBottomNavigationBar = getValueForScreenType( - context: context, - mobile: true, - tablet: false, - desktop: false, - ); - if (!useBottomNavigationBar) { final bool extendedRail = getValueForScreenType( context: context, @@ -151,7 +170,6 @@ class _HomePageState extends State { labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, destinations: _pages - .where((e) => e.isActive(context)) .map((e) => NavigationDestination( icon: Icon(e.icon(context)), label: e.label(context), diff --git a/lib/pages/home_page/_export.dart b/lib/pages/home_page/_export.dart index f6558666..2899e792 100644 --- a/lib/pages/home_page/_export.dart +++ b/lib/pages/home_page/_export.dart @@ -4,3 +4,4 @@ export 'shoppinglist.dart'; export 'planner.dart'; export 'expense_list.dart'; export 'home_page_item.dart'; +export 'overflow.dart'; diff --git a/lib/pages/home_page/expense_list.dart b/lib/pages/home_page/expense_list.dart index 22233092..a62d79c0 100644 --- a/lib/pages/home_page/expense_list.dart +++ b/lib/pages/home_page/expense_list.dart @@ -8,6 +8,7 @@ import 'package:kitchenowl/cubits/expense_list_cubit.dart'; import 'package:kitchenowl/cubits/settings_cubit.dart'; import 'package:kitchenowl/enums/expenselist_sorting.dart'; import 'package:kitchenowl/enums/update_enum.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/kitchenowl.dart'; import 'package:kitchenowl/models/user.dart'; import 'package:kitchenowl/pages/expense_add_update_page.dart'; @@ -26,10 +27,7 @@ class ExpenseListPage extends StatefulWidget with HomePageItem { _ExpensePageState createState() => _ExpensePageState(); @override - IconData icon(BuildContext context) => Icons.account_balance_rounded; - - @override - String label(BuildContext context) => AppLocalizations.of(context)!.balances; + ViewsEnum type() => ViewsEnum.balances; @override void onSelected(BuildContext context, bool alreadySelected) { diff --git a/lib/pages/home_page/home_page_item.dart b/lib/pages/home_page/home_page_item.dart index e5e77bb7..770fcde8 100644 --- a/lib/pages/home_page/home_page_item.dart +++ b/lib/pages/home_page/home_page_item.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; mixin HomePageItem on Widget { // ignore: no-empty-block void onSelected(BuildContext context, bool alreadySelected) {} - IconData icon(BuildContext context); - String label(BuildContext context); + ViewsEnum type(); + String label(BuildContext context) => type().toLocalizedString(context); + IconData icon(BuildContext context) => type().toIcon(); Widget? floatingActionButton(BuildContext context) => null; bool isActive(BuildContext context) => true; } diff --git a/lib/pages/home_page/home_page_item_wrapper.dart b/lib/pages/home_page/home_page_item_wrapper.dart new file mode 100644 index 00000000..37f7b85b --- /dev/null +++ b/lib/pages/home_page/home_page_item_wrapper.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:kitchenowl/pages/home_page/home_page_item.dart'; + +class HomePageItemWrapper extends StatelessWidget { + final HomePageItem homePageItem; + + const HomePageItemWrapper({super.key, required this.homePageItem}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(homePageItem.label(context))), + body: homePageItem, + floatingActionButton: homePageItem.floatingActionButton(context), + ); + } +} diff --git a/lib/pages/home_page/overflow.dart b/lib/pages/home_page/overflow.dart new file mode 100644 index 00000000..8094c4aa --- /dev/null +++ b/lib/pages/home_page/overflow.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:kitchenowl/app.dart'; +import 'package:kitchenowl/cubits/expense_list_cubit.dart'; +import 'package:kitchenowl/cubits/planner_cubit.dart'; +import 'package:kitchenowl/cubits/recipe_list_cubit.dart'; +import 'package:kitchenowl/cubits/settings_cubit.dart'; +import 'package:kitchenowl/cubits/shoppinglist_cubit.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; +import 'package:kitchenowl/kitchenowl.dart'; +import 'package:kitchenowl/pages/home_page/home_page_item_wrapper.dart'; + +import 'home_page_item.dart'; + +class OverflowPage extends StatelessWidget with HomePageItem { + final List pages; + + const OverflowPage({super.key, required this.pages}); + + @override + Widget build(BuildContext context) { + return CustomScrollView( + primary: true, + slivers: [ + BlocBuilder( + builder: (context, state) => SliverFillRemaining( + hasScrollBody: false, + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: pages + .map( + (view) => Card( + child: ListTile( + title: Text(view.label(context)), + leading: Icon(view.icon(context)), + minLeadingWidth: 16, + onTap: () { + view.onSelected(context, false); + Navigator.of(context).push(MaterialPageRoute( + builder: (ctx) => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: BlocProvider.of( + context, + ), + ), + BlocProvider.value( + value: BlocProvider.of( + context), + ), + BlocProvider.value( + value: + BlocProvider.of(context), + ), + BlocProvider.value( + value: BlocProvider.of( + context, + ), + ), + ], + child: HomePageItemWrapper( + homePageItem: view, + ), + ), + )); + }, + ), + ), + ) + .toList(), + ), + ), + ), + ), + ], + ); + } + + @override + IconData icon(context) => + App.isOffline ? Icons.cloud_off_rounded : Icons.view_headline_rounded; + + @override + ViewsEnum type() => ViewsEnum.profile; + + @override + String label(context) => AppLocalizations.of(context)!.more; +} diff --git a/lib/pages/home_page/planner.dart b/lib/pages/home_page/planner.dart index a1b16a7b..34908a0a 100644 --- a/lib/pages/home_page/planner.dart +++ b/lib/pages/home_page/planner.dart @@ -4,6 +4,7 @@ import 'package:intl/intl.dart'; import 'package:kitchenowl/cubits/planner_cubit.dart'; import 'package:kitchenowl/cubits/settings_cubit.dart'; import 'package:kitchenowl/enums/update_enum.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/kitchenowl.dart'; import 'package:kitchenowl/models/item.dart'; import 'package:kitchenowl/models/recipe.dart'; @@ -21,10 +22,7 @@ class PlannerPage extends StatefulWidget with HomePageItem { _PlannerPageState createState() => _PlannerPageState(); @override - IconData icon(BuildContext context) => Icons.calendar_today_rounded; - - @override - String label(BuildContext context) => AppLocalizations.of(context)!.planner; + ViewsEnum type() => ViewsEnum.mealPlanner; @override void onSelected(BuildContext context, bool alreadySelected) { diff --git a/lib/pages/home_page/profile.dart b/lib/pages/home_page/profile.dart index 0bbfe6ef..e86125cb 100644 --- a/lib/pages/home_page/profile.dart +++ b/lib/pages/home_page/profile.dart @@ -6,6 +6,7 @@ import 'package:kitchenowl/app.dart'; import 'package:kitchenowl/config.dart'; import 'package:kitchenowl/cubits/auth_cubit.dart'; import 'package:kitchenowl/cubits/settings_cubit.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/pages/settings_server_page.dart'; import 'package:kitchenowl/pages/settings_user_page.dart'; import 'package:kitchenowl/kitchenowl.dart'; @@ -197,8 +198,8 @@ class ProfilePage extends StatelessWidget with HomePageItem { @override IconData icon(context) => - App.isOffline ? Icons.cloud_off_rounded : Icons.person; + App.isOffline ? Icons.cloud_off_rounded : type().toIcon(); @override - String label(context) => AppLocalizations.of(context)!.profile; + ViewsEnum type() => ViewsEnum.profile; } diff --git a/lib/pages/home_page/recipe_list.dart b/lib/pages/home_page/recipe_list.dart index 91a316ce..454dfc9c 100644 --- a/lib/pages/home_page/recipe_list.dart +++ b/lib/pages/home_page/recipe_list.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kitchenowl/app.dart'; import 'package:kitchenowl/cubits/recipe_list_cubit.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/kitchenowl.dart'; import 'package:kitchenowl/widgets/recipe_create_fab.dart'; import 'package:kitchenowl/widgets/recipe_item.dart'; @@ -18,10 +19,10 @@ class RecipeListPage extends StatefulWidget with HomePageItem { _RecipeListPageState createState() => _RecipeListPageState(); @override - IconData icon(BuildContext context) => Icons.shopping_bag_outlined; + IconData icon(BuildContext context) => Icons.receipt_rounded; @override - String label(BuildContext context) => AppLocalizations.of(context)!.recipes; + ViewsEnum type() => ViewsEnum.recipes; @override void onSelected(BuildContext context, bool alreadySelected) { diff --git a/lib/pages/home_page/shoppinglist.dart b/lib/pages/home_page/shoppinglist.dart index 43477a4e..dcd715e3 100644 --- a/lib/pages/home_page/shoppinglist.dart +++ b/lib/pages/home_page/shoppinglist.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kitchenowl/app.dart'; import 'package:kitchenowl/cubits/shoppinglist_cubit.dart'; import 'package:kitchenowl/enums/shoppinglist_sorting.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/models/category.dart'; import 'package:kitchenowl/models/item.dart'; import 'package:kitchenowl/kitchenowl.dart'; @@ -16,11 +17,7 @@ class ShoppinglistPage extends StatefulWidget with HomePageItem { _ShoppinglistPageState createState() => _ShoppinglistPageState(); @override - IconData icon(BuildContext context) => Icons.shopping_bag_outlined; - - @override - String label(BuildContext context) => - AppLocalizations.of(context)!.shoppingList; + ViewsEnum type() => ViewsEnum.shoppingList; @override void onSelected(BuildContext context, bool alreadySelected) { diff --git a/lib/widgets/settings_server/sliver_server_features_settings.dart b/lib/widgets/settings_server/sliver_server_features_settings.dart index f1452e58..f81b09ef 100644 --- a/lib/widgets/settings_server/sliver_server_features_settings.dart +++ b/lib/widgets/settings_server/sliver_server_features_settings.dart @@ -2,9 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kitchenowl/cubits/settings_cubit.dart'; import 'package:kitchenowl/cubits/settings_server_cubit.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; import 'package:kitchenowl/kitchenowl.dart'; import 'package:kitchenowl/services/api/api_service.dart'; import 'package:kitchenowl/widgets/language_dialog.dart'; +import 'package:kitchenowl/widgets/settings_server/view_settings_list_tile.dart'; +import 'package:reorderables/reorderables.dart'; class SliverServerFeaturesSettings extends StatelessWidget { const SliverServerFeaturesSettings({Key? key}) : super(key: key); @@ -27,42 +30,46 @@ class SliverServerFeaturesSettings extends StatelessWidget { ), ), const SizedBox(height: 8), - Text( - '${AppLocalizations.of(context)!.features}:', - style: Theme.of(context).textTheme.headline6, - ), - const SizedBox(height: 8), BlocBuilder( - builder: (context, state) => Column( - mainAxisSize: MainAxisSize.min, + builder: (context, state) => Row( children: [ - ListTile( - title: Text( - AppLocalizations.of(context)!.mealPlanner, - ), - leading: const Icon(Icons.calendar_today_rounded), - contentPadding: const EdgeInsets.only(left: 20, right: 0), - trailing: KitchenOwlSwitch( - value: state.serverSettings.featurePlanner ?? false, - onChanged: - BlocProvider.of(context).setFeaturePlanner, + Expanded( + child: Text( + '${AppLocalizations.of(context)!.features}:', + style: Theme.of(context).textTheme.headline6, ), ), - ListTile( - title: Text( - AppLocalizations.of(context)!.balances, + if (state.serverSettings.viewOrdering != ViewsEnum.values) + IconButton( + onPressed: + BlocProvider.of(context).resetViewOrder, + icon: const Icon(Icons.restart_alt_rounded), + padding: EdgeInsets.zero, ), - leading: const Icon(Icons.account_balance_rounded), - contentPadding: const EdgeInsets.only(left: 20, right: 0), - trailing: KitchenOwlSwitch( - value: state.serverSettings.featureExpenses ?? false, - onChanged: BlocProvider.of(context) - .setFeatureExpenses, - ), - ), ], ), ), + const SizedBox(height: 8), + BlocBuilder( + builder: (context, state) => ReorderableColumn( + onReorder: BlocProvider.of(context).reorderView, + children: state.serverSettings.viewOrdering! + .sublist(0, state.serverSettings.viewOrdering!.length - 1) + .map( + (view) => ViewSettingsListTile( + key: ValueKey(view), + view: view, + isActive: state.isViewActive(view), + ), + ) + .toList(), + ), + ), + const ViewSettingsListTile( + view: ViewsEnum.profile, + showHandleIfNotOptional: false, + ), + const Divider(), ListTile( title: Text(AppLocalizations.of(context)!.language), leading: const Icon(Icons.language_rounded), diff --git a/lib/widgets/settings_server/view_settings_list_tile.dart b/lib/widgets/settings_server/view_settings_list_tile.dart new file mode 100644 index 00000000..ec489d38 --- /dev/null +++ b/lib/widgets/settings_server/view_settings_list_tile.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:kitchenowl/cubits/settings_cubit.dart'; +import 'package:kitchenowl/enums/views_enum.dart'; +import 'package:kitchenowl/kitchenowl.dart'; + +class ViewSettingsListTile extends StatelessWidget { + final ViewsEnum view; + final bool isActive; + final bool showHandleIfNotOptional; + + const ViewSettingsListTile({ + super.key, + required this.view, + this.isActive = false, + this.showHandleIfNotOptional = true, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text( + view.toLocalizedString(context), + ), + leading: Icon(view.toIcon()), + contentPadding: const EdgeInsets.only(left: 20, right: 0), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (view.isOptional()) + KitchenOwlSwitch( + value: isActive, + onChanged: (value) => + BlocProvider.of(context).setView(view, value), + ), + if (view.isOptional()) + const VerticalDivider( + endIndent: 4, + indent: 4, + ), + if (showHandleIfNotOptional) + const Padding( + padding: EdgeInsets.only(left: 4, right: 16), + child: Icon(Icons.drag_handle), + ), + ], + ), + ); + } +}