From 5ff36a86433b82aa9e2ac54381b5b48b2c2d7f1e Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 15 Sep 2024 14:53:41 +0600 Subject: [PATCH] fix(android): pressing back while the player is open doesn't take to previous page #1388 --- lib/components/framework/app_pop_scope.dart | 104 ++++++++++++++++++++ lib/modules/player/player.dart | 9 +- lib/pages/root/root_app.dart | 17 ++-- pubspec.lock | 4 +- pubspec.yaml | 2 +- 5 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 lib/components/framework/app_pop_scope.dart diff --git a/lib/components/framework/app_pop_scope.dart b/lib/components/framework/app_pop_scope.dart new file mode 100644 index 00000000..b8e35767 --- /dev/null +++ b/lib/components/framework/app_pop_scope.dart @@ -0,0 +1,104 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +/// A temporary workaround for [WillPopScope] and [PopScope] not working in GoRouter +/// https://github.com/flutter/flutter/issues/140869#issuecomment-2247181468 +class AppPopScope extends StatefulWidget { + final Widget child; + + final PopInvokedCallback? onPopInvoked; + + final bool canPop; + + const AppPopScope({ + super.key, + required this.child, + this.canPop = true, + this.onPopInvoked, + }); + + @override + State createState() => _AppPopScopeState(); +} + +class _AppPopScopeState extends State { + final bool _enable = Platform.isAndroid; + ModalRoute? _route; + BackButtonDispatcher? _parentBackBtnDispatcher; + ChildBackButtonDispatcher? _backBtnDispatcher; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _route = ModalRoute.of(context); + _updateBackButtonDispatcher(); + } + + @override + void activate() { + super.activate(); + _updateBackButtonDispatcher(); + } + + @override + void deactivate() { + super.deactivate(); + _disposeBackBtnDispatcher(); + } + + @override + void dispose() { + _disposeBackBtnDispatcher(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: widget.canPop, + onPopInvoked: widget.onPopInvoked, + child: widget.child, + ); + } + + void _updateBackButtonDispatcher() { + if (!_enable) return; + + var dispatcher = Router.maybeOf(context)?.backButtonDispatcher; + if (dispatcher != _parentBackBtnDispatcher) { + _disposeBackBtnDispatcher(); + _parentBackBtnDispatcher = dispatcher; + if (dispatcher is BackButtonDispatcher && + dispatcher is! ChildBackButtonDispatcher) { + dispatcher = dispatcher.createChildBackButtonDispatcher(); + } + _backBtnDispatcher = dispatcher as ChildBackButtonDispatcher; + } + _backBtnDispatcher?.removeCallback(_handleBackButton); + _backBtnDispatcher?.addCallback(_handleBackButton); + _backBtnDispatcher?.takePriority(); + } + + void _disposeBackBtnDispatcher() { + _backBtnDispatcher?.removeCallback(_handleBackButton); + if (_backBtnDispatcher is ChildBackButtonDispatcher) { + final child = _backBtnDispatcher as ChildBackButtonDispatcher; + _parentBackBtnDispatcher?.forget(child); + } + _backBtnDispatcher = null; + _parentBackBtnDispatcher = null; + } + + bool get _onlyRoute => _route != null && _route!.isFirst && _route!.isCurrent; + + Future _handleBackButton() async { + if (_onlyRoute) { + widget.onPopInvoked?.call(widget.canPop); + if (!widget.canPop) { + return true; + } + } + return false; + } +} diff --git a/lib/modules/player/player.dart b/lib/modules/player/player.dart index 3202eeda..93aec5f9 100644 --- a/lib/modules/player/player.dart +++ b/lib/modules/player/player.dart @@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/framework/app_pop_scope.dart'; import 'package:spotube/modules/player/player_actions.dart'; import 'package:spotube/modules/player/player_controls.dart'; import 'package:spotube/modules/player/player_queue.dart'; @@ -100,11 +101,11 @@ class PlayerView extends HookConsumerWidget { final topPadding = MediaQueryData.fromView(View.of(context)).padding.top; - // ignore: deprecated_member_use - return WillPopScope( - onWillPop: () async { + return AppPopScope( + canPop: context.canPop(), + onPopInvoked: (didPop) async { + if (didPop) return; await panelController.close(); - return false; }, child: IconTheme( data: theme.iconTheme.copyWith(color: bodyTextColor), diff --git a/lib/pages/root/root_app.dart b/lib/pages/root/root_app.dart index f7aedf63..c48dbf37 100644 --- a/lib/pages/root/root_app.dart +++ b/lib/pages/root/root_app.dart @@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/framework/app_pop_scope.dart'; import 'package:spotube/modules/player/player_queue.dart'; import 'package:spotube/components/dialogs/replace_downloaded_dialog.dart'; import 'package:spotube/modules/root/bottom_player.dart'; @@ -30,10 +31,12 @@ class RootApp extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); + final routerState = GoRouterState.of(context); + final showingDialogCompleter = useRef(Completer()..complete()); final downloader = ref.watch(downloadManagerProvider); final scaffoldMessenger = ScaffoldMessenger.of(context); - final theme = Theme.of(context); final connectRoutes = ref.watch(serverConnectRoutesProvider); useEffect(() { @@ -164,15 +167,17 @@ class RootApp extends HookConsumerWidget { return null; }, [backgroundColor]); - // ignore: deprecated_member_use - return WillPopScope( - onWillPop: () async { + return AppPopScope( + // Only allow to pop when in root screen + canPop: routerState.namedLocation(HomePage.name) == + routerState.matchedLocation, + onPopInvoked: (didPop) async { + if (didPop) return; + final routerState = GoRouterState.of(context); if (routerState.matchedLocation != "/") { context.goNamed(HomePage.name); - return false; } - return true; }, child: Scaffold( body: Sidebar(child: child), diff --git a/pubspec.lock b/pubspec.lock index 3249c759..089563d8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1006,10 +1006,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15 + sha256: "2ddb88e9ad56ae15ee144ed10e33886777eb5ca2509a914850a5faa7b52ff459" url: "https://pub.dev" source: hosted - version: "12.1.3" + version: "14.2.7" google_fonts: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d69ab5db..4972fc82 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,7 +51,6 @@ dependencies: flutter_svg: ^1.1.6 form_validator: ^2.1.1 fuzzywuzzy: ^1.1.6 - go_router: 12.1.3 # Stuck on this https://github.com/flutter/flutter/issues/140869 google_fonts: ^6.2.1 hive: ^2.2.3 hive_flutter: ^1.1.0 @@ -135,6 +134,7 @@ dependencies: sqlite3_flutter_libs: ^0.5.23 sqlite3: ^2.4.3 encrypt: ^5.0.3 + go_router: ^14.2.7 dev_dependencies: build_runner: ^2.4.9