Page navigation routing

This commit is contained in:
wanghongenpin
2024-10-01 16:14:27 +08:00
parent e770d5a6d5
commit 201166e466
11 changed files with 131 additions and 80 deletions

View File

@@ -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
}
}
}

View File

@@ -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},

View File

@@ -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<StatefulWidget> createState() => _MePageState();
}
class _MeMenus extends StatelessWidget {
final ProxyServer proxyServer;
const _MeMenus({required this.proxyServer});
class _MePageState extends State<MePage> {
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),

View File

@@ -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<MobileHomePage> implements EventListener, Li
int exitTime = 0;
var requestPageNavigatorKey = GlobalKey<NavigatorState>();
var toolboxNavigatorKey = GlobalKey<NavigatorState>();
var mePageNavigatorKey = GlobalKey<NavigatorState>();
@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<MobileHomePage> 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<MobileHomePage> 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<MobileHomePage> 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<Workspace> createState() => WorkspaceState();
State<RequestPage> createState() => RequestPageState();
}
class WorkspaceState extends State<Workspace> {
class RequestPageState extends State<RequestPage> {
/// 远程连接
final ValueNotifier<RemoteModel> 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: [

View File

@@ -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)),

View File

@@ -113,15 +113,13 @@ class RequestRowState extends State<RequestRow> {
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<RequestRow> {
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<RequestRow> {
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<RequestRow> {
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<RequestRow> {
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<RequestRow> {
//显示高级重发
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<RequestRow> {
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);
}
});
});
}

View File

@@ -76,8 +76,8 @@ class MobileSearchState extends State<MobileSearch> {
},
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')));
}

View File

@@ -397,7 +397,7 @@ class _DomainListState extends State<DomainList> {
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)),

View File

@@ -340,7 +340,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
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)),

View File

@@ -668,7 +668,7 @@ class _ScriptListState extends State<ScriptList> {
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)),

View File

@@ -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<NavigatorState> 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<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
@@ -29,10 +28,39 @@ class NavigatorHelper {
}
//返回上一页
static Future<bool> maybePop<T extends Object?>( [ T? result ]) {
static Future<bool> maybePop<T extends Object?>([T? result]) {
return Navigator.of(NavigatorHelper().context).maybePop<T>(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);
},
));
}
}