From cef14508a2a99ab2b1e93097c8d6b99dd120c0f1 Mon Sep 17 00:00:00 2001 From: Andrii Denysenko Date: Thu, 7 Mar 2024 09:09:57 -0400 Subject: [PATCH] Conditional tab order (#98) * Rename borrowed items * Switch order of first 2 tabs depending on the role * Create TabFactory * Add page titles list to the factory * Extract desktop pages * Refactor repetition --- lib/pages/home_page/home_page.dart | 107 +++++---------------- lib/pages/home_page/tab_factory.dart | 116 +++++++++++++++++++++++ test/pages/home_page/home_page_test.dart | 40 ++++++++ 3 files changed, 179 insertions(+), 84 deletions(-) create mode 100644 lib/pages/home_page/tab_factory.dart create mode 100644 test/pages/home_page/home_page_test.dart diff --git a/lib/pages/home_page/home_page.dart b/lib/pages/home_page/home_page.dart index 967ec38..dc08423 100644 --- a/lib/pages/home_page/home_page.dart +++ b/lib/pages/home_page/home_page.dart @@ -1,16 +1,13 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:spare_parts/pages/home_page/borrowed_items_view.dart'; -import 'package:spare_parts/pages/home_page/borrowing_requests_view/borrowing_requests_view.dart'; -import 'package:spare_parts/pages/home_page/inventory_view/inventory_view.dart'; import 'package:spare_parts/pages/home_page/settings_view/settings_view.dart'; +import 'package:spare_parts/pages/home_page/tab_factory.dart'; import 'package:spare_parts/pages/qr_scan_page.dart'; import 'package:spare_parts/services/repositories/inventory_item_repository.dart'; import 'package:spare_parts/utilities/constants.dart'; import 'package:spare_parts/widgets/add_inventory_item_button.dart'; import 'package:spare_parts/widgets/custom_layout_builder.dart'; -import 'package:spare_parts/widgets/title_text.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -21,8 +18,16 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { int _selectedBottomNavItemIndex = 0; - String _pageTitle = 'Spare Parts'; + late String _pageTitle; final PageController pageController = PageController(); + late final TabFactory tabFactory; + + @override + void initState() { + _pageTitle = isAdmin ? 'Inventory' : 'My Items'; + tabFactory = TabFactory(isAdmin: isAdmin, isDesktop: false); + super.initState(); + } void _handleSignOut() { final auth = context.read(); @@ -75,19 +80,7 @@ class _HomePageState extends State { setState(() { _selectedBottomNavItemIndex = index; - switch (index) { - case 1: - _pageTitle = 'Borrowed Items'; - break; - case 2: - _pageTitle = 'Borrowing Requests'; - break; - case 3: - _pageTitle = 'Settings'; - break; - default: - _pageTitle = 'Spare Parts'; - } + _pageTitle = tabFactory.getPageTitles()[index]; }); } @@ -103,20 +96,22 @@ class _HomePageState extends State { Widget build(BuildContext context) { return CustomLayoutBuilder( builder: (context, layout) { + final isDesktop = layout == LayoutType.desktop; + tabFactory.isDesktop = isDesktop; + return Scaffold( - floatingActionButtonLocation: layout == LayoutType.desktop - ? FloatingActionButtonLocation.startFloat - : null, + floatingActionButtonLocation: + isDesktop ? FloatingActionButtonLocation.startFloat : null, appBar: AppBar( centerTitle: true, - title: Text(_pageTitle), + title: Text(isDesktop ? 'Spare Parts' : _pageTitle), actions: [ IconButton( icon: const Icon(Icons.qr_code_scanner), onPressed: _handleScan, color: Theme.of(context).colorScheme.primary, ), - if (layout == LayoutType.desktop && isAdmin) + if (isDesktop && isAdmin) TextButton.icon( label: Text('Settings'), onPressed: _handleSettings, @@ -130,76 +125,20 @@ class _HomePageState extends State { ], ), floatingActionButton: isAdmin ? AddInventoryItemButton() : null, - body: layout == LayoutType.desktop + body: isDesktop ? SizedBox( - child: Row( - children: [ - Expanded( - child: Column( - children: const [ - TitleText('Inventory'), - Expanded(child: InventoryView()), - ], - ), - ), - VerticalDivider(), - Expanded( - child: Column( - children: const [ - TitleText('Borrowed Items'), - Expanded(child: BorrowedItemsView()) - ], - ), - ), - VerticalDivider(), - Expanded( - child: Column( - children: const [ - TitleText('Borrowing Requests'), - Expanded(child: BorrowingRequestsView()) - ], - ), - ) - ], - ), + child: Row(children: tabFactory.getDesktopPages()), ) : PageView( controller: pageController, onPageChanged: _onPageChanged, - children: [ - InventoryView(), - BorrowedItemsView(), - BorrowingRequestsView(), - if (isAdmin) SettingsView() - ], + children: tabFactory.getPages(), ), - bottomNavigationBar: layout == LayoutType.desktop + bottomNavigationBar: isDesktop ? null : BottomNavigationBar( type: BottomNavigationBarType.fixed, - items: [ - BottomNavigationBarItem( - icon: Icon(Icons.home_outlined), - activeIcon: Icon(Icons.home), - label: 'Inventory', - ), - BottomNavigationBarItem( - icon: Icon(Icons.backpack_outlined), - activeIcon: Icon(Icons.backpack), - label: 'Borrowed Items', - ), - BottomNavigationBarItem( - icon: Icon(Icons.waving_hand_outlined), - activeIcon: Icon(Icons.waving_hand), - label: 'Borrowing Requests', - ), - if (isAdmin) - BottomNavigationBarItem( - icon: Icon(Icons.settings_outlined), - activeIcon: Icon(Icons.settings), - label: 'Settings', - ), - ], + items: tabFactory.getBottomNavBarItems(), currentIndex: _selectedBottomNavItemIndex, onTap: _onBottomNavItemTapped, ), diff --git a/lib/pages/home_page/tab_factory.dart b/lib/pages/home_page/tab_factory.dart new file mode 100644 index 0000000..57b6cf4 --- /dev/null +++ b/lib/pages/home_page/tab_factory.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:spare_parts/pages/home_page/borrowed_items_view.dart'; +import 'package:spare_parts/pages/home_page/borrowing_requests_view/borrowing_requests_view.dart'; +import 'package:spare_parts/pages/home_page/inventory_view/inventory_view.dart'; +import 'package:spare_parts/pages/home_page/settings_view/settings_view.dart'; +import 'package:spare_parts/widgets/title_text.dart'; + +class TabFactory { + bool isAdmin; + bool isDesktop; + + TabFactory({required this.isAdmin, required this.isDesktop}); + + List getBottomNavBarItems() { + return getTabs() + .map( + (tab) => BottomNavigationBarItem( + icon: Icon(tab.activeIcon), + activeIcon: Icon(tab.inactiveIcon), + label: tab.title, + ), + ) + .toList(); + } + + List getPages() { + return getTabs().map((tab) => tab.widget).toList(); + } + + List getDesktopPages() { + final List pages = []; + final tabs = getTabs(); + + for (var i = 0; i < tabs.length; i++) { + pages.add(Expanded( + child: Column( + children: [ + TitleText(tabs[i].title), + Expanded(child: tabs[i].widget), + ], + ), + )); + + if (i != tabs.length - 1) { + pages.add(VerticalDivider()); + } + } + + return pages; + } + + List getPageTitles() { + return getTabs().map((tab) => tab.title).toList(); + } + + List getTabs() { + final tabs = [ + TabInfo( + title: 'Inventory', + activeIcon: Icons.home_outlined, + inactiveIcon: Icons.home, + widget: InventoryView(), + ordinal: isDesktop + ? 1 + : isAdmin + ? 1 + : 2, + ), + TabInfo( + title: 'My Items', + activeIcon: Icons.backpack_outlined, + inactiveIcon: Icons.backpack, + widget: BorrowedItemsView(), + ordinal: isDesktop + ? 2 + : isAdmin + ? 2 + : 1, + ), + TabInfo( + title: 'Borrowing Requests', + activeIcon: Icons.waving_hand_outlined, + inactiveIcon: Icons.waving_hand, + widget: BorrowingRequestsView(), + ordinal: 3, + ), + TabInfo( + title: 'Settings', + activeIcon: Icons.settings_outlined, + inactiveIcon: Icons.settings, + widget: SettingsView(), + ordinal: isDesktop ? -1 : 4, + ), + ]; + + final activeTabs = tabs.where((tab) => tab.ordinal != -1).toList(); + activeTabs.sort((tab1, tab2) => tab1.ordinal - tab2.ordinal); + return activeTabs; + } +} + +class TabInfo { + final String title; + final IconData activeIcon; + final IconData inactiveIcon; + final int ordinal; + Widget widget; + + TabInfo({ + required this.title, + required this.activeIcon, + required this.inactiveIcon, + required this.widget, + required this.ordinal, + }); +} diff --git a/test/pages/home_page/home_page_test.dart b/test/pages/home_page/home_page_test.dart new file mode 100644 index 0000000..417645c --- /dev/null +++ b/test/pages/home_page/home_page_test.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:spare_parts/pages/home_page/home_page.dart'; +import 'package:spare_parts/utilities/constants.dart'; + +import '../../helpers/test_helpers.dart'; + +void main() { + group('Home Page', () { + testWidgets('renders My Items page when current user is not admin', + (WidgetTester tester) async { + await pumpPage( + HomePage(), + tester, + userRole: UserRole.user, + ); + + final matchingTitle = find.descendant( + of: find.byType(AppBar), + matching: find.text('My Items'), + ); + expect(matchingTitle, findsOneWidget); + }); + + testWidgets('renders Inventory page when current user is admin', + (WidgetTester tester) async { + await pumpPage( + HomePage(), + tester, + userRole: UserRole.admin, + ); + + final matchingTitle = find.descendant( + of: find.byType(AppBar), + matching: find.text('Inventory'), + ); + expect(matchingTitle, findsOneWidget); + }); + }); +}