diff --git a/app/lib/apps/chatbot/chatbot_widget.dart b/app/lib/apps/chatbot/chatbot_widget.dart index 0fb6861d6..4a7825a49 100644 --- a/app/lib/apps/chatbot/chatbot_widget.dart +++ b/app/lib/apps/chatbot/chatbot_widget.dart @@ -3,7 +3,7 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:threebotlogin/apps/chatbot/chatbot_config.dart'; import 'package:threebotlogin/browser.dart'; import 'package:threebotlogin/helpers/logger.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + class ChatbotWidget extends StatefulWidget { final String email; @@ -85,15 +85,13 @@ class _ChatbotState extends State @override Widget build(BuildContext context) { super.build(context); - return LayoutDrawer( - titleText: 'Support', - content: Column( - children: [ - Expanded( - child: Container(child: iaWebview), - ), - ], - )); + return Column( + children: [ + Expanded( + child: Container(child: iaWebview), + ), + ], + ); } @override diff --git a/app/lib/apps/farmers/farmers_widget.dart b/app/lib/apps/farmers/farmers_widget.dart index b422741df..448fa470e 100644 --- a/app/lib/apps/farmers/farmers_widget.dart +++ b/app/lib/apps/farmers/farmers_widget.dart @@ -14,7 +14,7 @@ import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/models/wallet_data.dart'; import 'package:threebotlogin/screens/scan_screen.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + bool created = false; @@ -153,15 +153,13 @@ class _FarmersState extends State @override Widget build(BuildContext context) { super.build(context); - return LayoutDrawer( - titleText: 'Farming', - content: Column( - children: [ - Expanded( - child: Container(child: iaWebView), - ), - ], - )); + return Column( + children: [ + Expanded( + child: Container(child: iaWebView), + ), + ], + ); } @override diff --git a/app/lib/apps/news/news_screen.dart b/app/lib/apps/news/news_screen.dart index 4e3d9dad9..2b829f927 100644 --- a/app/lib/apps/news/news_screen.dart +++ b/app/lib/apps/news/news_screen.dart @@ -4,7 +4,7 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:http/http.dart' as http; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:threebotlogin/helpers/globals.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + import 'package:xml2json/xml2json.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:timeago/timeago.dart' as timeago; @@ -60,27 +60,24 @@ class _NewsScreenState extends State { @override Widget build(BuildContext context) { - return LayoutDrawer( - titleText: 'News', - content: RefreshIndicator( - onRefresh: () async => _pagingController.refresh(), - child: PagedListView>( - pagingController: _pagingController, - builderDelegate: PagedChildBuilderDelegate>( - itemBuilder: (context, entry, index) => - buildArticleCard(entry, context), - firstPageProgressIndicatorBuilder: (context) => Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CircularProgressIndicator(), - const SizedBox(height: 8), - Text('Loading Articles...', - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - color: Theme.of(context).colorScheme.onSurface, - fontWeight: FontWeight.bold)), - ], - ), + return RefreshIndicator( + onRefresh: () async => _pagingController.refresh(), + child: PagedListView>( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate>( + itemBuilder: (context, entry, index) => + buildArticleCard(entry, context), + firstPageProgressIndicatorBuilder: (context) => Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CircularProgressIndicator(), + const SizedBox(height: 8), + Text('Loading Articles...', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold)), + ], ), ), ), diff --git a/app/lib/apps/news/news_widget.dart b/app/lib/apps/news/news_widget.dart index 54a6b19cc..b235ee854 100644 --- a/app/lib/apps/news/news_widget.dart +++ b/app/lib/apps/news/news_widget.dart @@ -6,7 +6,7 @@ import 'package:threebotlogin/clipboard_hack/clipboard_hack.dart'; import 'package:threebotlogin/events/events.dart'; import 'package:threebotlogin/events/go_home_event.dart'; import 'package:threebotlogin/helpers/logger.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; +import 'package:threebotlogin/widgets/app_layout.dart'; import 'package:url_launcher/url_launcher.dart'; bool created = false; @@ -83,9 +83,9 @@ class _NewsState extends State with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); - return LayoutDrawer( - titleText: 'News', - content: Column( + return AppLayout( + title: 'News', + child: Column( children: [ Expanded( child: Container(child: iaWebView), diff --git a/app/lib/apps/wallet/wallet_widget.dart b/app/lib/apps/wallet/wallet_widget.dart index 12244a231..a51645dad 100644 --- a/app/lib/apps/wallet/wallet_widget.dart +++ b/app/lib/apps/wallet/wallet_widget.dart @@ -16,7 +16,6 @@ import 'package:threebotlogin/models/wallet_data.dart'; import 'package:threebotlogin/screens/scan_screen.dart'; import 'package:threebotlogin/services/crypto_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; bool created = false; @@ -184,15 +183,13 @@ class _WalletState extends State @override Widget build(BuildContext context) { super.build(context); - return LayoutDrawer( - titleText: 'Wallet', - content: Column( - children: [ - Expanded( - child: Container(child: iaWebView), - ), - ], - )); + return Column( + children: [ + Expanded( + child: Container(child: iaWebView), + ), + ], + ); } @override diff --git a/app/lib/constants/navigation_config.dart b/app/lib/constants/navigation_config.dart new file mode 100644 index 000000000..26ee38aca --- /dev/null +++ b/app/lib/constants/navigation_config.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; +import 'package:threebotlogin/helpers/globals.dart'; + +/// Data class to hold navigation entry information +class NavigationEntry { + final String name; + final String path; + final IconData icon; + final bool showInBottomNav; + final bool showInDrawer; + final int _cachedIndex; + + const NavigationEntry({ + required this.name, + required this.path, + required this.icon, + this.showInBottomNav = false, + this.showInDrawer = true, + required int cachedIndex, + }) : _cachedIndex = cachedIndex; + + int get index => _cachedIndex; +} + +class NavigationConfig { + static final Map _pathToEntry = {}; + static final Map _nameToEntry = {}; + static final Map _indexToEntry = {}; + static List? _bottomNavEntries; + static List? _drawerEntries; + static bool _isInitialized = false; + + // Navigation entries configuration + static const List _navigationEntries = [ + NavigationEntry( + name: 'Home', + path: '/home', + icon: Icons.home, + showInBottomNav: true, + showInDrawer: true, + cachedIndex: 0, + ), + NavigationEntry( + name: 'News', + path: '/news', + icon: Icons.article, + showInDrawer: true, + cachedIndex: 1, + ), + NavigationEntry( + name: 'Wallet', + path: '/wallet', + icon: Icons.account_balance_wallet, + showInBottomNav: true, + showInDrawer: true, + cachedIndex: 2, + ), + NavigationEntry( + name: 'Farming', + path: '/farming', + icon: Icons.storage, + showInBottomNav: true, + showInDrawer: true, + cachedIndex: 3, + ), + NavigationEntry( + name: 'Dao', + path: '/dao', + icon: Icons.how_to_vote_outlined, + showInDrawer: true, + cachedIndex: 4, + ), + NavigationEntry( + name: 'Identity', + path: '/identity', + icon: Icons.lock, + showInDrawer: true, + cachedIndex: 5, + ), + NavigationEntry( + name: 'Market', + path: '/market', + icon: Icons.show_chart_sharp, + showInDrawer: true, + cachedIndex: 6, + ), + NavigationEntry( + name: 'Settings', + path: '/settings', + icon: Icons.settings, + showInBottomNav: true, + showInDrawer: true, + cachedIndex: 7, + ), + NavigationEntry( + name: 'Council', + path: '/council', + icon: Icons.how_to_vote_outlined, + showInDrawer: true, + cachedIndex: 8, + ), + NavigationEntry( + name: 'Sign', + path: '/sign', + icon: Icons.draw_sharp, + showInDrawer: true, + cachedIndex: 9, + ), + NavigationEntry( + name: 'Notifications', + path: '/notifications', + icon: Icons.notifications, + showInDrawer: true, + cachedIndex: 10, + ), + ]; + + static void initialize() { + if (_isInitialized) return; + + // Build cached maps for O(1) lookups + for (final entry in _navigationEntries) { + _pathToEntry[entry.path] = entry; + _nameToEntry[entry.name] = entry; + _indexToEntry[entry.index] = entry; + } + + // Cache filtered lists + _bottomNavEntries = _navigationEntries.where((entry) => entry.showInBottomNav).toList(); + _drawerEntries = _navigationEntries.where((entry) => entry.showInDrawer).toList(); + + _isInitialized = true; + } + + static List get navigationEntries { + _ensureInitialized(); + return _navigationEntries; + } + + static NavigationEntry? getEntryByPath(String path) { + _ensureInitialized(); + return _pathToEntry[path]; + } + + static NavigationEntry? getEntryByName(String name) { + _ensureInitialized(); + return _nameToEntry[name]; + } + + static NavigationEntry? getEntryByIndex(int index) { + _ensureInitialized(); + return _indexToEntry[index]; + } + + static List getBottomNavEntries() { + _ensureInitialized(); + return _bottomNavEntries!; + } + + static List getDrawerEntries() { + _ensureInitialized(); + return _drawerEntries!; + } + + static int getBottomNavIndex(int tabIndex) { + _ensureInitialized(); + final bottomNavEntries = _bottomNavEntries!; + for (int i = 0; i < bottomNavEntries.length; i++) { + if (bottomNavEntries[i].index == tabIndex) { + return i; + } + } + return 0; // Default to home + } + + static int getTabIndexFromBottomNav(int bottomNavIndex) { + _ensureInitialized(); + final bottomNavEntries = _bottomNavEntries!; + if (bottomNavIndex >= 0 && bottomNavIndex < bottomNavEntries.length) { + return bottomNavEntries[bottomNavIndex].index; + } + return 0; // Default to home + } + + static void navigateToPath(String path) { + final entry = getEntryByPath(path); + if (entry != null) { + Globals().tabController.animateTo(entry.index); + } + } + + static void navigateToEntry(NavigationEntry entry) { + Globals().tabController.animateTo(entry.index); + } + + static void _ensureInitialized() { + if (!_isInitialized) { + initialize(); + } + } +} \ No newline at end of file diff --git a/app/lib/jrouter.dart b/app/lib/jrouter.dart index 36e5e2f08..97f952f51 100644 --- a/app/lib/jrouter.dart +++ b/app/lib/jrouter.dart @@ -4,9 +4,8 @@ import 'package:threebotlogin/apps/council/council.dart'; import 'package:threebotlogin/apps/dao/dao.dart'; import 'package:threebotlogin/apps/market/market.dart'; import 'package:threebotlogin/apps/wallet/wallet.dart'; -import 'package:threebotlogin/screens/identity_verification_screen.dart'; +import 'package:threebotlogin/screens/identity_screen.dart'; import 'package:threebotlogin/screens/preference_screen.dart'; -import 'package:threebotlogin/screens/registered_screen.dart'; import 'package:threebotlogin/apps/notifications/notifications.dart'; import 'apps/farmers/farmers.dart'; import 'apps/news/news.dart'; @@ -24,14 +23,6 @@ class JRouter { init() async { routes = [ - AppInfo( - route: Route( - path: '/', - name: 'Home', - icon: Icons.home, - view: RegisteredScreen(), - ), - app: null), AppInfo( route: Route( path: '/news', @@ -50,9 +41,9 @@ class JRouter { app: Wallet()), AppInfo( route: Route( - path: '/farmers', - name: 'Farmers', - icon: Icons.person_pin, + path: '/farming', + name: 'Farming', + icon: Icons.storage, view: await Farmers().widget(), ), app: Farmers()), @@ -66,10 +57,10 @@ class JRouter { app: Dao()), AppInfo( route: Route( - path: '/identityverification', - name: 'IdentityVerification', + path: '/identity', + name: 'Identity', icon: Icons.lock, - view: const IdentityVerificationScreen(), + view: const IdentityScreen(), ), app: null), AppInfo( diff --git a/app/lib/screens/council_screen.dart b/app/lib/screens/council_screen.dart index 9989511ae..b605c71e7 100644 --- a/app/lib/screens/council_screen.dart +++ b/app/lib/screens/council_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:threebotlogin/widgets/council/councils.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + import 'package:validators/validators.dart'; class CouncilScreen extends StatefulWidget { @@ -157,6 +157,6 @@ class _CouncilScreenState extends State { ], )); })); - return LayoutDrawer(titleText: 'Council', content: content); + return content; } } diff --git a/app/lib/screens/dao_screen.dart b/app/lib/screens/dao_screen.dart index 3b1d1cb37..b1d67bd81 100644 --- a/app/lib/screens/dao_screen.dart +++ b/app/lib/screens/dao_screen.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:tfchain_client/models/dao.dart'; import 'package:threebotlogin/helpers/logger.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + import 'package:threebotlogin/widgets/dao/proposals.dart'; import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; @@ -194,6 +194,6 @@ class _DaoPageState extends State with SingleTickerProviderStateMixin { ], ); } - return LayoutDrawer(titleText: 'Dao', content: content); + return content; } } diff --git a/app/lib/screens/farm_screen.dart b/app/lib/screens/farm_screen.dart index dd88a7fe7..eab79499c 100644 --- a/app/lib/screens/farm_screen.dart +++ b/app/lib/screens/farm_screen.dart @@ -14,7 +14,6 @@ import 'package:threebotlogin/services/gridproxy_service.dart'; import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/widgets/add_farm.dart'; import 'package:threebotlogin/widgets/farm_item.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; class FarmScreen extends ConsumerStatefulWidget { @@ -336,19 +335,7 @@ class _FarmScreenState extends ConsumerState ], ); } - return LayoutDrawer( - titleText: 'Farming', - content: mainWidget, - appBarActions: loading || failed - ? [] - : [ - IconButton( - onPressed: _openAddFarmOverlay, - icon: const Icon( - Icons.add, - )) - ], - ); + return mainWidget; } _openAddFarmOverlay() { diff --git a/app/lib/screens/home_screen.dart b/app/lib/screens/home_screen.dart index b44a63933..a1f153c50 100644 --- a/app/lib/screens/home_screen.dart +++ b/app/lib/screens/home_screen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:threebotlogin/events/email_event.dart'; import 'package:threebotlogin/events/go_news_event.dart'; import 'package:threebotlogin/events/go_reservations_event.dart'; @@ -16,6 +17,7 @@ import 'package:threebotlogin/events/go_wallet_event.dart'; import 'package:threebotlogin/events/new_login_event.dart'; import 'package:threebotlogin/events/uni_link_event.dart'; import 'package:threebotlogin/helpers/globals.dart'; +import 'package:threebotlogin/constants/navigation_config.dart'; import 'package:threebotlogin/main.dart'; import 'package:threebotlogin/providers/wallets_provider.dart'; import 'package:threebotlogin/screens/authentication_screen.dart'; @@ -23,6 +25,12 @@ import 'package:threebotlogin/services/socket_service.dart'; import 'package:threebotlogin/services/uni_link_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/widgets/email_verification_needed.dart'; +import 'package:threebotlogin/widgets/add_farm.dart'; +import 'package:threebotlogin/widgets/wallets/add_wallet.dart'; +import 'package:threebotlogin/widgets/chat_widget.dart'; +import 'package:threebotlogin/widgets/home_card.dart'; +import 'package:threebotlogin/widgets/home_logo.dart'; +import 'package:threebotlogin/widgets/app_layout.dart'; import 'package:uni_links/uni_links.dart'; /* Screen shows tab bar and all pages defined in router.dart */ @@ -42,6 +50,14 @@ class _HomeScreenState extends ConsumerState String? initialLink; bool timeoutExpiredInBackground = true; bool pinCheckOpen = false; + late final NavigationEntry walletEntry; + late final NavigationEntry farmingEntry; + late final NavigationEntry marketEntry; + late final NavigationEntry daoEntry; + late final NavigationEntry signEntry; + late final NavigationEntry newsEntry; + late final NavigationEntry identityEntry; + late final NavigationEntry settingsEntry; @override void dispose() { @@ -95,12 +111,14 @@ class _HomeScreenState extends ConsumerState await emailVerificationDialog(context); } - if (globals.tabController.index != 2 && Globals().paymentRequest != null) { + final currentEntry = NavigationConfig.getEntryByIndex(globals.tabController.index); + if (currentEntry?.path != '/wallet' && Globals().paymentRequest != null) { Globals().paymentRequest = null; Globals().paymentRequestIsUsed = false; } - if (globals.tabController.previousIndex == 2 && + final previousEntry = NavigationConfig.getEntryByIndex(globals.tabController.previousIndex); + if (previousEntry?.path == '/wallet' && Globals().paymentRequest != null && Globals().paymentRequestIsUsed == true) { Globals().paymentRequest = null; @@ -108,8 +126,168 @@ class _HomeScreenState extends ConsumerState } close(GoHomeEvent e) { - int homeTab = 0; - globals.tabController.animateTo(homeTab); + globals.tabController.animateTo(0); + } + + String _getCurrentPageTitle() { + if (globals.tabController.index == 0) { + return 'Home'; + } + int routerIndex = globals.tabController.index - 1; + if (routerIndex >= 0 && routerIndex < Globals().router.routes.length) { + return Globals().router.routes[routerIndex].route.name; + } + return 'Home'; + } + + void _openAddFarmOverlay() { + final wallets = ref.read(walletsNotifier); + + showModalBottomSheet( + isScrollControlled: true, + useSafeArea: true, + isDismissible: false, + constraints: const BoxConstraints(maxWidth: double.infinity), + context: context, + builder: (ctx) => NewFarm( + onAddFarm: (farm) { + Navigator.of(ctx).pop(); + }, + wallets: wallets, + isV4: false, + )); + } + + void _openAddWalletOverlay() { + final wallets = ref.read(walletsNotifier); + + showModalBottomSheet( + isScrollControlled: true, + useSafeArea: true, + isDismissible: false, + constraints: const BoxConstraints(maxWidth: double.infinity), + context: context, + builder: (ctx) => NewWallet( + wallets: wallets, + )); + } + + Widget _buildDashboardContent() { + return SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.3, + width: MediaQuery.of(context).size.width, + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset( + 'assets/map.png', + fit: BoxFit.cover, + ), + const Hero( + tag: 'logo', + child: HomeLogoWidget( + animate: false, + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.only(left: 10, right: 10, top: 50), + height: MediaQuery.of(context).size.height * 0.6, + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width / 1.2, + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + children: const [ + TextSpan( + text: + 'Your portal to ThreeFold: access your wallets, your digital identity, your farms, and ThreeFold updates with ease.'), + ]), + ), + ), + const Spacer(), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HomeCardWidget( + navigationEntry: walletEntry, + onTap: () => _navigateToEntry(walletEntry)), + HomeCardWidget( + navigationEntry: farmingEntry, + onTap: () => _navigateToEntry(farmingEntry)), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HomeCardWidget( + navigationEntry: marketEntry, + onTap: () => _navigateToEntry(marketEntry)), + HomeCardWidget( + navigationEntry: daoEntry, + onTap: () => _navigateToEntry(daoEntry)), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HomeCardWidget( + navigationEntry: signEntry, + onTap: () => _navigateToEntry(signEntry)), + HomeCardWidget( + navigationEntry: newsEntry, + onTap: () => _navigateToEntry(newsEntry)), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HomeCardWidget( + navigationEntry: identityEntry, + onTap: () => _navigateToEntry(identityEntry)), + HomeCardWidget( + navigationEntry: settingsEntry, + onTap: () => _navigateToEntry(settingsEntry)), + ], + ), + const SizedBox(height: 40), + const Row( + children: [Spacer(), CrispChatbot(), SizedBox(width: 20)], + ) + ], + ), + ) + ], + ), + ); + } + + void _navigateToScreen(String routePath) { + NavigationConfig.navigateToPath(routePath); + } + + void _navigateToEntry(NavigationEntry entry) { + NavigationConfig.navigateToEntry(entry); } @override @@ -117,44 +295,60 @@ class _HomeScreenState extends ConsumerState super.initState(); initUniLinks(); + // Initialize navigation entries + walletEntry = NavigationConfig.getEntryByPath('/wallet')!; + farmingEntry = NavigationConfig.getEntryByPath('/farming')!; + marketEntry = NavigationConfig.getEntryByPath('/market')!; + daoEntry = NavigationConfig.getEntryByPath('/dao')!; + signEntry = NavigationConfig.getEntryByPath('/sign')!; + newsEntry = NavigationConfig.getEntryByPath('/news')!; + identityEntry = NavigationConfig.getEntryByPath('/identity')!; + settingsEntry = NavigationConfig.getEntryByPath('/settings')!; globals.tabController = TabController( - initialIndex: 0, length: Globals().router.routes.length, vsync: this); + initialIndex: 0, length: Globals().router.routes.length + 1, vsync: this); globals.tabController.addListener(_handleTabSelection); + globals.tabController.addListener(() { + if (mounted) { + setState(() {}); + } + }); Events().onEvent(GoHomeEvent().runtimeType, close); Events().onEvent(GoHomeEvent().runtimeType, (GoHomeEvent event) { - globals.tabController.animateTo(0, duration: const Duration(seconds: 0)); + _navigateToScreen('/home'); }); Events().onEvent(GoNewsEvent().runtimeType, (GoNewsEvent event) { - globals.tabController.animateTo(1, duration: const Duration(seconds: 0)); + _navigateToScreen('/news'); }); - // Needed to hardcode this to prevent double tapping and gaining access without knowing the pincode with the current logic that was implemented. Events().onEvent(GoWalletEvent().runtimeType, (GoWalletEvent event) { - if (pinCheckOpen) { - return; - } - - int tabIndex = 2; - - if (Globals().router.pinRequired(tabIndex)) { - checkPinAndNavigateIfSuccess(tabIndex); + final navigationEntry = NavigationConfig.getEntryByPath('/wallet'); + if (navigationEntry != null) { + if (pinCheckOpen) { + return; + } + int tabIndex = navigationEntry.index; + if (Globals().router.pinRequired(tabIndex)) { + checkPinAndNavigateIfSuccess(tabIndex); + } else { + globals.tabController.animateTo(tabIndex); + } } }); Events().onEvent(GoSupportEvent().runtimeType, (GoSupportEvent event) { - globals.tabController.animateTo(3, duration: const Duration(seconds: 0)); + _navigateToScreen('/settings'); }); Events().onEvent(GoSettingsEvent().runtimeType, (GoSettingsEvent event) { - globals.tabController.animateTo(4, duration: const Duration(seconds: 0)); + _navigateToScreen('/settings'); }); Events().onEvent(GoReservationsEvent().runtimeType, (GoReservationsEvent event) { - globals.tabController.animateTo(5, duration: const Duration(seconds: 0)); + _navigateToScreen('/market'); }); Events().onEvent(NewLoginEvent().runtimeType, (NewLoginEvent event) { @@ -172,8 +366,7 @@ class _HomeScreenState extends ConsumerState Events().onEvent(IdentityCallbackEvent().runtimeType, (IdentityCallbackEvent event) async { Future(() { - globals.tabController - .animateTo(0, duration: const Duration(seconds: 0)); + _navigateToScreen('/identity'); showIdentityMessage(context, event.type!); }); }); @@ -203,25 +396,35 @@ class _HomeScreenState extends ConsumerState Widget build(BuildContext context) { ProviderScope.containerOf(context, listen: false) .read(walletsNotifier.notifier); - return Scaffold( - resizeToAvoidBottomInset: false, - appBar: PreferredSize( - preferredSize: const Size.fromHeight(0), - child: AppBar( - automaticallyImplyLeading: true, - ), - ), - body: DefaultTabController( - length: Globals().router.routes.length, - child: WillPopScope( - onWillPop: onWillPop, - child: Scaffold( - body: SafeArea( + + String currentTitle = _getCurrentPageTitle(); + + bool isHomePage = currentTitle == 'Home'; + + return AppLayout( + showAppBar: !isHomePage, + showDrawer: !isHomePage, + showBottomNav: !isHomePage, + onAddFarm: _openAddFarmOverlay, + onAddWallet: _openAddWalletOverlay, + child: PopScope( + canPop: false, + child: DefaultTabController( + length: Globals().router.routes.length + 1, + child: WillPopScope( + onWillPop: onWillPop, + child: Scaffold( + body: SafeArea( child: TabBarView( - controller: globals.tabController, - physics: const NeverScrollableScrollPhysics(), - children: Globals().router.getContent(), - )), + controller: globals.tabController, + physics: const NeverScrollableScrollPhysics(), + children: [ + _buildDashboardContent(), + ...Globals().router.getContent(), + ], + ), + ), + ), ), ), ), @@ -230,17 +433,13 @@ class _HomeScreenState extends ConsumerState Future onWillPop() { if (globals.tabController.index == 0) { - return Future(() => true); // if home screen exit + return Future(() => true); } - if (Globals().router.routes[globals.tabController.index].app == null) { - Events().emit(GoHomeEvent()); // if not an app, eg settings, go home + if (Globals().router.routes[globals.tabController.index - 1].app == null) { + Events().emit(GoHomeEvent()); + } else { + Globals().router.routes[globals.tabController.index - 1].app!.back(); } - Globals() - .router - .routes[globals.tabController.index] - .app! - .back(); // if app ask app to handle back event - return Future(() => false); } -} +} \ No newline at end of file diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_screen.dart similarity index 97% rename from app/lib/screens/identity_verification_screen.dart rename to app/lib/screens/identity_screen.dart index c676c02b6..4b751b35f 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_screen.dart @@ -14,21 +14,20 @@ import 'package:threebotlogin/services/pkid_service.dart'; import 'package:threebotlogin/services/tools_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; import 'package:threebotlogin/widgets/phone_widget.dart'; -class IdentityVerificationScreen extends StatefulWidget { - const IdentityVerificationScreen({super.key}); +class IdentityScreen extends StatefulWidget { + const IdentityScreen({super.key}); @override - State createState() => - IdentityVerificationScreenState(); + State createState() => + IdentityScreenState(); } -class IdentityVerificationScreenState - extends State { - static final GlobalKey globalKey = - GlobalKey(); +class IdentityScreenState + extends State { + static final GlobalKey globalKey = + GlobalKey(); final emailController = TextEditingController(); final changeEmailController = TextEditingController(); @@ -726,9 +725,6 @@ class IdentityVerificationScreenState ); } - return LayoutDrawer( - titleText: 'Identity', - content: content, - ); + return content; } } diff --git a/app/lib/screens/main_screen.dart b/app/lib/screens/main_screen.dart index 66d2d263d..4bba08aaa 100644 --- a/app/lib/screens/main_screen.dart +++ b/app/lib/screens/main_screen.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:threebotlogin/app_config.dart'; +import 'package:threebotlogin/constants/navigation_config.dart'; import 'package:threebotlogin/events/events.dart'; import 'package:threebotlogin/helpers/environment.dart'; import 'package:threebotlogin/helpers/flags.dart'; @@ -169,7 +170,7 @@ class _AppState extends State { } await Globals().router.init(); - + NavigationConfig.initialize(); _backendConnection = BackendConnection((await getDoubleName())!); _backendConnection.init(); diff --git a/app/lib/screens/market/market_screen.dart b/app/lib/screens/market/market_screen.dart index ebc74c20e..06c5faed9 100644 --- a/app/lib/screens/market/market_screen.dart +++ b/app/lib/screens/market/market_screen.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + import 'package:threebotlogin/screens/market/order_book.dart'; import 'package:threebotlogin/screens/market/overview.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -28,41 +28,38 @@ class _MarketPageState extends ConsumerState @override Widget build(BuildContext context) { - return LayoutDrawer( - titleText: 'Market', - content: DefaultTabController( - length: 2, - child: Column(children: [ - PreferredSize( - preferredSize: const Size.fromHeight(10.0), - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: TabBar( - controller: _tabController, - labelColor: Theme.of(context).colorScheme.primary, - indicatorColor: Theme.of(context).colorScheme.primary, - unselectedLabelColor: Theme.of(context).colorScheme.onSurface, - dividerColor: Theme.of(context).scaffoldBackgroundColor, - labelStyle: Theme.of(context).textTheme.titleMedium, - unselectedLabelStyle: Theme.of(context).textTheme.titleMedium, - tabs: const [ - Tab(text: 'Overview'), - Tab(text: 'OrderBook'), - ], - ), - ), - ), - Expanded( - child: TabBarView( + return DefaultTabController( + length: 2, + child: Column(children: [ + PreferredSize( + preferredSize: const Size.fromHeight(10.0), + child: Container( + color: Theme.of(context).scaffoldBackgroundColor, + child: TabBar( controller: _tabController, - children: const [ - OverviewWidget(), - OrderbookWidget(), + labelColor: Theme.of(context).colorScheme.primary, + indicatorColor: Theme.of(context).colorScheme.primary, + unselectedLabelColor: Theme.of(context).colorScheme.onSurface, + dividerColor: Theme.of(context).scaffoldBackgroundColor, + labelStyle: Theme.of(context).textTheme.titleMedium, + unselectedLabelStyle: Theme.of(context).textTheme.titleMedium, + tabs: const [ + Tab(text: 'Overview'), + Tab(text: 'OrderBook'), ], ), - ) - ]), - ), + ), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: const [ + OverviewWidget(), + OrderbookWidget(), + ], + ), + ) + ]), ); } } diff --git a/app/lib/screens/notifications_screen.dart b/app/lib/screens/notifications_screen.dart index 5bef1331b..b6a4f8c98 100644 --- a/app/lib/screens/notifications_screen.dart +++ b/app/lib/screens/notifications_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:threebotlogin/apps/notifications/notifications_user_data.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; class NotificationsScreen extends StatefulWidget { const NotificationsScreen({super.key}); @@ -29,10 +28,7 @@ class _NotificationsScreenState extends State { @override Widget build(BuildContext context) { - return LayoutDrawer( - titleText: 'Notifications', - content: _buildNotificationSettings(), - ); + return _buildNotificationSettings(); } Widget _buildNotificationSettings() { diff --git a/app/lib/screens/preference_screen.dart b/app/lib/screens/preference_screen.dart index d61a7038b..e6668ca60 100644 --- a/app/lib/screens/preference_screen.dart +++ b/app/lib/screens/preference_screen.dart @@ -24,7 +24,7 @@ import 'package:threebotlogin/services/pkid_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + import 'package:threebotlogin/providers/theme_provider.dart'; import 'package:threebotlogin/widgets/wallets/warning_dialog.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -94,9 +94,7 @@ class _PreferenceScreenState extends ConsumerState { } else { isDarkMode = themeMode == ThemeMode.dark; } - return LayoutDrawer( - titleText: 'Settings', - content: ListView( + return ListView( children: [ const ListTile( title: Text('Global settings'), @@ -259,7 +257,6 @@ class _PreferenceScreenState extends ConsumerState { ], ), ], - ), ); } diff --git a/app/lib/screens/registered_screen.dart b/app/lib/screens/registered_screen.dart deleted file mode 100644 index c443412f4..000000000 --- a/app/lib/screens/registered_screen.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:threebotlogin/widgets/chat_widget.dart'; -import 'package:threebotlogin/widgets/home_card.dart'; -import 'package:threebotlogin/widgets/home_logo.dart'; - -class RegisteredScreen extends StatefulWidget { - static final RegisteredScreen _singleton = RegisteredScreen._internal(); - - factory RegisteredScreen() { - return _singleton; - } - - RegisteredScreen._internal() { - //init - } - - @override - State createState() => _RegisteredScreenState(); -} - -class _RegisteredScreenState extends State - with WidgetsBindingObserver { - // We will treat this error as a singleton - - bool showSettings = false; - bool showPreference = false; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.3, - width: MediaQuery.of(context).size.width, - child: Stack( - alignment: Alignment.center, - children: [ - Image.asset( - 'assets/map.png', - fit: BoxFit.cover, - ), - const Hero( - tag: 'logo', - child: HomeLogoWidget( - animate: false, - ), - ), - ], - ), - ), - Container( - padding: const EdgeInsets.only(left: 10, right: 10, top: 50), - height: MediaQuery.of(context).size.height * 0.6, - width: MediaQuery.of(context).size.width, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - width: MediaQuery.of(context).size.width / 1.2, - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - color: Theme.of(context).colorScheme.onSurface, - ), - children: const [ - TextSpan( - text: - 'Your portal to ThreeFold: access your wallets, your digital identity, your farms, and ThreeFold updates with ease.'), - ]), - ), - ), - const Spacer(), - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - HomeCardWidget( - name: 'Wallet', - icon: Icons.account_balance_wallet, - pageNumber: 2), - HomeCardWidget( - name: 'Farming', icon: Icons.storage, pageNumber: 3), - ], - ), - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - HomeCardWidget( - name: 'Market', - icon: Icons.show_chart_sharp, - pageNumber: 6), - HomeCardWidget( - name: 'Dao', - icon: Icons.how_to_vote_outlined, - pageNumber: 4), - ], - ), - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - HomeCardWidget( - name: 'Sign', icon: Icons.draw_sharp, pageNumber: 9), - HomeCardWidget( - name: 'News', icon: Icons.article, pageNumber: 1), - ], - ), - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - HomeCardWidget( - name: 'Identity', icon: Icons.person, pageNumber: 5), - HomeCardWidget( - name: 'Settings', icon: Icons.settings, pageNumber: 7), - // HomeCardWidget( - // name: 'Notifications', - // icon: Icons.notifications, - // pageNumber: 9), - ], - ), - const SizedBox(height: 40), - const Row( - children: [Spacer(), CrispChatbot(), SizedBox(width: 20)], - ) - ], - ), - ) - ], - ), - )); - } - - void updatePreference(bool preference) { - setState(() { - showPreference = preference; - }); - } -} diff --git a/app/lib/screens/signing/signing.dart b/app/lib/screens/signing/signing.dart index 02e6e7656..3eddf4582 100644 --- a/app/lib/screens/signing/signing.dart +++ b/app/lib/screens/signing/signing.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:threebotlogin/screens/signing/sign_with_link.dart'; import 'package:threebotlogin/screens/signing/sign_with_qrcode.dart'; import 'package:threebotlogin/screens/signing/sign_with_text.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + class Signing extends StatefulWidget { const Signing({super.key}); @@ -51,66 +51,63 @@ class _SigningState extends State { @override Widget build(BuildContext context) { - return LayoutDrawer( - titleText: 'Sign', - content: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 32), - Text( - 'Choose Signing Method', - style: Theme.of(context).textTheme.titleLarge!.copyWith( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 32), + Text( + 'Choose Signing Method', + style: Theme.of(context).textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + const SizedBox(height: 24), + _buildSignButton( + icon: Icons.text_fields, + label: 'Sign Text', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignWithTextScreen()), + ); + }, + ), + _buildSignButton( + icon: Icons.qr_code_scanner, + label: 'Sign QR Code Content', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignWithQRCodeScreen()), + ); + }, + ), + _buildSignButton( + icon: Icons.link, + label: 'Sign URL Content', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignWithLinkScreen()), + ); + }, + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(16), + child: Text( + 'Choose a method to sign your data securely', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: 24), - _buildSignButton( - icon: Icons.text_fields, - label: 'Sign Text', - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SignWithTextScreen()), - ); - }, - ), - _buildSignButton( - icon: Icons.qr_code_scanner, - label: 'Sign QR Code Content', - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SignWithQRCodeScreen()), - ); - }, - ), - _buildSignButton( - icon: Icons.link, - label: 'Sign URL Content', - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SignWithLinkScreen()), - ); - }, - ), - const Spacer(), - Padding( - padding: const EdgeInsets.all(16), - child: Text( - 'Choose a method to sign your data securely', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - ), - ], - ), + ), + ], ); } } diff --git a/app/lib/screens/wallets/wallet_screen.dart b/app/lib/screens/wallets/wallet_screen.dart index 8ed72e801..f357f9d59 100644 --- a/app/lib/screens/wallets/wallet_screen.dart +++ b/app/lib/screens/wallets/wallet_screen.dart @@ -6,7 +6,7 @@ import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/providers/wallets_provider.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/services/wallet_service.dart'; -import 'package:threebotlogin/widgets/layout_drawer.dart'; + import 'package:threebotlogin/widgets/wallets/add_wallet.dart'; import 'package:threebotlogin/widgets/wallets/wallet_card.dart'; import 'package:hashlib/hashlib.dart'; @@ -93,19 +93,7 @@ class _WalletScreenState extends ConsumerState { })); } - return LayoutDrawer( - titleText: 'Wallet', - content: mainWidget, - appBarActions: loading && !failed - ? [] - : [ - IconButton( - onPressed: _openAddWalletOverlay, - icon: const Icon( - Icons.add, - )) - ], - ); + return mainWidget; } listMyWallets() async { @@ -145,17 +133,6 @@ class _WalletScreenState extends ConsumerState { } } - _openAddWalletOverlay() { - showModalBottomSheet( - isScrollControlled: true, - useSafeArea: true, - isDismissible: false, - constraints: const BoxConstraints(maxWidth: double.infinity), - context: context, - builder: (ctx) => NewWallet( - wallets: wallets, - )); - } Future _addInitialWallet() async { const walletName = 'Daily'; diff --git a/app/lib/widgets/app_layout.dart b/app/lib/widgets/app_layout.dart new file mode 100644 index 000000000..65b16748d --- /dev/null +++ b/app/lib/widgets/app_layout.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:threebotlogin/helpers/globals.dart'; +import 'package:threebotlogin/constants/navigation_config.dart'; + +/// Main app layout wrapper that handles navigation, drawer, app bar, and bottom navigation +/// This replaces the layout logic that was previously in HomeScreen +class AppLayout extends StatefulWidget { + const AppLayout({ + super.key, + required this.child, + this.showAppBar = true, + this.showDrawer = true, + this.showBottomNav = true, + this.appBarActions, + this.title, + this.onAddFarm, + this.onAddWallet, + }); + + final Widget child; + final bool showAppBar; + final bool showDrawer; + final bool showBottomNav; + final List? appBarActions; + final String? title; + final VoidCallback? onAddFarm; + final VoidCallback? onAddWallet; + + @override + State createState() => _AppLayoutState(); +} + +class _AppLayoutState extends State { + Globals globals = Globals(); + + /// Get current page title based on tab index + String _getCurrentPageTitle() { + if (widget.title != null) return widget.title!; + + // Index 0 is now the home page (handled directly in HomeScreen) + if (globals.tabController.index == 0) { + return 'Home'; + } + // Other indices are offset by 1 since we removed home from router + int routerIndex = globals.tabController.index - 1; + if (routerIndex >= 0 && routerIndex < Globals().router.routes.length) { + return Globals().router.routes[routerIndex].route.name; + } + return 'Home'; + } + + /// Get current app bar actions based on tab index + List? _getCurrentAppBarActions() { + if (widget.appBarActions != null) return widget.appBarActions; + + int currentTabIndex = globals.tabController.index; + + // Return app bar actions based on current tab + final currentEntry = NavigationConfig.getEntryByIndex(currentTabIndex); + if (currentEntry != null) { + switch (currentEntry.path) { + case '/farming': // Farming screen + return widget.onAddFarm != null + ? [ + IconButton( + onPressed: widget.onAddFarm, + icon: const Icon(Icons.add), + tooltip: 'Add Farm', + ) + ] + : null; + case '/wallet': // Wallet screen + return widget.onAddWallet != null + ? [ + IconButton( + onPressed: widget.onAddWallet, + icon: const Icon(Icons.add), + tooltip: 'Add Wallet', + ) + ] + : null; + default: + return null; + } + } + return null; + } + + /// Get current bottom navigation index + int _getCurrentBottomNavIndex() { + return NavigationConfig.getBottomNavIndex(globals.tabController.index); + } + + /// Handle bottom navigation tap + void _selectScreen(index) { + int tabIndex = NavigationConfig.getTabIndexFromBottomNav(index); + globals.tabController.animateTo(tabIndex); + } + + /// Build the navigation drawer + Widget _buildDrawer() { + return Drawer( + elevation: 5, + width: MediaQuery.of(context).size.width * 2 / 3, + child: Column( + children: [ + SizedBox( + height: 120, + child: DrawerHeader( + decoration: BoxDecoration( + border: Border( + bottom: + BorderSide(color: Theme.of(context).colorScheme.primary), + ), + ), + child: SvgPicture.asset( + 'assets/TF_log_horizontal.svg', + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ), + ), + ), + _buildDrawerItem(Icons.home, 'Home', 0), + _buildDrawerItem(Icons.account_balance_wallet, 'Wallet', NavigationConfig.getEntryByPath('/wallet')!.index), + if (Globals().canSeeFarmers) + _buildDrawerItem(Icons.storage, 'Farming', NavigationConfig.getEntryByPath('/farming')!.index) + else + Container(), + _buildDrawerItem(Icons.show_chart_sharp, 'Market', NavigationConfig.getEntryByPath('/market')!.index), + _buildDrawerItem(Icons.how_to_vote_outlined, 'Dao', NavigationConfig.getEntryByPath('/dao')!.index), + _buildDrawerItem(Icons.draw_sharp, 'Sign', NavigationConfig.getEntryByPath('/sign')!.index), + _buildDrawerItem(Icons.article, 'News', NavigationConfig.getEntryByPath('/news')!.index), + _buildDrawerItem(Icons.settings, 'Settings', NavigationConfig.getEntryByPath('/settings')!.index), + if (Globals().council) + _buildDrawerItem(Icons.how_to_vote_outlined, 'Council', NavigationConfig.getEntryByPath('/council')!.index), + ], + ), + ); + } + + /// Build individual drawer item + Widget _buildDrawerItem(IconData icon, String title, int tabIndex) { + return ListTile( + minLeadingWidth: 10, + leading: Padding( + padding: const EdgeInsets.only(left: 10), + child: Icon(icon, size: 18)), + title: Text(title), + onTap: () { + Navigator.pop(context); + globals.tabController.animateTo(tabIndex); + }, + ); + } + + @override + Widget build(BuildContext context) { + String currentTitle = _getCurrentPageTitle(); + int currentBottomNavIndex = _getCurrentBottomNavIndex(); + + IconThemeData? selectedIconTheme = + BottomNavigationBarTheme.of(context).selectedIconTheme; + Color? selectedItemColor = + BottomNavigationBarTheme.of(context).selectedItemColor; + double selectedFontSize = 14; + + if (currentTitle != 'Home' && + currentTitle != 'Wallet' && + currentTitle != 'Farming' && + currentTitle != 'Settings') { + selectedIconTheme = + BottomNavigationBarTheme.of(context).unselectedIconTheme; + selectedItemColor = + BottomNavigationBarTheme.of(context).unselectedItemColor; + selectedFontSize = 12; + } + + return PopScope( + canPop: false, + child: Scaffold( + resizeToAvoidBottomInset: false, + appBar: widget.showAppBar + ? AppBar( + actions: _getCurrentAppBarActions() ?? [], + title: Text(currentTitle), + toolbarHeight: 60, + ) + : null, + drawer: widget.showDrawer ? _buildDrawer() : null, + body: widget.child, + bottomNavigationBar: widget.showBottomNav + ? BottomNavigationBar( + onTap: _selectScreen, + selectedIconTheme: selectedIconTheme, + selectedItemColor: selectedItemColor, + showUnselectedLabels: true, + selectedFontSize: selectedFontSize, + unselectedFontSize: 12, + currentIndex: currentBottomNavIndex, + type: BottomNavigationBarType.fixed, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), label: 'Home'), + BottomNavigationBarItem( + icon: Icon(Icons.account_balance_wallet), + label: 'Wallet'), + BottomNavigationBarItem( + icon: Icon(Icons.storage), label: 'Farming'), + BottomNavigationBarItem( + icon: Icon(Icons.settings), label: 'Settings'), + ], + ) + : null, + ), + ); + } +} diff --git a/app/lib/widgets/home_card.dart b/app/lib/widgets/home_card.dart index a5fd2e711..372206056 100644 --- a/app/lib/widgets/home_card.dart +++ b/app/lib/widgets/home_card.dart @@ -1,23 +1,20 @@ import 'package:flutter/material.dart'; -import 'package:threebotlogin/helpers/globals.dart'; +import 'package:threebotlogin/constants/navigation_config.dart'; class HomeCardWidget extends StatelessWidget { const HomeCardWidget({ super.key, - required this.name, - required this.icon, - required this.pageNumber, + required this.navigationEntry, + required this.onTap, this.fullWidth = false, }); - final String name; - final IconData icon; - final int pageNumber; + final NavigationEntry navigationEntry; + final VoidCallback onTap; final bool fullWidth; @override Widget build(BuildContext context) { - Globals globals = Globals(); final size = MediaQuery.of(context).size.width; const double margin = 3; return Card( @@ -27,9 +24,7 @@ class HomeCardWidget extends StatelessWidget { clipBehavior: Clip.hardEdge, elevation: 2, child: InkWell( - onTap: () { - globals.tabController.animateTo(pageNumber); - }, + onTap: onTap, child: Column( children: [ Container( @@ -41,12 +36,12 @@ class HomeCardWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( - icon, + navigationEntry.icon, color: Theme.of(context).colorScheme.onPrimaryContainer, ), const SizedBox(width: 7), Text( - name, + navigationEntry.name, style: Theme.of(context).textTheme.titleMedium!.copyWith( color: Theme.of(context).colorScheme.onPrimaryContainer, fontWeight: FontWeight.bold), @@ -59,4 +54,4 @@ class HomeCardWidget extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/app/lib/widgets/layout_drawer.dart b/app/lib/widgets/layout_drawer.dart deleted file mode 100644 index 457467766..000000000 --- a/app/lib/widgets/layout_drawer.dart +++ /dev/null @@ -1,248 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:threebotlogin/helpers/globals.dart'; - -class LayoutDrawer extends StatefulWidget { - const LayoutDrawer( - {super.key, - required this.titleText, - required this.content, - this.appBarActions}); - - final String titleText; - final Widget content; - final List? appBarActions; - - @override - State createState() => _LayoutDrawerState(); -} - -class _LayoutDrawerState extends State { - Globals globals = Globals(); - int currentScreenIndex = 0; - - void _selectScreen(index) { - setState(() { - currentScreenIndex = index; - if (index == 0) { - globals.tabController.animateTo(0); - } else if (index == 1) { - globals.tabController.animateTo(2); - } else if (index == 2) { - globals.tabController.animateTo(3); - } else if (index == 3) { - globals.tabController.animateTo(7); - } else { - return; - } - }); - } - - @override - Widget build(BuildContext context) { - IconThemeData? selectedIconTheme = - BottomNavigationBarTheme.of(context).selectedIconTheme; - Color? selectedItemColor = - BottomNavigationBarTheme.of(context).selectedItemColor; - double selectedFontSize = 14; - if (widget.titleText == 'Home') { - currentScreenIndex = 0; - } else if (widget.titleText == 'Wallet') { - currentScreenIndex = 1; - } else if (widget.titleText == 'Farming') { - currentScreenIndex = 2; - } else if (widget.titleText == 'Settings') { - currentScreenIndex = 3; - } else { - selectedIconTheme = - BottomNavigationBarTheme.of(context).unselectedIconTheme; - selectedItemColor = - BottomNavigationBarTheme.of(context).unselectedItemColor; - selectedFontSize = 12; - } - - return PopScope( - canPop: false, - child: Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - actions: widget.appBarActions ?? [], - title: Text(widget.titleText), - toolbarHeight: 60, - ), - body: widget.content, - drawer: Drawer( - elevation: 5, - width: MediaQuery.of(context).size.width * 2 / 3, - // space to fit everything. - child: Column( - children: [ - SizedBox( - height: 70, - child: DrawerHeader( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Theme.of(context).colorScheme.primary), - ), - ), - child: SvgPicture.asset( - 'assets/TF_log_horizontal.svg', - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.onSurface, - BlendMode.srcIn), - ), - ), - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.home, size: 18)), - title: const Text('Home'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(0); - }, - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.account_balance_wallet, size: 18)), - title: const Text('Wallet'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(2); - }, - ), - if (Globals().canSeeFarmers) - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.storage, size: 18)), - title: const Text('Farming'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(3); - }, - ) - else - Container(), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.show_chart_sharp, size: 18)), - title: const Text('Market'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(6); - }, - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.how_to_vote_outlined, size: 18)), - title: const Text('Dao'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(4); - }, - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.draw_sharp, size: 18)), - title: const Text('Sign'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(9); - }, - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.article, size: 18)), - title: const Text('News'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(1); - }, - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.person, size: 18)), - title: const Text('Identity'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(5); - }, - ), - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.settings, size: 18)), - title: const Text('Settings'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(7); - }, - ), - if (Globals().council) - ListTile( - minLeadingWidth: 10, - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.how_to_vote_outlined, size: 18)), - title: const Text('Council'), - onTap: () { - Navigator.pop(context); - globals.tabController.animateTo(8); - }, - ), - // ListTile( - // minLeadingWidth: 10, - // leading: const Padding( - // padding: EdgeInsets.only(left: 10), - // child: Icon(Icons.notifications, size: 18)), - // title: const Text('Notifications'), - // onTap: () { - // Navigator.pop(context); - // globals.tabController.animateTo(9); - // }, - // ), - ], - ), - ), - bottomNavigationBar: BottomNavigationBar( - onTap: _selectScreen, - selectedIconTheme: selectedIconTheme, - selectedItemColor: selectedItemColor, - showUnselectedLabels: true, - selectedFontSize: selectedFontSize, - unselectedFontSize: 12, - currentIndex: currentScreenIndex, - type: BottomNavigationBarType.fixed, - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), - BottomNavigationBarItem( - icon: Icon(Icons.account_balance_wallet), label: 'Wallet'), - BottomNavigationBarItem( - icon: Icon(Icons.storage), label: 'Farming'), - BottomNavigationBarItem( - icon: Icon(Icons.settings), label: 'Settings'), - ], - ), - ), - ); - } -}