mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-03-15 04:23:17 +08:00
Toolbox supports regular expressions (#264)
This commit is contained in:
@@ -296,6 +296,7 @@
|
||||
|
||||
"other": "Other",
|
||||
"certHashName": "CA Hash Name",
|
||||
"regExp": "RegExp",
|
||||
"systemCertName": "System Certificate Name",
|
||||
"qrCode": "QR Code",
|
||||
"scanQrCode": "Scan QR Code",
|
||||
|
||||
@@ -296,6 +296,7 @@
|
||||
"other": "其他",
|
||||
"certHashName": "证书Hash名称",
|
||||
"systemCertName": "系统证书名称",
|
||||
"regExp": "正则表达式",
|
||||
"qrCode": "二维码",
|
||||
"generateQrCode": "生成二维码",
|
||||
"scanQrCode": "扫描二维码",
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/util/cert/x509.dart';
|
||||
import 'package:network_proxy/ui/component/text_field.dart';
|
||||
|
||||
///证书哈希名称查看
|
||||
///@author Hongen Wang
|
||||
@@ -50,7 +51,7 @@ class _CertHashPageState extends State<CertHashPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(localizations.systemCertName, style: TextStyle(fontSize: 16)), centerTitle: true),
|
||||
appBar: AppBar(title: Text(localizations.systemCertName, style: TextStyle(fontSize: 16)), centerTitle: true),
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: ListView(children: [
|
||||
Wrap(alignment: WrapAlignment.end, children: [
|
||||
@@ -93,7 +94,7 @@ class _CertHashPageState extends State<CertHashPage> {
|
||||
controller: input,
|
||||
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: decoration(localizations.inputContent))),
|
||||
decoration: decoration(context, label: localizations.inputContent))),
|
||||
Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: TextButton(onPressed: () {}, child: const Text("Output:", style: TextStyle(fontSize: 16)))),
|
||||
@@ -105,7 +106,7 @@ class _CertHashPageState extends State<CertHashPage> {
|
||||
maxLines: 30,
|
||||
readOnly: true,
|
||||
controller: decodeData,
|
||||
decoration: decoration('Android ${localizations.systemCertName}'))),
|
||||
decoration: decoration(context, label: 'Android ${localizations.systemCertName}'))),
|
||||
]));
|
||||
}
|
||||
|
||||
@@ -124,25 +125,9 @@ class _CertHashPageState extends State<CertHashPage> {
|
||||
}
|
||||
}
|
||||
|
||||
ButtonStyle get buttonStyle =>
|
||||
ButtonStyle(
|
||||
padding: WidgetStateProperty.all<EdgeInsets>(EdgeInsets.symmetric(horizontal: 15, vertical: 8)),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(TextStyle(fontSize: 14)),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))));
|
||||
|
||||
InputDecoration decoration(String label, {String? hintText}) {
|
||||
Color color = Theme
|
||||
.of(context)
|
||||
.colorScheme
|
||||
.primary;
|
||||
return InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: label,
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500),
|
||||
border: OutlineInputBorder(borderSide: BorderSide(width: 0.8, color: color)),
|
||||
enabledBorder: OutlineInputBorder(borderSide: BorderSide(width: 1.5, color: color)),
|
||||
focusedBorder: OutlineInputBorder(borderSide: BorderSide(width: 2, color: color)));
|
||||
}
|
||||
ButtonStyle get buttonStyle => ButtonStyle(
|
||||
padding: WidgetStateProperty.all<EdgeInsets>(EdgeInsets.symmetric(horizontal: 15, vertical: 8)),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(TextStyle(fontSize: 14)),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))));
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import 'package:network_proxy/ui/component/device.dart';
|
||||
import 'package:network_proxy/ui/component/encoder.dart';
|
||||
import 'package:network_proxy/ui/component/js_run.dart';
|
||||
import 'package:network_proxy/ui/component/qr_code_page.dart';
|
||||
import 'package:network_proxy/ui/component/regexp.dart';
|
||||
import 'package:network_proxy/ui/component/utils.dart';
|
||||
import 'package:network_proxy/ui/content/body.dart';
|
||||
import 'package:network_proxy/ui/desktop/request/request_editor.dart';
|
||||
@@ -88,15 +89,17 @@ Widget multiWindow(int windowId, Map<dynamic, dynamic> argument) {
|
||||
return CertHashPage();
|
||||
}
|
||||
|
||||
//脚本日志
|
||||
if (argument['name'] == 'ScriptConsoleWidget') {
|
||||
return ScriptConsoleWidget(windowId: windowId);
|
||||
}
|
||||
|
||||
if (argument['name'] == 'JavaScript') {
|
||||
return const JavaScript();
|
||||
}
|
||||
|
||||
if (argument['name'] == 'RegExpPage') {
|
||||
return const RegExpPage();
|
||||
}
|
||||
//脚本日志
|
||||
if (argument['name'] == 'ScriptConsoleWidget') {
|
||||
return ScriptConsoleWidget(windowId: windowId);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
@@ -284,24 +287,6 @@ encodeWindow(EncoderType type, BuildContext context, [String? text]) async {
|
||||
..show();
|
||||
}
|
||||
|
||||
///打开脚本窗口
|
||||
openScriptWindow() async {
|
||||
var ratio = 1.0;
|
||||
if (Platform.isWindows) {
|
||||
ratio = WindowManager.instance.getDevicePixelRatio();
|
||||
}
|
||||
registerMethodHandler();
|
||||
final window = await DesktopMultiWindow.createWindow(jsonEncode(
|
||||
{'name': 'ScriptWidget'},
|
||||
));
|
||||
|
||||
window.setTitle('Script');
|
||||
window
|
||||
..setFrame(const Offset(30, 0) & Size(800 * ratio, 700 * ratio))
|
||||
..center()
|
||||
..show();
|
||||
}
|
||||
|
||||
openScriptConsoleWindow() async {
|
||||
var ratio = 1.0;
|
||||
if (Platform.isWindows) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import 'package:flutter_qr_reader/flutter_qr_reader.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:image_pickers/image_pickers.dart';
|
||||
import 'package:network_proxy/ui/component/qrcode/qr_scan_view.dart';
|
||||
import 'package:network_proxy/ui/component/text_field.dart';
|
||||
import 'package:network_proxy/utils/platform.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
@@ -92,18 +93,6 @@ class _QrCodePageState extends State<QrCodePage> with SingleTickerProviderStateM
|
||||
}
|
||||
}
|
||||
|
||||
InputDecoration _decoration(BuildContext context, String label, {String? hintText}) {
|
||||
Color color = Theme.of(context).colorScheme.primary;
|
||||
return InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: label,
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500),
|
||||
border: OutlineInputBorder(borderSide: BorderSide(width: 0.8, color: color)),
|
||||
enabledBorder: OutlineInputBorder(borderSide: BorderSide(width: 1.3, color: color)),
|
||||
focusedBorder: OutlineInputBorder(borderSide: BorderSide(width: 2, color: color)));
|
||||
}
|
||||
|
||||
class _QrDecode extends StatefulWidget {
|
||||
final int? windowId;
|
||||
|
||||
@@ -188,7 +177,7 @@ class _QrDecodeState extends State<_QrDecode> with AutomaticKeepAliveClientMixin
|
||||
maxLines: 7,
|
||||
minLines: 7,
|
||||
readOnly: true,
|
||||
decoration: _decoration(context, localizations.encodeResult),
|
||||
decoration: decoration(context, label: localizations.encodeResult),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
TextButton.icon(
|
||||
@@ -261,7 +250,7 @@ class _QrEncodeState extends State<_QrEncode> with AutomaticKeepAliveClientMixin
|
||||
controller: inputData,
|
||||
maxLines: 8,
|
||||
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: _decoration(context, localizations.inputContent))),
|
||||
decoration: decoration(context, label: localizations.inputContent))),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
||||
232
lib/ui/component/regexp.dart
Normal file
232
lib/ui/component/regexp.dart
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2024 Hongen Wang All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/ui/component/text_field.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
///正则表达式工具
|
||||
///@author Hongen Wang
|
||||
class RegExpPage extends StatefulWidget {
|
||||
const RegExpPage({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _RegExpPageState();
|
||||
}
|
||||
}
|
||||
|
||||
class _RegExpPageState extends State<RegExpPage> {
|
||||
var pattern = TextEditingController();
|
||||
var input = HighlightTextEditingController();
|
||||
var replaceText = TextEditingController();
|
||||
String? resultInput;
|
||||
|
||||
AppLocalizations get localizations => AppLocalizations.of(context)!;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
pattern.addListener(onInputChangeMatch);
|
||||
input.addListener(onInputChangeMatch);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
pattern.dispose();
|
||||
input.dispose();
|
||||
replaceText.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
ButtonStyle get buttonStyle => ButtonStyle(
|
||||
padding: WidgetStateProperty.all<EdgeInsets>(EdgeInsets.symmetric(horizontal: 15, vertical: 8)),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(TextStyle(fontSize: 14)),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color primaryColor = Theme.of(context).colorScheme.primary;
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: Size.fromHeight(40),
|
||||
child: AppBar(
|
||||
title: Text(localizations.regExp, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
|
||||
centerTitle: true)),
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: ListView(padding: const EdgeInsets.all(10), children: [
|
||||
TextField(
|
||||
controller: pattern,
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: decoration(context,
|
||||
label: 'Pattern',
|
||||
hintText: 'Enter a regular expression',
|
||||
suffixIcon: IconButton(icon: Icon(Icons.clear), onPressed: () => pattern.clear())),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => pattern.text += r'\d+', // Only digits
|
||||
child: const Text('Digits'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => pattern.text += r'[a-zA-Z]+', // Only letters
|
||||
child: const Text('Letters'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => pattern.text += r'[a-zA-Z0-9]+', // Alphanumeric
|
||||
child: const Text('Alphanumeric'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => pattern.text += r'\w+@\w+\.\w+', // Email
|
||||
child: const Text('Email'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => pattern.text += r'(https?|ftp)://[^\s/$.?#].[^\s]*', // URL
|
||||
child: const Text('URL'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => pattern.text += r'\d{4}-\d{2}-\d{2}', // Date (YYYY-MM-DD)
|
||||
child: const Text('Date (YYYY-MM-DD)'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(children: [
|
||||
Align(alignment: Alignment.centerLeft, child: Text(localizations.testData)),
|
||||
const SizedBox(width: 10),
|
||||
if (!isMatch) Text(localizations.noChangesDetected, style: TextStyle(color: Colors.red))
|
||||
]),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: input,
|
||||
minLines: 5,
|
||||
maxLines: 8,
|
||||
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: decoration(context, hintText: localizations.enterMatchData),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
//输入替换文本
|
||||
Wrap(spacing: 10, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||
SizedBox(
|
||||
width: 355,
|
||||
child: TextField(
|
||||
controller: replaceText,
|
||||
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: decoration(context, label: 'Replace Text', hintText: 'Enter replacement text'),
|
||||
)),
|
||||
FilledButton.icon(
|
||||
onPressed: () {
|
||||
if (pattern.text.isEmpty) return;
|
||||
setState(() {
|
||||
resultInput = input.text;
|
||||
});
|
||||
},
|
||||
style: buttonStyle,
|
||||
icon: const Icon(Icons.play_arrow_rounded),
|
||||
label: const Text('Run')),
|
||||
const SizedBox(width: 20),
|
||||
]),
|
||||
SizedBox(height: 10),
|
||||
|
||||
if (resultInput != null)
|
||||
Row(children: [
|
||||
Text("Result", style: TextStyle(fontSize: 16, color: primaryColor, fontWeight: FontWeight.w500)),
|
||||
const SizedBox(width: 15),
|
||||
//copy
|
||||
IconButton(
|
||||
icon: Icon(Icons.copy, color: primaryColor, size: 18),
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: resultInput!));
|
||||
FlutterToastr.show(localizations.copied, context, duration: 3);
|
||||
}),
|
||||
]),
|
||||
if (resultInput != null) SizedBox(height: 5),
|
||||
if (resultInput != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(border: Border.all(color: Theme.of(context).colorScheme.primary, width: 1.2)),
|
||||
child: SelectableText.rich(
|
||||
showCursor: true,
|
||||
TextSpan(
|
||||
children: _buildHighlightedText(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
List<InlineSpan> _buildHighlightedText() {
|
||||
if (resultInput == null) return [];
|
||||
|
||||
final spans = <InlineSpan>[];
|
||||
int start = 0;
|
||||
|
||||
var text = resultInput!;
|
||||
var regex = RegExp(pattern.text);
|
||||
var replaceText = this.replaceText.text;
|
||||
var matches = regex.allMatches(text);
|
||||
|
||||
for (var match in matches) {
|
||||
if (start < match.start) {
|
||||
spans.add(TextSpan(text: text.substring(start, match.start)));
|
||||
}
|
||||
spans.add(TextSpan(text: replaceText, style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold)));
|
||||
start = match.end;
|
||||
}
|
||||
|
||||
if (start < text.length) {
|
||||
spans.add(TextSpan(text: text.substring(start)));
|
||||
}
|
||||
return spans;
|
||||
}
|
||||
|
||||
bool onMatch = false; //是否正在匹配
|
||||
bool isMatch = true; //是否匹配成功
|
||||
|
||||
onInputChangeMatch() {
|
||||
if (onMatch || input.highlightEnabled == false) {
|
||||
return;
|
||||
}
|
||||
onMatch = true;
|
||||
|
||||
//高亮显示
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
onMatch = false;
|
||||
if (pattern.text.isEmpty) {
|
||||
if (isMatch) return;
|
||||
setState(() {
|
||||
isMatch = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
var match = input.highlight(pattern.text);
|
||||
isMatch = match;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -60,3 +60,16 @@ class HighlightTextEditingController extends TextEditingController {
|
||||
return TextSpan(children: spans, style: style);
|
||||
}
|
||||
}
|
||||
|
||||
InputDecoration decoration(BuildContext context, {String? label, String? hintText, Widget? suffixIcon}) {
|
||||
Color color = Theme.of(context).colorScheme.primary;
|
||||
return InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: label,
|
||||
hintText: hintText,
|
||||
suffixIcon: suffixIcon,
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500),
|
||||
border: OutlineInputBorder(borderSide: BorderSide(width: 0.8, color: color)),
|
||||
enabledBorder: OutlineInputBorder(borderSide: BorderSide(width: 1.3, color: color)),
|
||||
focusedBorder: OutlineInputBorder(borderSide: BorderSide(width: 2, color: color)));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:network_proxy/ui/component/encoder.dart';
|
||||
import 'package:network_proxy/ui/component/js_run.dart';
|
||||
import 'package:network_proxy/ui/component/multi_window.dart';
|
||||
import 'package:network_proxy/ui/component/qr_code_page.dart';
|
||||
import 'package:network_proxy/ui/component/regexp.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/request_editor.dart';
|
||||
import 'package:network_proxy/utils/platform.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
@@ -123,6 +124,17 @@ class _ToolboxState extends State<Toolbox> {
|
||||
icon: Icons.key,
|
||||
text: localizations.certHashName),
|
||||
const SizedBox(width: 10),
|
||||
IconText(
|
||||
onTap: () async {
|
||||
if (Platforms.isMobile()) {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const RegExpPage()));
|
||||
return;
|
||||
}
|
||||
MultiWindow.openWindow(localizations.regExp, 'RegExpPage', size: const Size(800, 720));
|
||||
},
|
||||
icon: Icons.code,
|
||||
text: localizations.regExp),
|
||||
const SizedBox(width: 10),
|
||||
IconText(
|
||||
onTap: () async {
|
||||
if (Platforms.isMobile()) {
|
||||
|
||||
@@ -71,7 +71,8 @@ class _SettingState extends State<Setting> {
|
||||
item(localizations.domainFilter, onPressed: hostFilter),
|
||||
item(localizations.requestRewrite, onPressed: requestRewrite),
|
||||
item(localizations.requestBlock, onPressed: showRequestBlock),
|
||||
item(localizations.script, onPressed: () => openScriptWindow()),
|
||||
item(localizations.script,
|
||||
onPressed: () => MultiWindow.openWindow(localizations.script, 'ScriptWidget', size: const Size(800, 700))),
|
||||
item(localizations.externalProxy, onPressed: setExternalProxy),
|
||||
item("Github", onPressed: () => launchUrl(Uri.parse("https://github.com/wanghongenpin/network_proxy_flutter"))),
|
||||
],
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:network_proxy/network/components/host_filter.dart';
|
||||
import 'package:network_proxy/network/host_port.dart';
|
||||
import 'package:network_proxy/network/http/http.dart';
|
||||
import 'package:network_proxy/network/http_client.dart';
|
||||
import 'package:network_proxy/ui/component/widgets.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/request_sequence.dart';
|
||||
import 'package:network_proxy/utils/listenable_list.dart';
|
||||
|
||||
@@ -226,6 +227,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
///菜单
|
||||
menu(int index) {
|
||||
var hostAndPort = view.elementAt(index);
|
||||
|
||||
showModalBottomSheet(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(10))),
|
||||
context: context,
|
||||
@@ -234,57 +236,45 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
return Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
child: SizedBox(
|
||||
width: double.infinity, child: Text(localizations.copyHost, textAlign: TextAlign.center)),
|
||||
BottomSheetItem(
|
||||
text: localizations.copyHost,
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: hostAndPort.host));
|
||||
FlutterToastr.show(localizations.copied, context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
TextButton(
|
||||
child: SizedBox(
|
||||
width: double.infinity, child: Text(localizations.addBlacklist, textAlign: TextAlign.center)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
BottomSheetItem(
|
||||
text: localizations.addBlacklist,
|
||||
onPressed: () {
|
||||
HostFilter.blacklist.add(hostAndPort.host);
|
||||
configuration.flushConfig();
|
||||
FlutterToastr.show(localizations.addSuccess, context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
TextButton(
|
||||
child: SizedBox(
|
||||
width: double.infinity, child: Text(localizations.addWhitelist, textAlign: TextAlign.center)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
BottomSheetItem(
|
||||
text: localizations.addWhitelist,
|
||||
onPressed: () {
|
||||
HostFilter.whitelist.add(hostAndPort.host);
|
||||
configuration.flushConfig();
|
||||
FlutterToastr.show(localizations.addSuccess, context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
TextButton(
|
||||
child: SizedBox(
|
||||
width: double.infinity, child: Text(localizations.deleteWhitelist, textAlign: TextAlign.center)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
BottomSheetItem(
|
||||
text: localizations.deleteWhitelist,
|
||||
onPressed: () {
|
||||
HostFilter.whitelist.remove(hostAndPort.host);
|
||||
configuration.flushConfig();
|
||||
FlutterToastr.show(localizations.deleteSuccess, context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
TextButton(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text(localizations.repeatDomainRequests, textAlign: TextAlign.center)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
BottomSheetItem(
|
||||
text: localizations.repeatDomainRequests,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
repeatDomainRequests(hostAndPort);
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
TextButton(
|
||||
child:
|
||||
SizedBox(width: double.infinity, child: Text(localizations.delete, textAlign: TextAlign.center)),
|
||||
const Divider(thickness: 0.5, height: 5),
|
||||
BottomSheetItem(
|
||||
text: localizations.delete,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var requests = containerMap.remove(hostAndPort);
|
||||
@@ -294,7 +284,6 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
widget.onRemove?.call(requests);
|
||||
}
|
||||
FlutterToastr.show(localizations.deleteSuccess, context);
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}),
|
||||
Container(
|
||||
@@ -303,12 +292,12 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
),
|
||||
TextButton(
|
||||
child: Container(
|
||||
height: 50,
|
||||
height: 45,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(localizations.cancel, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(ctx).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -339,7 +339,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
widget.requestRewrites.flushRequestRewriteConfig();
|
||||
if (mounted) FlutterToastr.show(localizations.deleteSuccess, context);
|
||||
}),
|
||||
Container(color: Theme.of(context).hoverColor, height: 8),
|
||||
Container(color: Theme.of(ctx).hoverColor, height: 8),
|
||||
TextButton(
|
||||
child: Container(
|
||||
height: 45,
|
||||
@@ -347,7 +347,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(localizations.cancel, textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(ctx).pop();
|
||||
}),
|
||||
]);
|
||||
}).then((value) {
|
||||
|
||||
@@ -679,6 +679,9 @@ class _ScriptListState extends State<ScriptList> {
|
||||
],
|
||||
);
|
||||
}).then((value) {
|
||||
if (multiple) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
selected.remove(index);
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ dependencies:
|
||||
qr_flutter: ^4.1.0
|
||||
flutter_qr_reader: ^1.0.5
|
||||
flutter_toastr: ^1.0.3
|
||||
share_plus: ^10.1.0
|
||||
share_plus: ^10.1.1
|
||||
brotli: ^0.6.0
|
||||
flutter_js: ^0.8.1
|
||||
flutter_code_editor: ^0.3.2
|
||||
|
||||
Reference in New Issue
Block a user