diff --git a/lib/services/remote_sync_service.dart b/lib/services/remote_sync_service.dart index 17cff0d13..5b1f3195c 100644 --- a/lib/services/remote_sync_service.dart +++ b/lib/services/remote_sync_service.dart @@ -129,6 +129,7 @@ class RemoteSyncService { // session are not processed now sync(); } else { + debugPrint("Fire backup completed event"); Bus.instance.fire(SyncStatusUpdate(SyncStatus.completedBackup)); } } else { diff --git a/lib/ui/grant_permissions_widget.dart b/lib/ui/home/grant_permissions_widget.dart similarity index 100% rename from lib/ui/grant_permissions_widget.dart rename to lib/ui/home/grant_permissions_widget.dart diff --git a/lib/ui/header_error_widget.dart b/lib/ui/home/header_error_widget.dart similarity index 100% rename from lib/ui/header_error_widget.dart rename to lib/ui/home/header_error_widget.dart diff --git a/lib/ui/home/header_widget.dart b/lib/ui/home/header_widget.dart new file mode 100644 index 000000000..0d9ccf2ee --- /dev/null +++ b/lib/ui/home/header_widget.dart @@ -0,0 +1,26 @@ +import 'package:flutter/widgets.dart'; +import 'package:logging/logging.dart'; +import 'package:photos/ui/home/memories_widget.dart'; +import 'package:photos/ui/home/status_bar_widget.dart'; + +class HeaderWidget extends StatelessWidget { + static const _memoriesWidget = MemoriesWidget(); + static const _statusBarWidget = StatusBarWidget(); + + const HeaderWidget({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Logger("Header").info("Building header widget"); + const list = [ + _statusBarWidget, + _memoriesWidget, + ]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: list, + ); + } +} diff --git a/lib/ui/home/home_bottom_nav_bar.dart b/lib/ui/home/home_bottom_nav_bar.dart new file mode 100644 index 000000000..5ac274a7c --- /dev/null +++ b/lib/ui/home/home_bottom_nav_bar.dart @@ -0,0 +1,176 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:photos/core/event_bus.dart'; +import 'package:photos/ente_theme_data.dart'; +import 'package:photos/events/tab_changed_event.dart'; +import 'package:photos/models/selected_files.dart'; +import 'package:photos/theme/colors.dart'; +import 'package:photos/theme/effects.dart'; +import 'package:photos/theme/ente_theme.dart'; +import 'package:photos/ui/nav_bar.dart'; + +class HomeBottomNavigationBar extends StatefulWidget { + const HomeBottomNavigationBar( + this.selectedFiles, { + required this.selectedTabIndex, + Key? key, + }) : super(key: key); + + final SelectedFiles selectedFiles; + final int selectedTabIndex; + + @override + State createState() => + _HomeBottomNavigationBarState(); +} + +class _HomeBottomNavigationBarState extends State { + late StreamSubscription _tabChangedEventSubscription; + int currentTabIndex = 0; + + @override + void initState() { + super.initState(); + currentTabIndex = widget.selectedTabIndex; + widget.selectedFiles.addListener(() { + setState(() {}); + }); + _tabChangedEventSubscription = + Bus.instance.on().listen((event) { + if (event.source != TabChangedEventSource.tabBar) { + debugPrint('index changed to ${event.selectedIndex}'); + if (mounted) { + setState(() { + currentTabIndex = event.selectedIndex; + }); + } + } + }); + } + + @override + void dispose() { + _tabChangedEventSubscription.cancel(); + super.dispose(); + } + + void _onTabChange(int index) { + Bus.instance.fire( + TabChangedEvent( + index, + TabChangedEventSource.tabBar, + ), + ); + } + + @override + Widget build(BuildContext context) { + final bool filesAreSelected = widget.selectedFiles.files.isNotEmpty; + final enteColorScheme = getEnteColorScheme(context); + final navBarBlur = + MediaQuery.of(context).platformBrightness == Brightness.light + ? blurBase + : blurMuted; + + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + height: filesAreSelected ? 0 : 56, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 100), + opacity: filesAreSelected ? 0.0 : 1.0, + curve: Curves.easeIn, + child: IgnorePointer( + ignoring: filesAreSelected, + child: ListView( + physics: const NeverScrollableScrollPhysics(), + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(32), + child: Container( + alignment: Alignment.bottomCenter, + height: 48, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: navBarBlur, + sigmaY: navBarBlur, + ), + child: GNav( + curve: Curves.easeOutExpo, + backgroundColor: + getEnteColorScheme(context).fillMuted, + mainAxisAlignment: MainAxisAlignment.center, + rippleColor: Colors.white.withOpacity(0.1), + activeColor: Theme.of(context) + .colorScheme + .gNavBarActiveColor, + iconSize: 24, + padding: const EdgeInsets.fromLTRB(16, 6, 16, 6), + duration: const Duration(milliseconds: 200), + gap: 0, + tabBorderRadius: 32, + tabBackgroundColor: Theme.of(context) + .colorScheme + .gNavBarActiveColor, + haptic: false, + tabs: [ + GButton( + margin: const EdgeInsets.fromLTRB(8, 6, 10, 6), + icon: Icons.home_rounded, + iconColor: enteColorScheme.tabIcon, + iconActiveColor: strokeBaseLight, + text: '', + onPressed: () { + _onTabChange( + 0, + ); // To take care of occasional missing events + }, + ), + GButton( + margin: const EdgeInsets.fromLTRB(10, 6, 10, 6), + icon: Icons.collections_rounded, + iconColor: enteColorScheme.tabIcon, + iconActiveColor: strokeBaseLight, + text: '', + onPressed: () { + _onTabChange( + 1, + ); // To take care of occasional missing events + }, + ), + GButton( + margin: const EdgeInsets.fromLTRB(10, 6, 8, 6), + icon: Icons.people_outlined, + iconColor: enteColorScheme.tabIcon, + iconActiveColor: strokeBaseLight, + text: '', + onPressed: () { + _onTabChange( + 2, + ); // To take care of occasional missing events + }, + ), + ], + selectedIndex: currentTabIndex, + onTabChange: _onTabChange, + ), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/home/home_gallery_widget.dart b/lib/ui/home/home_gallery_widget.dart new file mode 100644 index 000000000..607933102 --- /dev/null +++ b/lib/ui/home/home_gallery_widget.dart @@ -0,0 +1,84 @@ +// @dart=2.9 +import 'package:flutter/material.dart'; +import 'package:photos/core/configuration.dart'; +import 'package:photos/core/event_bus.dart'; +import 'package:photos/db/files_db.dart'; +import 'package:photos/events/backup_folders_updated_event.dart'; +import 'package:photos/events/files_updated_event.dart'; +import 'package:photos/events/force_reload_home_gallery_event.dart'; +import 'package:photos/events/local_photos_updated_event.dart'; +import 'package:photos/models/file_load_result.dart'; +import 'package:photos/models/selected_files.dart'; +import 'package:photos/services/collections_service.dart'; +import 'package:photos/services/ignored_files_service.dart'; +import 'package:photos/ui/viewer/gallery/gallery.dart'; + +class HomeGalleryWidget extends StatelessWidget { + final Widget header; + final Widget footer; + final SelectedFiles selectedFiles; + + const HomeGalleryWidget({ + Key key, + this.header, + this.footer, + this.selectedFiles, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final gallery = Gallery( + asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async { + final ownerID = Configuration.instance.getUserID(); + final hasSelectedAllForBackup = + Configuration.instance.hasSelectedAllFoldersForBackup(); + final archivedCollectionIds = + CollectionsService.instance.getArchivedCollections(); + FileLoadResult result; + if (hasSelectedAllForBackup) { + result = await FilesDB.instance.getAllLocalAndUploadedFiles( + creationStartTime, + creationEndTime, + ownerID, + limit: limit, + asc: asc, + ignoredCollectionIDs: archivedCollectionIds, + ); + } else { + result = await FilesDB.instance.getAllPendingOrUploadedFiles( + creationStartTime, + creationEndTime, + ownerID, + limit: limit, + asc: asc, + ignoredCollectionIDs: archivedCollectionIds, + ); + } + + // hide ignored files from home page UI + final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs; + result.files.removeWhere( + (f) => + f.uploadedFileID == null && + IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, f), + ); + return result; + }, + reloadEvent: Bus.instance.on(), + removalEventTypes: const { + EventType.deletedFromRemote, + EventType.deletedFromEverywhere, + EventType.archived, + }, + forceReloadEvents: [ + Bus.instance.on(), + Bus.instance.on(), + ], + tagPrefix: "home_gallery", + selectedFiles: selectedFiles, + header: header, + footer: footer, + ); + return gallery; + } +} diff --git a/lib/ui/landing_page_widget.dart b/lib/ui/home/landing_page_widget.dart similarity index 100% rename from lib/ui/landing_page_widget.dart rename to lib/ui/home/landing_page_widget.dart diff --git a/lib/ui/memories_widget.dart b/lib/ui/home/memories_widget.dart similarity index 100% rename from lib/ui/memories_widget.dart rename to lib/ui/home/memories_widget.dart diff --git a/lib/ui/viewer/gallery/gallery_footer_widget.dart b/lib/ui/home/preserve_footer_widget.dart similarity index 88% rename from lib/ui/viewer/gallery/gallery_footer_widget.dart rename to lib/ui/home/preserve_footer_widget.dart index 68064be63..c8d7b4ae7 100644 --- a/lib/ui/viewer/gallery/gallery_footer_widget.dart +++ b/lib/ui/home/preserve_footer_widget.dart @@ -1,5 +1,3 @@ -// @dart=2.9 - import 'package:flutter/material.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:photos/services/local_sync_service.dart'; @@ -7,8 +5,8 @@ import 'package:photos/ui/backup_folder_selection_page.dart'; import 'package:photos/ui/common/gradient_button.dart'; import 'package:photos/utils/navigation_util.dart'; -class GalleryFooterWidget extends StatelessWidget { - const GalleryFooterWidget({Key key}) : super(key: key); +class PreserveFooterWidget extends StatelessWidget { + const PreserveFooterWidget({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/ui/home/start_backup_hook_widget.dart b/lib/ui/home/start_backup_hook_widget.dart new file mode 100644 index 000000000..9abdc0d29 --- /dev/null +++ b/lib/ui/home/start_backup_hook_widget.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:photo_manager/photo_manager.dart'; +import 'package:photos/services/local_sync_service.dart'; +import 'package:photos/ui/backup_folder_selection_page.dart'; +import 'package:photos/ui/common/gradient_button.dart'; +import 'package:photos/utils/navigation_util.dart'; + +class StartBackupHookWidget extends StatelessWidget { + final Widget headerWidget; + + const StartBackupHookWidget({super.key, required this.headerWidget}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + headerWidget, + Padding( + padding: const EdgeInsets.only(top: 64), + child: Image.asset( + "assets/onboarding_safe.png", + height: 206, + ), + ), + Text( + 'No photos are being backed up right now', + style: Theme.of(context) + .textTheme + .caption! + .copyWith(fontFamily: 'Inter-Medium', fontSize: 16), + ), + Center( + child: Material( + type: MaterialType.transparency, + child: Container( + width: double.infinity, + height: 64, + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: GradientButton( + onTap: () async { + if (LocalSyncService.instance + .hasGrantedLimitedPermissions()) { + PhotoManager.presentLimited(); + } else { + routeToPage( + context, + const BackupFolderSelectionPage( + buttonText: "Start backup", + ), + ); + } + }, + text: "Start backup", + ), + ), + ), + ), + const Padding(padding: EdgeInsets.all(50)), + ], + ); + } +} diff --git a/lib/ui/status_bar_widget.dart b/lib/ui/home/status_bar_widget.dart similarity index 99% rename from lib/ui/status_bar_widget.dart rename to lib/ui/home/status_bar_widget.dart index f59bb8b25..bf67a53e5 100644 --- a/lib/ui/status_bar_widget.dart +++ b/lib/ui/home/status_bar_widget.dart @@ -13,7 +13,7 @@ import 'package:photos/theme/text_style.dart'; import 'package:photos/ui/account/verify_recovery_page.dart'; import 'package:photos/ui/components/home_header_widget.dart'; import 'package:photos/ui/components/notification_warning_widget.dart'; -import 'package:photos/ui/header_error_widget.dart'; +import 'package:photos/ui/home/header_error_widget.dart'; import 'package:photos/utils/navigation_util.dart'; const double kContainerHeight = 36; diff --git a/lib/ui/home_widget.dart b/lib/ui/home_widget.dart index 218dfa489..b6535d39a 100644 --- a/lib/ui/home_widget.dart +++ b/lib/ui/home_widget.dart @@ -2,62 +2,48 @@ import 'dart:async'; import 'dart:io'; -import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; import 'package:move_to_background/move_to_background.dart'; -import 'package:photo_manager/photo_manager.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/core/event_bus.dart'; -import 'package:photos/db/files_db.dart'; import 'package:photos/ente_theme_data.dart'; import 'package:photos/events/account_configured_event.dart'; import 'package:photos/events/backup_folders_updated_event.dart'; -import 'package:photos/events/files_updated_event.dart'; -import 'package:photos/events/force_reload_home_gallery_event.dart'; -import 'package:photos/events/local_photos_updated_event.dart'; import 'package:photos/events/permission_granted_event.dart'; import 'package:photos/events/subscription_purchased_event.dart'; import 'package:photos/events/sync_status_update_event.dart'; import 'package:photos/events/tab_changed_event.dart'; import 'package:photos/events/trigger_logout_event.dart'; import 'package:photos/events/user_logged_out_event.dart'; -import 'package:photos/models/file_load_result.dart'; import 'package:photos/models/gallery_type.dart'; import 'package:photos/models/selected_files.dart'; import 'package:photos/services/collections_service.dart'; -import 'package:photos/services/ignored_files_service.dart'; import 'package:photos/services/local_sync_service.dart'; import 'package:photos/services/update_service.dart'; import 'package:photos/services/user_service.dart'; import 'package:photos/states/user_details_state.dart'; -import 'package:photos/theme/colors.dart'; -import 'package:photos/theme/effects.dart'; import 'package:photos/theme/ente_theme.dart'; -import 'package:photos/ui/backup_folder_selection_page.dart'; import 'package:photos/ui/collections_gallery_widget.dart'; import 'package:photos/ui/common/bottom_shadow.dart'; -import 'package:photos/ui/common/gradient_button.dart'; import 'package:photos/ui/create_collection_page.dart'; import 'package:photos/ui/extents_page_view.dart'; -import 'package:photos/ui/grant_permissions_widget.dart'; -import 'package:photos/ui/landing_page_widget.dart'; +import 'package:photos/ui/home/grant_permissions_widget.dart'; +import 'package:photos/ui/home/header_widget.dart'; +import 'package:photos/ui/home/home_bottom_nav_bar.dart'; +import 'package:photos/ui/home/home_gallery_widget.dart'; +import 'package:photos/ui/home/landing_page_widget.dart'; +import 'package:photos/ui/home/preserve_footer_widget.dart'; +import 'package:photos/ui/home/start_backup_hook_widget.dart'; import 'package:photos/ui/loading_photos_widget.dart'; -import 'package:photos/ui/memories_widget.dart'; -import 'package:photos/ui/nav_bar.dart'; import 'package:photos/ui/settings/app_update_dialog.dart'; import 'package:photos/ui/settings_page.dart'; import 'package:photos/ui/shared_collections_gallery.dart'; -import 'package:photos/ui/status_bar_widget.dart'; -import 'package:photos/ui/viewer/gallery/gallery.dart'; -import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart'; -import 'package:photos/ui/viewer/gallery/gallery_footer_widget.dart'; import 'package:photos/ui/viewer/gallery/gallery_overlay_widget.dart'; import 'package:photos/utils/dialog_util.dart'; -import 'package:photos/utils/navigation_util.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:uni_links/uni_links.dart'; @@ -81,7 +67,6 @@ class _HomeWidgetState extends State { final PageController _pageController = PageController(); int _selectedTabIndex = 0; - Widget _headerWidgetWithSettingsButton; // for receiving media files // ignore: unused_field @@ -100,11 +85,6 @@ class _HomeWidgetState extends State { @override void initState() { _logger.info("Building initstate"); - _headerWidgetWithSettingsButton = Stack( - children: const [ - _headerWidget, - ], - ); _tabChangedEventSubscription = Bus.instance.on().listen((event) { if (event.source != TabChangedEventSource.pageView) { @@ -126,34 +106,7 @@ class _HomeWidgetState extends State { }); _triggerLogoutEvent = Bus.instance.on().listen((event) async { - final AlertDialog alert = AlertDialog( - title: const Text("Session expired"), - content: const Text("Please login again"), - actions: [ - TextButton( - child: Text( - "Ok", - style: TextStyle( - color: Theme.of(context).colorScheme.greenAlternative, - ), - ), - onPressed: () async { - Navigator.of(context, rootNavigator: true).pop('dialog'); - final dialog = createProgressDialog(context, "Logging out..."); - await dialog.show(); - await Configuration.instance.logout(); - await dialog.hide(); - }, - ), - ], - ); - - showDialog( - context: context, - builder: (BuildContext context) { - return alert; - }, - ); + await _autoLogoutAlert(); }); _loggedOutEvent = Bus.instance.on().listen((event) { _logger.info('logged out, selectTab index to 0'); @@ -218,6 +171,37 @@ class _HomeWidgetState extends State { super.initState(); } + Future _autoLogoutAlert() async { + final AlertDialog alert = AlertDialog( + title: const Text("Session expired"), + content: const Text("Please login again"), + actions: [ + TextButton( + child: Text( + "Ok", + style: TextStyle( + color: Theme.of(context).colorScheme.greenAlternative, + ), + ), + onPressed: () async { + Navigator.of(context, rootNavigator: true).pop('dialog'); + final dialog = createProgressDialog(context, "Logging out..."); + await dialog.show(); + await Configuration.instance.logout(); + await dialog.hide(); + }, + ), + ], + ); + + await showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + @override void dispose() { _tabChangedEventSubscription.cancel(); @@ -345,8 +329,12 @@ class _HomeWidgetState extends State { physics: const BouncingScrollPhysics(), children: [ showBackupFolderHook - ? _getBackupFolderSelectionHook() - : _getMainGalleryWidget(), + ? const StartBackupHookWidget(headerWidget: _headerWidget) + : HomeGalleryWidget( + header: _headerWidget, + footer: const PreserveFooterWidget(), + selectedFiles: _selectedFiles, + ), _deviceFolderGalleryWidget, _sharedCollectionGallery, ], @@ -422,347 +410,4 @@ class _HomeWidgetState extends State { final ott = Uri.parse(link).queryParameters["ott"]; UserService.instance.verifyEmail(context, ott); } - - Widget _getMainGalleryWidget() { - Widget header; - if (_selectedFiles.files.isEmpty) { - header = _headerWidgetWithSettingsButton; - } else { - header = _headerWidget; - } - final gallery = Gallery( - asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async { - final ownerID = Configuration.instance.getUserID(); - final hasSelectedAllForBackup = - Configuration.instance.hasSelectedAllFoldersForBackup(); - final archivedCollectionIds = - CollectionsService.instance.getArchivedCollections(); - FileLoadResult result; - if (hasSelectedAllForBackup) { - result = await FilesDB.instance.getAllLocalAndUploadedFiles( - creationStartTime, - creationEndTime, - ownerID, - limit: limit, - asc: asc, - ignoredCollectionIDs: archivedCollectionIds, - ); - } else { - result = await FilesDB.instance.getAllPendingOrUploadedFiles( - creationStartTime, - creationEndTime, - ownerID, - limit: limit, - asc: asc, - ignoredCollectionIDs: archivedCollectionIds, - ); - } - - // hide ignored files from home page UI - final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs; - result.files.removeWhere( - (f) => - f.uploadedFileID == null && - IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, f), - ); - return result; - }, - reloadEvent: Bus.instance.on(), - removalEventTypes: const { - EventType.deletedFromRemote, - EventType.deletedFromEverywhere, - EventType.archived, - }, - forceReloadEvents: [ - Bus.instance.on(), - Bus.instance.on(), - ], - tagPrefix: "home_gallery", - selectedFiles: _selectedFiles, - header: header, - footer: const GalleryFooterWidget(), - ); - return Stack( - children: [ - Container( - child: gallery, - ), - HomePageAppBar(_selectedFiles), - ], - ); - } - - Widget _getBackupFolderSelectionHook() { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _headerWidgetWithSettingsButton, - Padding( - padding: const EdgeInsets.only(top: 64), - child: Image.asset( - "assets/onboarding_safe.png", - height: 206, - ), - ), - Text( - 'No photos are being backed up right now', - style: Theme.of(context) - .textTheme - .caption - .copyWith(fontFamily: 'Inter-Medium', fontSize: 16), - ), - Center( - child: Material( - type: MaterialType.transparency, - child: Container( - width: double.infinity, - height: 64, - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: GradientButton( - onTap: () async { - if (LocalSyncService.instance - .hasGrantedLimitedPermissions()) { - PhotoManager.presentLimited(); - } else { - routeToPage( - context, - const BackupFolderSelectionPage( - buttonText: "Start backup", - ), - ); - } - }, - text: "Start backup", - ), - ), - ), - ), - const Padding(padding: EdgeInsets.all(50)), - ], - ); - } -} - -class HomePageAppBar extends StatefulWidget { - const HomePageAppBar( - this.selectedFiles, { - Key key, - }) : super(key: key); - - final SelectedFiles selectedFiles; - - @override - State createState() => _HomePageAppBarState(); -} - -class _HomePageAppBarState extends State { - @override - void initState() { - super.initState(); - widget.selectedFiles.addListener(() { - setState(() {}); - }); - } - - @override - Widget build(BuildContext context) { - final appBar = SizedBox( - height: 60, - child: GalleryAppBarWidget( - GalleryType.homepage, - null, - widget.selectedFiles, - ), - ); - if (widget.selectedFiles.files.isEmpty) { - return IgnorePointer(child: appBar); - } else { - return appBar; - } - } -} - -class HomeBottomNavigationBar extends StatefulWidget { - const HomeBottomNavigationBar( - this.selectedFiles, { - this.selectedTabIndex, - Key key, - }) : super(key: key); - - final SelectedFiles selectedFiles; - final int selectedTabIndex; - - @override - State createState() => - _HomeBottomNavigationBarState(); -} - -class _HomeBottomNavigationBarState extends State { - StreamSubscription _tabChangedEventSubscription; - int currentTabIndex = 0; - - @override - void initState() { - super.initState(); - currentTabIndex = widget.selectedTabIndex; - widget.selectedFiles.addListener(() { - setState(() {}); - }); - _tabChangedEventSubscription = - Bus.instance.on().listen((event) { - if (event.source != TabChangedEventSource.tabBar) { - debugPrint('index changed to ${event.selectedIndex}'); - if (mounted) { - setState(() { - currentTabIndex = event.selectedIndex; - }); - } - } - }); - } - - @override - void dispose() { - _tabChangedEventSubscription.cancel(); - super.dispose(); - } - - void _onTabChange(int index) { - Bus.instance.fire( - TabChangedEvent( - index, - TabChangedEventSource.tabBar, - ), - ); - } - - @override - Widget build(BuildContext context) { - final bool filesAreSelected = widget.selectedFiles.files.isNotEmpty; - final enteColorScheme = getEnteColorScheme(context); - final navBarBlur = - MediaQuery.of(context).platformBrightness == Brightness.light - ? blurBase - : blurMuted; - - return AnimatedContainer( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - height: filesAreSelected ? 0 : 56, - child: AnimatedOpacity( - duration: const Duration(milliseconds: 100), - opacity: filesAreSelected ? 0.0 : 1.0, - curve: Curves.easeIn, - child: IgnorePointer( - ignoring: filesAreSelected, - child: ListView( - physics: const NeverScrollableScrollPhysics(), - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(32), - child: Container( - alignment: Alignment.bottomCenter, - height: 48, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: navBarBlur, - sigmaY: navBarBlur, - ), - child: GNav( - curve: Curves.easeOutExpo, - backgroundColor: - getEnteColorScheme(context).fillMuted, - mainAxisAlignment: MainAxisAlignment.center, - rippleColor: Colors.white.withOpacity(0.1), - activeColor: Theme.of(context) - .colorScheme - .gNavBarActiveColor, - iconSize: 24, - padding: const EdgeInsets.fromLTRB(16, 6, 16, 6), - duration: const Duration(milliseconds: 200), - gap: 0, - tabBorderRadius: 32, - tabBackgroundColor: Theme.of(context) - .colorScheme - .gNavBarActiveColor, - haptic: false, - tabs: [ - GButton( - margin: const EdgeInsets.fromLTRB(8, 6, 10, 6), - icon: Icons.home_rounded, - iconColor: enteColorScheme.tabIcon, - iconActiveColor: strokeBaseLight, - text: '', - onPressed: () { - _onTabChange( - 0, - ); // To take care of occasional missing events - }, - ), - GButton( - margin: const EdgeInsets.fromLTRB(10, 6, 10, 6), - icon: Icons.collections_rounded, - iconColor: enteColorScheme.tabIcon, - iconActiveColor: strokeBaseLight, - text: '', - onPressed: () { - _onTabChange( - 1, - ); // To take care of occasional missing events - }, - ), - GButton( - margin: const EdgeInsets.fromLTRB(10, 6, 8, 6), - icon: Icons.people_outlined, - iconColor: enteColorScheme.tabIcon, - iconActiveColor: strokeBaseLight, - text: '', - onPressed: () { - _onTabChange( - 2, - ); // To take care of occasional missing events - }, - ), - ], - selectedIndex: currentTabIndex, - onTabChange: _onTabChange, - ), - ), - ), - ), - ), - ], - ), - ], - ), - ), - ), - ); - } -} - -class HeaderWidget extends StatelessWidget { - static const _memoriesWidget = MemoriesWidget(); - static const _statusBarWidget = StatusBarWidget(); - - const HeaderWidget({ - Key key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - Logger("Header").info("Building header widget"); - const list = [ - _statusBarWidget, - _memoriesWidget, - ]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: list, - ); - } }