mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-09 00:34:17 +08:00
Optimize request pop-up menu UI
This commit is contained in:
@@ -72,8 +72,8 @@
|
||||
"type": "Type",
|
||||
"enable": "Enable",
|
||||
"example": "Example: ",
|
||||
"responseHeader": "Response Header",
|
||||
"requestHeader": "Request Header",
|
||||
"responseHeader": "Headers",
|
||||
"requestHeader": "Headers",
|
||||
"requestLine": "Request Line",
|
||||
"requestMethod": "Request Method",
|
||||
"param": "Param",
|
||||
@@ -124,6 +124,7 @@
|
||||
"deleteWhitelist": "Delete Proxy Whitelist",
|
||||
"domainListSubtitle": "Last Request Time: {time}, Count: {count}",
|
||||
|
||||
"selectAction": "Select action",
|
||||
"copy": "Copy",
|
||||
"copyHost": "Copy Host",
|
||||
"copyUrl": "Copy URL",
|
||||
|
||||
@@ -124,12 +124,13 @@
|
||||
"deleteWhitelist": "删除代理白名单",
|
||||
"domainListSubtitle": "最后请求时间: {time}, 次数: {count}",
|
||||
|
||||
"selectAction": "选择操作",
|
||||
"copy": "复制",
|
||||
"copyHost": "复制域名",
|
||||
"copyUrl": "复制URL",
|
||||
"copyRequestResponse": "复制 请求和响应",
|
||||
"copyCurl": "复制 cURL 请求",
|
||||
"copyAsPythonRequests": "复制 Python Requests 请求",
|
||||
"copyCurl": "复制 cURL",
|
||||
"copyAsPythonRequests": "复制 Python Requests",
|
||||
"delete": "删除",
|
||||
"rename": "重命名",
|
||||
"repeat": "重放",
|
||||
@@ -153,7 +154,7 @@
|
||||
"keyword": "关键词",
|
||||
"keywordSearchScope": "关键词搜索范围: ",
|
||||
|
||||
"favorite": "收藏请求",
|
||||
"favorite": "收藏",
|
||||
"deleteFavorite": "删除收藏",
|
||||
"emptyFavorite": "暂无收藏",
|
||||
"deleteFavoriteSuccess": "已删除收藏",
|
||||
|
||||
@@ -2,14 +2,10 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_js/quickjs/ffi.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/components/script_manager.dart';
|
||||
import 'package:network_proxy/network/http/http.dart';
|
||||
import 'package:network_proxy/ui/component/utils.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/request_editor.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/script.dart';
|
||||
import 'package:network_proxy/utils/curl.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
@@ -25,67 +21,46 @@ class ShareWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
AppLocalizations localizations = AppLocalizations.of(context)!;
|
||||
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed: () {
|
||||
showMenu(context: context, position: menuPosition(context), items: [
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.shareUrl),
|
||||
return PopupMenuButton(
|
||||
icon: const Icon(Icons.share, size: 24),
|
||||
offset: const Offset(0, 30),
|
||||
itemBuilder: (BuildContext context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.shareUrl),
|
||||
onTap: () {
|
||||
if (request == null) {
|
||||
FlutterToastr.show("Request is empty", context);
|
||||
return;
|
||||
}
|
||||
Share.share(request!.requestUrl, subject: localizations.proxyPinSoftware);
|
||||
},
|
||||
),
|
||||
PopupMenuItem(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(localizations.shareRequestResponse),
|
||||
onTap: () {
|
||||
if (request == null) {
|
||||
FlutterToastr.show("Request is empty", context);
|
||||
return;
|
||||
}
|
||||
Share.share(request!.requestUrl, subject: localizations.proxyPinSoftware);
|
||||
},
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.shareRequestResponse),
|
||||
onTap: () {
|
||||
if (request == null) {
|
||||
FlutterToastr.show("Request is empty", context);
|
||||
return;
|
||||
}
|
||||
var file = XFile.fromData(utf8.encode(copyRequest(request!, response)),
|
||||
name: localizations.captureDetail, mimeType: "txt");
|
||||
Share.shareXFiles([file], fileNameOverrides: ['request.txt'], text: localizations.proxyPinSoftware);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.shareCurl),
|
||||
onTap: () {
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
var text = curlRequest(request!);
|
||||
var file = XFile.fromData(utf8.encode(text), name: "cURL.txt", mimeType: "txt");
|
||||
Share.shareXFiles([file], fileNameOverrides: ["cURL.txt"], text: localizations.proxyPinSoftware);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.requestEdit),
|
||||
onTap: () {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => MobileRequestEditor(request: request, proxyServer: proxyServer)));
|
||||
});
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.script),
|
||||
onTap: () {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
var scriptManager = await ScriptManager.instance;
|
||||
|
||||
var url = '${request?.remoteDomain()}${request?.path()}';
|
||||
var scriptItem = (scriptManager).list.firstWhereOrNull((it) => it.url == url);
|
||||
String? script = scriptItem == null ? null : await scriptManager.getScript(scriptItem);
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ScriptEdit(scriptItem: scriptItem, script: script, url: scriptItem?.url ?? url)));
|
||||
});
|
||||
}),
|
||||
]);
|
||||
});
|
||||
var file = XFile.fromData(utf8.encode(copyRequest(request!, response)),
|
||||
name: localizations.captureDetail, mimeType: "txt");
|
||||
Share.shareXFiles([file], fileNameOverrides: ['request.txt'], text: localizations.proxyPinSoftware);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(localizations.shareCurl),
|
||||
onTap: () {
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
var text = curlRequest(request!);
|
||||
var file = XFile.fromData(utf8.encode(text), name: "cURL.txt", mimeType: "txt");
|
||||
Share.shareXFiles([file], fileNameOverrides: ["cURL.txt"], text: localizations.proxyPinSoftware);
|
||||
}),
|
||||
];
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class _ToolboxState extends State<Toolbox> {
|
||||
onTap: () => encodeWindow(EncoderType.base64, context),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: const Column(children: [Icon(Icons.currency_bitcoin), SizedBox(height: 3), Text('Base64')]),
|
||||
child: const Column(children: [Icon(Icons.format_bold), SizedBox(height: 3), Text('Base64')]),
|
||||
)),
|
||||
const SizedBox(width: 15),
|
||||
InkWell(
|
||||
|
||||
@@ -10,6 +10,7 @@ class CustomPopupMenuItem<T> extends PopupMenuItem<T> {
|
||||
super.height,
|
||||
super.value,
|
||||
super.enabled,
|
||||
super.padding,
|
||||
required Widget super.child,
|
||||
this.color,
|
||||
});
|
||||
@@ -30,6 +31,28 @@ class _CustomPopupMenuItemState<T> extends PopupMenuItemState<T, CustomPopupMenu
|
||||
}
|
||||
}
|
||||
|
||||
class PopupMenuContainer extends PopupMenuEntry {
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
final double height;
|
||||
|
||||
const PopupMenuContainer({super.key, required this.child, this.height = 40});
|
||||
|
||||
@override
|
||||
bool represents(value) => false;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _PopupMenuContainerState();
|
||||
}
|
||||
|
||||
class _PopupMenuContainerState extends State<PopupMenuContainer> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
|
||||
class SwitchWidget extends StatefulWidget {
|
||||
final String? title;
|
||||
final String? subtitle;
|
||||
|
||||
@@ -14,16 +14,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/components/script_manager.dart';
|
||||
import 'package:network_proxy/network/http/http.dart';
|
||||
import 'package:network_proxy/network/http/websocket.dart';
|
||||
import 'package:network_proxy/storage/favorites.dart';
|
||||
import 'package:network_proxy/ui/component/share.dart';
|
||||
import 'package:network_proxy/ui/component/state_component.dart';
|
||||
import 'package:network_proxy/ui/component/utils.dart';
|
||||
import 'package:network_proxy/ui/component/widgets.dart';
|
||||
import 'package:network_proxy/ui/configuration.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/request_editor.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/script.dart';
|
||||
import 'package:network_proxy/utils/lang.dart';
|
||||
import 'package:network_proxy/utils/platform.dart';
|
||||
import 'package:network_proxy/utils/python.dart';
|
||||
|
||||
import 'body.dart';
|
||||
|
||||
@@ -114,7 +122,61 @@ class NetworkTabState extends State<NetworkTabController> with SingleTickerProvi
|
||||
bottom: tabBar,
|
||||
actions: [
|
||||
ShareWidget(
|
||||
proxyServer: widget.proxyServer, request: widget.request.get(), response: widget.response.get())
|
||||
proxyServer: widget.proxyServer, request: widget.request.get(), response: widget.response.get()),
|
||||
const SizedBox(width: 3),
|
||||
PopupMenuButton(
|
||||
offset: const Offset(0, 30),
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.favorite),
|
||||
onTap: () {
|
||||
var request = widget.request.get();
|
||||
if (request == null) return;
|
||||
|
||||
FavoriteStorage.addFavorite(request);
|
||||
FlutterToastr.show(localizations.addSuccess, context);
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.requestEdit),
|
||||
onTap: () {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => MobileRequestEditor(
|
||||
request: widget.request.get(), proxyServer: widget.proxyServer)));
|
||||
});
|
||||
}),
|
||||
PopupMenuItem(
|
||||
child: Text(localizations.script),
|
||||
onTap: () {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
var scriptManager = await ScriptManager.instance;
|
||||
var request = widget.request.get();
|
||||
var url = '${request?.remoteDomain()}${request?.path()}';
|
||||
var scriptItem = (scriptManager).list.firstWhereOrNull((it) => it.url == url);
|
||||
String? script = scriptItem == null ? null : await scriptManager.getScript(scriptItem);
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => ScriptEdit(
|
||||
scriptItem: scriptItem, script: script, url: scriptItem?.url ?? url)));
|
||||
});
|
||||
}),
|
||||
CustomPopupMenuItem(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(localizations.copyAsPythonRequests),
|
||||
onTap: () {
|
||||
var request = widget.request.get();
|
||||
if (request == null) return;
|
||||
|
||||
var text = copyAsPythonRequests(request);
|
||||
Clipboard.setData(ClipboardData(text: text));
|
||||
FlutterToastr.show(localizations.copied, context);
|
||||
})
|
||||
],
|
||||
child: const SizedBox(height: 38, width: 38, child: Icon(Icons.more_vert, size: 28))),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -619,7 +619,7 @@ class _RuleAddDialogState extends State<RuleAddDialog> {
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500, fontSize: 14),
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),
|
||||
errorStyle: const TextStyle(height: 0, fontSize: 0),
|
||||
focusedBorder: focusedBorder(),
|
||||
isDense: true,
|
||||
|
||||
@@ -102,6 +102,11 @@ class _MePageState extends State<MePage> {
|
||||
navigator(context, MobileRequestRewrite(requestRewrites: requestRewrites));
|
||||
}
|
||||
}),
|
||||
ListTile(
|
||||
title: Text(localizations.script),
|
||||
leading: Icon(Icons.javascript_outlined, color: color),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
onTap: () => navigator(context, const MobileScript())),
|
||||
ListTile(
|
||||
title: Text(localizations.requestBlock),
|
||||
leading: Icon(Icons.block_flipped, color: color),
|
||||
@@ -112,11 +117,6 @@ class _MePageState extends State<MePage> {
|
||||
navigator(context, MobileRequestBlock(requestBlockManager: requestBlockManager));
|
||||
}
|
||||
}),
|
||||
ListTile(
|
||||
title: Text(localizations.script),
|
||||
leading: Icon(Icons.code, color: color),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
onTap: () => navigator(context, const MobileScript())),
|
||||
ListTile(
|
||||
title: Text(localizations.setting),
|
||||
leading: Icon(Icons.settings_outlined, color: color),
|
||||
|
||||
@@ -32,7 +32,6 @@ import 'package:network_proxy/ui/mobile/request/repeat.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/request_editor.dart';
|
||||
import 'package:network_proxy/utils/curl.dart';
|
||||
import 'package:network_proxy/utils/lang.dart';
|
||||
import 'package:network_proxy/utils/python.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// 收藏列表页面
|
||||
@@ -164,8 +163,6 @@ class _FavoriteItemState extends State<_FavoriteItem> {
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
menuItem(localizations.copyCurl, () => curlRequest(request)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
menuItem(localizations.copyAsPythonRequests, () => copyAsPythonRequests(request)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
TextButton(
|
||||
child: SizedBox(width: double.infinity, child: Text(localizations.rename, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:network_proxy/network/http_client.dart';
|
||||
import 'package:network_proxy/network/util/cache.dart';
|
||||
import 'package:network_proxy/storage/favorites.dart';
|
||||
import 'package:network_proxy/ui/component/utils.dart';
|
||||
import 'package:network_proxy/ui/component/widgets.dart';
|
||||
import 'package:network_proxy/ui/content/panel.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/repeat.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/request_editor.dart';
|
||||
@@ -94,33 +95,36 @@ class RequestRowState extends State<RequestRow> {
|
||||
|
||||
var highlightColor = KeywordHighlight.getHighlightColor(url);
|
||||
|
||||
return ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
minLeadingWidth: 5,
|
||||
selected: selected,
|
||||
textColor: highlightColor,
|
||||
selectedColor: highlightColor,
|
||||
leading: appIcon(),
|
||||
title: Text(title, overflow: TextOverflow.ellipsis, maxLines: 2, style: const TextStyle(fontSize: 14)),
|
||||
subtitle: Text.rich(
|
||||
maxLines: 1,
|
||||
TextSpan(children: [
|
||||
TextSpan(text: '#${widget.index} ', style: const TextStyle(fontSize: 11, color: Colors.teal)),
|
||||
TextSpan(text: subTitle, style: const TextStyle(fontSize: 11, color: Colors.grey)),
|
||||
])),
|
||||
trailing: getIcon(response),
|
||||
contentPadding:
|
||||
Platform.isIOS ? const EdgeInsets.symmetric(horizontal: 8) : const EdgeInsets.only(left: 3, right: 5),
|
||||
onLongPress: menu,
|
||||
onTap: () {
|
||||
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)));
|
||||
}));
|
||||
});
|
||||
return GestureDetector(
|
||||
onLongPressStart: menu,
|
||||
child: ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
minLeadingWidth: 5,
|
||||
selected: selected,
|
||||
textColor: highlightColor,
|
||||
selectedColor: highlightColor,
|
||||
leading: appIcon(),
|
||||
title: Text(title, overflow: TextOverflow.ellipsis, maxLines: 2, style: const TextStyle(fontSize: 14)),
|
||||
subtitle: Text.rich(
|
||||
maxLines: 1,
|
||||
TextSpan(children: [
|
||||
TextSpan(text: '#${widget.index} ', style: const TextStyle(fontSize: 11, color: Colors.teal)),
|
||||
TextSpan(text: subTitle, style: const TextStyle(fontSize: 11, color: Colors.grey)),
|
||||
])),
|
||||
trailing: getIcon(response),
|
||||
contentPadding:
|
||||
Platform.isIOS ? const EdgeInsets.symmetric(horizontal: 8) : const EdgeInsets.only(left: 3, right: 5),
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(this.context).push(MaterialPageRoute(builder: (context) {
|
||||
return NetworkTabController(
|
||||
proxyServer: widget.proxyServer,
|
||||
httpRequest: request,
|
||||
httpResponse: response,
|
||||
title: Text(localizations.captureDetail, style: const TextStyle(fontSize: 16)));
|
||||
}));
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Widget? appIcon() {
|
||||
@@ -149,78 +153,125 @@ class RequestRowState extends State<RequestRow> {
|
||||
}
|
||||
|
||||
///菜单
|
||||
menu() {
|
||||
menu(details) {
|
||||
setState(() {
|
||||
selected = true;
|
||||
});
|
||||
|
||||
showModalBottomSheet(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(10))),
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
builder: (ctx) {
|
||||
return Wrap(alignment: WrapAlignment.center, children: [
|
||||
menuCopyItem(localizations.copyUrl, () => widget.request.requestUrl),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
menuCopyItem(localizations.copyCurl, () => curlRequest(widget.request)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
TextButton(
|
||||
child: SizedBox(width: double.infinity, child: Text(localizations.repeat, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
onRepeat(widget.request);
|
||||
Navigator.maybePop(context);
|
||||
}),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
TextButton(
|
||||
child: SizedBox(
|
||||
width: double.infinity, child: Text(localizations.customRepeat, textAlign: TextAlign.center)),
|
||||
onPressed: () => showCustomRepeat(widget.request)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
TextButton(
|
||||
child:
|
||||
SizedBox(width: double.infinity, child: Text(localizations.editRequest, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.maybePop(context);
|
||||
var pageRoute = MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
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(
|
||||
child: SizedBox(width: double.infinity, child: Text(localizations.favorite, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
FavoriteStorage.addFavorite(widget.request);
|
||||
FlutterToastr.show(localizations.addSuccess, context);
|
||||
Navigator.maybePop(context);
|
||||
}),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
TextButton(
|
||||
child: SizedBox(width: double.infinity, child: Text(localizations.delete, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
widget.onRemove?.call(request);
|
||||
FlutterToastr.show(localizations.deleteSuccess, context);
|
||||
Navigator.maybePop(context);
|
||||
}),
|
||||
Container(color: Theme.of(context).hoverColor, height: 8),
|
||||
TextButton(
|
||||
child: Container(
|
||||
height: 45,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(localizations.cancel, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.maybePop(context);
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
).then((value) {
|
||||
var globalPosition = details.globalPosition;
|
||||
MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
var position = RelativeRect.fromLTRB(globalPosition.dx, globalPosition.dy, globalPosition.dx, globalPosition.dy);
|
||||
// Trigger haptic feedback
|
||||
HapticFeedback.mediumImpact();
|
||||
showMenu(
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: mediaQuery.size.width * 0.88),
|
||||
position: position,
|
||||
items: [
|
||||
//复制url
|
||||
PopupMenuContainer(
|
||||
child: Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 20, top: 5),
|
||||
child: Text(localizations.selectAction, style: Theme.of(context).textTheme.bodyLarge)),
|
||||
),
|
||||
//copy
|
||||
menuItem(
|
||||
left: button(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: request.requestUrl)).then((value) {
|
||||
if (mounted) {
|
||||
FlutterToastr.show(localizations.copied, context);
|
||||
Navigator.maybePop(context);
|
||||
}
|
||||
});
|
||||
},
|
||||
label: localizations.copyUrl,
|
||||
icon: Icons.link,
|
||||
iconSize: 22),
|
||||
right: button(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: curlRequest(request))).then((value) {
|
||||
if (mounted) {
|
||||
FlutterToastr.show(localizations.copied, context);
|
||||
Navigator.maybePop(context);
|
||||
}
|
||||
});
|
||||
},
|
||||
label: localizations.copyCurl,
|
||||
icon: Icons.code),
|
||||
),
|
||||
//repeat
|
||||
menuItem(
|
||||
left: button(
|
||||
onPressed: () {
|
||||
onRepeat(request);
|
||||
Navigator.maybePop(context);
|
||||
},
|
||||
label: localizations.repeat,
|
||||
icon: Icons.repeat_one),
|
||||
right: button(
|
||||
onPressed: () => showCustomRepeat(request), label: localizations.customRepeat, icon: Icons.repeat),
|
||||
),
|
||||
//favorite and edit
|
||||
menuItem(
|
||||
left: button(
|
||||
onPressed: () {
|
||||
FavoriteStorage.addFavorite(widget.request);
|
||||
FlutterToastr.show(localizations.addSuccess, context);
|
||||
Navigator.maybePop(context);
|
||||
},
|
||||
label: localizations.favorite,
|
||||
icon: Icons.favorite_outline),
|
||||
right: button(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
|
||||
var pageRoute = MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
MobileRequestEditor(request: widget.request, proxyServer: widget.proxyServer));
|
||||
if (mounted) {
|
||||
Navigator.push(context, pageRoute);
|
||||
} else {
|
||||
NavigatorHelper.push(pageRoute);
|
||||
}
|
||||
},
|
||||
label: localizations.editRequest,
|
||||
icon: Icons.edit_outlined),
|
||||
),
|
||||
|
||||
// menuItem(
|
||||
// left: button(
|
||||
// onPressed: () {}, label: localizations.script, icon: Icons.javascript_outlined, iconSize: 24),
|
||||
// right: button(onPressed: () {}, label: localizations.requestRewrite, icon: Icons.replay_outlined),
|
||||
// ),
|
||||
// menuItem(
|
||||
// left: TextButton.icon(
|
||||
// onPressed: () {},
|
||||
// label: Text(localizations.highlight),
|
||||
// icon: const Icon(Icons.highlight_outlined, size: 20)),
|
||||
// right: TextButton.icon(
|
||||
// onPressed: () {},
|
||||
// label: Text(localizations.requestRewrite),
|
||||
// icon: const Icon(Icons.highlight_remove, size: 20)),
|
||||
// ),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
button(
|
||||
onPressed: () {
|
||||
widget.onRemove?.call(request);
|
||||
FlutterToastr.show(localizations.deleteSuccess, context);
|
||||
Navigator.maybePop(context);
|
||||
},
|
||||
label: localizations.delete,
|
||||
icon: Icons.delete_outline),
|
||||
SizedBox(width: 15),
|
||||
]),
|
||||
],
|
||||
)),
|
||||
]).then((value) {
|
||||
selected = false;
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
@@ -228,7 +279,7 @@ class RequestRowState extends State<RequestRow> {
|
||||
|
||||
//显示高级重发
|
||||
showCustomRepeat(HttpRequest request) {
|
||||
Navigator.maybePop(context);
|
||||
Navigator.pop(context);
|
||||
var pageRoute = MaterialPageRoute(
|
||||
builder: (context) => futureWidget(SharedPreferences.getInstance(),
|
||||
(prefs) => MobileCustomRepeat(onRepeat: () => onRepeat(request), prefs: prefs)));
|
||||
@@ -249,16 +300,18 @@ class RequestRowState extends State<RequestRow> {
|
||||
}
|
||||
}
|
||||
|
||||
Widget menuCopyItem(String title, String Function() callback) {
|
||||
return TextButton(
|
||||
child: SizedBox(width: double.infinity, child: Text(title, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: callback.call())).then((value) {
|
||||
if (mounted) {
|
||||
FlutterToastr.show(localizations.copied, context);
|
||||
Navigator.maybePop(context);
|
||||
}
|
||||
});
|
||||
});
|
||||
Widget button({required String label, required IconData icon, required Function() onPressed, double iconSize = 20}) {
|
||||
var style = Theme.of(context).textTheme.bodyMedium;
|
||||
return TextButton.icon(
|
||||
onPressed: onPressed, label: Text(label, style: style), icon: Icon(icon, size: iconSize, color: style?.color));
|
||||
}
|
||||
|
||||
Widget menuItem({required Widget left, required Widget right}) {
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(width: 130, child: Align(alignment: Alignment.centerLeft, child: left)),
|
||||
Expanded(child: Align(alignment: Alignment.centerLeft, child: right))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ class _RewriteRuleState extends State<RewriteRule> {
|
||||
child: ListView(children: <Widget>[
|
||||
Row(children: [
|
||||
SizedBox(
|
||||
width: 58,
|
||||
width: 60,
|
||||
child: Text('${localizations.enable}:',
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500))),
|
||||
SwitchWidget(value: rule.enabled, onChanged: (val) => rule.enabled = val, scale: 0.8)
|
||||
@@ -504,7 +504,7 @@ class _RewriteRuleState extends State<RewriteRule> {
|
||||
required: true, keyboardType: TextInputType.url),
|
||||
Row(children: [
|
||||
SizedBox(
|
||||
width: 58,
|
||||
width: 60,
|
||||
child: Text('${localizations.action}:',
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500))),
|
||||
SizedBox(
|
||||
@@ -546,7 +546,7 @@ class _RewriteRuleState extends State<RewriteRule> {
|
||||
Widget textField(String label, TextEditingController controller, String hint,
|
||||
{bool required = false, TextInputType? keyboardType, FormFieldSetter<String>? onSaved}) {
|
||||
return Row(children: [
|
||||
SizedBox(width: 58, child: Text(label, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500))),
|
||||
SizedBox(width: 60, child: Text(label, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500))),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
@@ -556,7 +556,7 @@ class _RewriteRuleState extends State<RewriteRule> {
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500),
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
contentPadding: const EdgeInsets.only(),
|
||||
errorStyle: const TextStyle(height: 0, fontSize: 0),
|
||||
),
|
||||
))
|
||||
|
||||
@@ -347,9 +347,7 @@ class RewriteReplaceState extends State<MobileRewriteReplace> {
|
||||
Widget statusCodeEdit() {
|
||||
var rewriteItem = items.firstWhere((item) => item.type == RewriteType.replaceResponseStatus);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(children: [
|
||||
return Column(children: [
|
||||
Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
|
||||
Text(localizations.statusCode),
|
||||
const SizedBox(width: 10),
|
||||
@@ -379,7 +377,7 @@ class RewriteReplaceState extends State<MobileRewriteReplace> {
|
||||
])),
|
||||
const SizedBox(width: 10),
|
||||
])
|
||||
]));
|
||||
]);
|
||||
}
|
||||
|
||||
InputDecoration decoration(String label, {String? hintText}) {
|
||||
|
||||
Reference in New Issue
Block a user