From 201166e466b73b82653c91ccb8e771fa93fe2945 Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Tue, 1 Oct 2024 16:14:27 +0800 Subject: [PATCH] Page navigation routing --- .../com/network/proxy/vpn/ProxyVpnThread.kt | 4 ++ lib/ui/configuration.dart | 2 +- lib/ui/mobile/menu/me.dart | 24 +++---- lib/ui/mobile/mobile.dart | 72 ++++++++++++------- lib/ui/mobile/request/favorite.dart | 2 +- lib/ui/mobile/request/request.dart | 59 ++++++++------- lib/ui/mobile/request/search.dart | 4 +- lib/ui/mobile/setting/filter.dart | 2 +- lib/ui/mobile/setting/request_rewrite.dart | 2 +- lib/ui/mobile/setting/script.dart | 2 +- lib/utils/navigator.dart | 38 ++++++++-- 11 files changed, 131 insertions(+), 80 deletions(-) diff --git a/android/app/src/main/kotlin/com/network/proxy/vpn/ProxyVpnThread.kt b/android/app/src/main/kotlin/com/network/proxy/vpn/ProxyVpnThread.kt index c88f400..d76a0c1 100644 --- a/android/app/src/main/kotlin/com/network/proxy/vpn/ProxyVpnThread.kt +++ b/android/app/src/main/kotlin/com/network/proxy/vpn/ProxyVpnThread.kt @@ -80,6 +80,10 @@ class ProxyVpnThread( } catch (e: Exception) { val errorMessage = (e.message ?: e.toString()) Log.e(TAG, errorMessage, e) + if (!vpnReadChannel.isOpen) { + Log.i(TAG, "VPN read channel closed") + running = false + } } } diff --git a/lib/ui/configuration.dart b/lib/ui/configuration.dart index 1297326..5103c40 100644 --- a/lib/ui/configuration.dart +++ b/lib/ui/configuration.dart @@ -224,7 +224,7 @@ class AppConfiguration { if (Platforms.isMobile()) 'pipEnabled': pipEnabled.value, if (Platforms.isMobile()) 'pipIcon': pipIcon.value ? true : null, - if (Platforms.isMobile()) 'bottomNavigation': bottomNavigation ? true : null, + if (Platforms.isMobile()) 'bottomNavigation': bottomNavigation, if (Platforms.isDesktop()) "windowSize": windowSize == null ? null : {"width": windowSize?.width, "height": windowSize?.height}, diff --git a/lib/ui/mobile/menu/me.dart b/lib/ui/mobile/menu/me.dart index 4c69070..5657718 100644 --- a/lib/ui/mobile/menu/me.dart +++ b/lib/ui/mobile/menu/me.dart @@ -35,26 +35,17 @@ import 'package:network_proxy/ui/mobile/widgets/about.dart'; /// @author wanghongen /// 2024/9/30 -class MePage extends StatelessWidget { +class MePage extends StatefulWidget { final ProxyServer proxyServer; const MePage({super.key, required this.proxyServer}); @override - Widget build(BuildContext context) { - return Navigator( - initialRoute: '/', - onGenerateRoute: (settings) { - return MaterialPageRoute(builder: (context) => _MeMenus(proxyServer: proxyServer), settings: settings); - }, - ); - } + State createState() => _MePageState(); } -class _MeMenus extends StatelessWidget { - final ProxyServer proxyServer; - - const _MeMenus({required this.proxyServer}); +class _MePageState extends State { + late ProxyServer proxyServer = widget.proxyServer; @override Widget build(BuildContext context) { @@ -66,14 +57,16 @@ class _MeMenus extends StatelessWidget { appBar: PreferredSize( preferredSize: const Size.fromHeight(42), child: AppBar( - title: Text(localizations.me, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500)))), + title: Text(localizations.me, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w400)), + centerTitle: true, + )), body: ListView( padding: const EdgeInsets.only(top: 5, left: 5), children: [ const SizedBox(height: 10), ListTile( title: Text(localizations.httpsProxy), - leading: Icon(Icons.https, color: proxyServer.enableSsl ? color : Colors.red), + leading: Icon(Icons.https, color: color), trailing: const Icon(Icons.arrow_forward_ios, size: 16), onTap: () => navigator(context, MobileSslWidget(proxyServer: proxyServer))), const Divider(thickness: 0.35), @@ -145,7 +138,6 @@ class _MeMenus extends StatelessWidget { } void navigator(BuildContext context, Widget widget) async { - await Navigator.maybePop(context); if (context.mounted) { Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) => widget), diff --git a/lib/ui/mobile/mobile.dart b/lib/ui/mobile/mobile.dart index d2a85ac..860c861 100644 --- a/lib/ui/mobile/mobile.dart +++ b/lib/ui/mobile/mobile.dart @@ -46,6 +46,7 @@ import 'package:network_proxy/ui/mobile/widgets/pip.dart'; import 'package:network_proxy/utils/ip.dart'; import 'package:network_proxy/utils/lang.dart'; import 'package:network_proxy/utils/listenable_list.dart'; +import 'package:network_proxy/utils/navigator.dart'; ///移动端首页 ///@author wanghongen @@ -123,18 +124,27 @@ class MobileHomeState extends State implements EventListener, Li int exitTime = 0; + var requestPageNavigatorKey = GlobalKey(); + var toolboxNavigatorKey = GlobalKey(); + var mePageNavigatorKey = GlobalKey(); + @override Widget build(BuildContext context) { var navigationView = [ - Workspace(proxyServer: proxyServer, appConfiguration: widget.appConfiguration), - Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(42), - child: AppBar( - title: Text(localizations.toolbox, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500)), - centerTitle: true)), - body: Toolbox(proxyServer: proxyServer)), - MePage(proxyServer: proxyServer), + NavigatorPage( + navigatorKey: requestPageNavigatorKey, + child: RequestPage(proxyServer: proxyServer, appConfiguration: widget.appConfiguration)), + NavigatorPage( + navigatorKey: toolboxNavigatorKey, + child: Scaffold( + appBar: PreferredSize( + preferredSize: const Size.fromHeight(42), + child: AppBar( + title: Text(localizations.toolbox, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w400)), + centerTitle: true)), + body: Toolbox(proxyServer: proxyServer))), + NavigatorPage(navigatorKey: mePageNavigatorKey, child: MePage(proxyServer: proxyServer)), ]; if (!widget.appConfiguration.bottomNavigation) _selectIndex.value = 0; @@ -146,6 +156,10 @@ class MobileHomeState extends State implements EventListener, Li return; } + if (navigationView[_selectIndex.value].onPopInvoked()) { + return; + } + if (DateTime.now().millisecondsSinceEpoch - exitTime > 2000) { exitTime = DateTime.now().millisecondsSinceEpoch; if (mounted) { @@ -162,19 +176,23 @@ class MobileHomeState extends State implements EventListener, Li builder: (context, index, child) => Scaffold( body: LazyIndexedStack(index: index, children: navigationView), bottomNavigationBar: widget.appConfiguration.bottomNavigation - ? SizedBox( - height: 72, - child: BottomNavigationBar( - selectedFontSize: 0, - items: [ - BottomNavigationBarItem(icon: const Icon(Icons.workspaces), label: localizations.requests), - BottomNavigationBarItem(icon: const Icon(Icons.construction), label: localizations.toolbox), - BottomNavigationBarItem(icon: const Icon(Icons.person), label: localizations.me), - ], - currentIndex: _selectIndex.value, - onTap: (index) => _selectIndex.value = index, - ), - ) + ? Container( + constraints: const BoxConstraints(maxHeight: 72), + child: Theme( + data: Theme.of(context).copyWith(splashColor: Colors.transparent), + child: BottomNavigationBar( + selectedFontSize: 0, + items: [ + BottomNavigationBarItem( + icon: const Icon(Icons.workspaces), label: localizations.requests), + BottomNavigationBarItem( + icon: const Icon(Icons.construction), label: localizations.toolbox), + BottomNavigationBarItem(icon: const Icon(Icons.person), label: localizations.me), + ], + currentIndex: _selectIndex.value, + onTap: (index) => _selectIndex.value = index, + ), + )) : null))); } @@ -280,17 +298,17 @@ class MobileHomeState extends State implements EventListener, Li } } -class Workspace extends StatefulWidget { +class RequestPage extends StatefulWidget { final ProxyServer proxyServer; final AppConfiguration appConfiguration; - const Workspace({super.key, required this.proxyServer, required this.appConfiguration}); + const RequestPage({super.key, required this.proxyServer, required this.appConfiguration}); @override - State createState() => WorkspaceState(); + State createState() => RequestPageState(); } -class WorkspaceState extends State { +class RequestPageState extends State { /// 远程连接 final ValueNotifier desktop = ValueNotifier(RemoteModel(connect: false)); @@ -428,8 +446,10 @@ class _MobileAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { AppLocalizations localizations = AppLocalizations.of(context)!; + var bottomNavigation = appConfiguration.bottomNavigation; return AppBar( + leading: bottomNavigation ? const SizedBox() : null, title: MobileSearch( key: MobileApp.searchStateKey, onSearch: (val) => MobileApp.requestStateKey.currentState?.search(val)), actions: [ diff --git a/lib/ui/mobile/request/favorite.dart b/lib/ui/mobile/request/favorite.dart index 4bb1ea0..1a71e26 100644 --- a/lib/ui/mobile/request/favorite.dart +++ b/lib/ui/mobile/request/favorite.dart @@ -205,7 +205,7 @@ class _FavoriteItemState extends State<_FavoriteItem> { Container(color: Theme.of(context).hoverColor, height: 8), TextButton( child: Container( - height: 50, + height: 45, width: double.infinity, padding: const EdgeInsets.only(top: 10), child: Text(localizations.cancel, textAlign: TextAlign.center)), diff --git a/lib/ui/mobile/request/request.dart b/lib/ui/mobile/request/request.dart index 87b5d5d..c557783 100644 --- a/lib/ui/mobile/request/request.dart +++ b/lib/ui/mobile/request/request.dart @@ -113,15 +113,13 @@ class RequestRowState extends State { Platform.isIOS ? const EdgeInsets.symmetric(horizontal: 8) : const EdgeInsets.only(left: 3, right: 5), onLongPress: menu, onTap: () { - NavigatorHelper.push(MaterialPageRoute( - settings: const RouteSettings(name: "NetworkTabController"), - builder: (context) { - return NetworkTabController( - proxyServer: widget.proxyServer, - httpRequest: request, - httpResponse: response, - title: Text(localizations.captureDetail, style: const TextStyle(fontSize: 16))); - })); + Navigator.of(context).push(MaterialPageRoute(builder: (context) { + return NetworkTabController( + proxyServer: widget.proxyServer, + httpRequest: request, + httpResponse: response, + title: Text(localizations.captureDetail, style: const TextStyle(fontSize: 16))); + })); }); } @@ -171,7 +169,7 @@ class RequestRowState extends State { child: SizedBox(width: double.infinity, child: Text(localizations.repeat, textAlign: TextAlign.center)), onPressed: () { onRepeat(widget.request); - NavigatorHelper.pop(); + Navigator.maybePop(context); }), const Divider(thickness: 0.5, height: 5), TextButton( @@ -183,10 +181,15 @@ class RequestRowState extends State { child: SizedBox(width: double.infinity, child: Text(localizations.editRequest, textAlign: TextAlign.center)), onPressed: () { - NavigatorHelper.pop(); - NavigatorHelper.push(MaterialPageRoute( + Navigator.maybePop(context); + var pageRoute = MaterialPageRoute( builder: (context) => - MobileRequestEditor(request: widget.request, proxyServer: widget.proxyServer))); + MobileRequestEditor(request: widget.request, proxyServer: widget.proxyServer)); + if (mounted) { + Navigator.push(context, pageRoute); + } else { + NavigatorHelper.push(pageRoute); + } }), const Divider(thickness: 0.5, height: 5), TextButton( @@ -194,7 +197,7 @@ class RequestRowState extends State { onPressed: () { FavoriteStorage.addFavorite(widget.request); FlutterToastr.show(localizations.addSuccess, context); - NavigatorHelper.pop(); + Navigator.maybePop(context); }), const Divider(thickness: 0.5, height: 5), TextButton( @@ -202,20 +205,17 @@ class RequestRowState extends State { onPressed: () { widget.onRemove?.call(request); FlutterToastr.show(localizations.deleteSuccess, context); - NavigatorHelper.pop(); + Navigator.maybePop(context); }), - Container( - color: Theme.of(context).hoverColor, - height: 8, - ), + Container(color: Theme.of(context).hoverColor, height: 8), TextButton( child: Container( - height: 55, + height: 45, width: double.infinity, padding: const EdgeInsets.only(top: 10), child: Text(localizations.cancel, textAlign: TextAlign.center)), onPressed: () { - NavigatorHelper.pop(); + Navigator.maybePop(context); }, ), ]); @@ -228,10 +228,15 @@ class RequestRowState extends State { //显示高级重发 showCustomRepeat(HttpRequest request) { - NavigatorHelper.pop(); - NavigatorHelper.push(MaterialPageRoute( + Navigator.maybePop(context); + var pageRoute = MaterialPageRoute( builder: (context) => futureWidget(SharedPreferences.getInstance(), - (prefs) => MobileCustomRepeat(onRepeat: () => onRepeat(request), prefs: prefs)))); + (prefs) => MobileCustomRepeat(onRepeat: () => onRepeat(request), prefs: prefs))); + if (mounted) { + Navigator.push(context, pageRoute); + } else { + NavigatorHelper.push(pageRoute); + } } onRepeat(HttpRequest request) { @@ -249,8 +254,10 @@ class RequestRowState extends State { child: SizedBox(width: double.infinity, child: Text(title, textAlign: TextAlign.center)), onPressed: () { Clipboard.setData(ClipboardData(text: callback.call())).then((value) { - FlutterToastr.show(localizations.copied, context); - NavigatorHelper.pop(); + if (mounted) { + FlutterToastr.show(localizations.copied, context); + Navigator.maybePop(context); + } }); }); } diff --git a/lib/ui/mobile/request/search.dart b/lib/ui/mobile/request/search.dart index 266513f..29c116f 100644 --- a/lib/ui/mobile/request/search.dart +++ b/lib/ui/mobile/request/search.dart @@ -76,8 +76,8 @@ class MobileSearchState extends State { }, decoration: InputDecoration( border: InputBorder.none, - prefixIcon: - InkWell(onTap: showSearch, child: Icon(Icons.search, color: _searched ? Colors.green : Colors.blue)), + prefixIcon: InkWell( + onTap: showSearch, child: Icon(Icons.search, color: _searched ? Colors.green : Colors.blue)), hintText: 'Search'))); } diff --git a/lib/ui/mobile/setting/filter.dart b/lib/ui/mobile/setting/filter.dart index 7b06628..c2fde93 100644 --- a/lib/ui/mobile/setting/filter.dart +++ b/lib/ui/mobile/setting/filter.dart @@ -397,7 +397,7 @@ class _DomainListState extends State { Container(color: Theme.of(context).hoverColor, height: 8), TextButton( child: Container( - height: 50, + height: 45, width: double.infinity, padding: const EdgeInsets.only(top: 10), child: Text(localizations.cancel, textAlign: TextAlign.center)), diff --git a/lib/ui/mobile/setting/request_rewrite.dart b/lib/ui/mobile/setting/request_rewrite.dart index 9152a94..f7b8810 100644 --- a/lib/ui/mobile/setting/request_rewrite.dart +++ b/lib/ui/mobile/setting/request_rewrite.dart @@ -340,7 +340,7 @@ class _RequestRuleListState extends State { Container(color: Theme.of(context).hoverColor, height: 8), TextButton( child: Container( - height: 50, + height: 45, width: double.infinity, padding: const EdgeInsets.only(top: 10), child: Text(localizations.cancel, textAlign: TextAlign.center)), diff --git a/lib/ui/mobile/setting/script.dart b/lib/ui/mobile/setting/script.dart index 02a5d43..4140b92 100644 --- a/lib/ui/mobile/setting/script.dart +++ b/lib/ui/mobile/setting/script.dart @@ -668,7 +668,7 @@ class _ScriptListState extends State { Container(color: Theme.of(context).hoverColor, height: 8), TextButton( child: Container( - height: 50, + height: 45, width: double.infinity, padding: const EdgeInsets.only(top: 10), child: Text(localizations.cancel, textAlign: TextAlign.center)), diff --git a/lib/utils/navigator.dart b/lib/utils/navigator.dart index 119f2f8..2de82c9 100644 --- a/lib/utils/navigator.dart +++ b/lib/utils/navigator.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; class NavigatorHelper { + static final NavigatorHelper _instance = NavigatorHelper._internal(); + //私有构造方法 NavigatorHelper._internal(); @@ -10,10 +12,7 @@ class NavigatorHelper { GlobalKey get navigatorKey => _navigatorKey; - BuildContext get context => - NavigatorHelper().navigatorKey.currentState!.context; - - static final NavigatorHelper _instance = NavigatorHelper._internal(); + BuildContext get context => NavigatorHelper().navigatorKey.currentState!.context; //保存单例 final GlobalKey _navigatorKey = GlobalKey(); @@ -29,10 +28,39 @@ class NavigatorHelper { } //返回上一页 - static Future maybePop( [ T? result ]) { + static Future maybePop([T? result]) { return Navigator.of(NavigatorHelper().context).maybePop(result); } } ///定义全局的NavigatorHelper对象,页面引入该文件后可以直接使用 NavigatorHelper navigatorHelper = NavigatorHelper(); + +class NavigatorPage extends StatelessWidget { + final GlobalKey navigatorKey; + final Widget child; + + const NavigatorPage({super.key, required this.child, required this.navigatorKey}); + + bool onPopInvoked() { + var context = navigatorKey.currentState?.context; + if (context == null) return false; + if (Navigator.canPop(context)) { + Navigator.maybePop(context); + return true; + } + return false; + } + + @override + Widget build(BuildContext context) { + return Material( + child: Navigator( + key: navigatorKey, + initialRoute: '/', + onGenerateRoute: (settings) { + return MaterialPageRoute(builder: (context) => child, settings: settings); + }, + )); + } +}