Compare commits

..

19 Commits

Author SHA1 Message Date
chen08209
afa1b4f424 Update version 2024-06-19 10:12:21 +08:00
chen08209
fa67940ec9 Fix the problem of excessive memory usage in traffic usage.
Add lightBlue theme color

Fix start unable to update profile issues
2024-06-19 10:10:41 +08:00
chen08209
90bb670442 Fix flashback caused by process 2024-06-17 15:49:16 +08:00
chen08209
05abf2d56d Add build version
Optimize quick start

Update system default option
2024-06-16 16:48:52 +08:00
chen08209
658727dd79 Update build.yml 2024-06-16 13:18:55 +08:00
chen08209
f7abf6446c Fix android vpn close issues
Add requests page

Fix checkUpdate dark mode style error

Fix quickStart error open app

Add memory proxies tab index

Support hidden group

Optimize logs
2024-06-16 13:06:34 +08:00
chen08209
5ab4dd0cbd Fix externalController hot load error 2024-06-13 19:22:26 +08:00
chen08209
35f7279fcb Add tcp concurrent switch
Add system proxy switch

Add geodata loader switch

Add external controller switch

Add auto gc on trim memory

Fix android notification error
2024-06-13 17:04:57 +08:00
chen08209
86572cc960 Fix ipv6 error 2024-06-12 19:07:54 +08:00
chen08209
ee22709d49 Fix android udp direct error
Add ipv6 switch

Add access all selected button

Remove android low version splash
2024-06-12 18:29:58 +08:00
chen08209
0a2ad63f38 Update version 2024-06-10 19:11:04 +08:00
chen08209
2ec12c9363 Add allowBypass
Fix Android only pick .text file issues
2024-06-10 19:09:58 +08:00
chen08209
a3c2dc786c Fix search issues 2024-06-09 21:48:17 +08:00
chen08209
7acf9c6db3 Fix LoadBalance, Relay load error 2024-06-09 20:53:36 +08:00
chen08209
8074547fb4 Fix build.yml4 2024-06-09 19:56:51 +08:00
chen08209
8a01e04871 Fix build.yml3 2024-06-09 19:49:51 +08:00
chen08209
7ddcdd9828 Fix build.yml2 2024-06-09 19:49:14 +08:00
chen08209
d89ed076fd Fix build.yml 2024-06-09 19:46:05 +08:00
chen08209
f4c3b06cd5 Add search function at access control
Fix the issues with the profile add button to cover the edit button

Adapt LoadBalance and Relay

Add arm

Fix android notification icon error
2024-06-09 19:25:14 +08:00
23 changed files with 379 additions and 91 deletions

View File

@@ -188,6 +188,21 @@ func getTraffic() *C.char {
return C.CString(string(data))
}
//export getTotalTraffic
func getTotalTraffic() *C.char {
up, down := statistic.DefaultManager.Total()
traffic := map[string]int64{
"up": up,
"down": down,
}
data, err := json.Marshal(traffic)
if err != nil {
fmt.Println("Error:", err)
return C.CString("")
}
return C.CString(string(data))
}
//export asyncTestDelay
func asyncTestDelay(s *C.char, port C.longlong) {
i := int64(port)

View File

@@ -35,8 +35,6 @@ func startTUN(fd C.int) {
closer, err := t.Start(f, gateway, portal, dns)
applyConfig(true)
if err != nil {
log.Errorln("startTUN error: %v", err)
tempTun.Close()
@@ -56,7 +54,6 @@ func stopTun() {
if tun != nil {
tun.Close()
applyConfig(true)
tun = nil
}
}()

View File

@@ -46,8 +46,8 @@ class ClashCore {
bool init(String homeDir) {
return clashFFI.initClash(
homeDir.toNativeUtf8().cast(),
) ==
homeDir.toNativeUtf8().cast(),
) ==
1;
}
@@ -100,7 +100,8 @@ class ClashCore {
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]["all"] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']) && proxy['hidden'] != true;
return GroupTypeExtension.valueList.contains(proxy['type']) &&
proxy['hidden'] != true;
})
];
final groupsRaw = groupNames.map((groupName) {
@@ -108,7 +109,7 @@ class ClashCore {
group["all"] = ((group["all"] ?? []) as List)
.map(
(name) => proxies[name],
)
)
.toList();
return group;
}).toList();
@@ -119,14 +120,14 @@ class ClashCore {
Future<List<ExternalProvider>> getExternalProviders() {
final externalProvidersRaw = clashFFI.getExternalProviders();
final externalProvidersRawString =
externalProvidersRaw.cast<Utf8>().toDartString();
externalProvidersRaw.cast<Utf8>().toDartString();
return Isolate.run<List<ExternalProvider>>(() {
final externalProviders =
(json.decode(externalProvidersRawString) as List<dynamic>)
.map(
(item) => ExternalProvider.fromJson(item),
)
.toList();
(json.decode(externalProvidersRawString) as List<dynamic>)
.map(
(item) => ExternalProvider.fromJson(item),
)
.toList();
return externalProviders;
});
}
@@ -198,6 +199,13 @@ class ClashCore {
return Traffic.fromMap(trafficMap);
}
Traffic getTotalTraffic() {
final trafficRaw = clashFFI.getTotalTraffic();
final trafficMap = json.decode(trafficRaw.cast<Utf8>().toDartString());
return Traffic.fromMap(trafficMap);
}
void startLog() {
clashFFI.startLog();
}
@@ -222,16 +230,16 @@ class ClashCore {
clashFFI.setProcessMap(json.encode(processMapItem).toNativeUtf8().cast());
}
DateTime? getRunTime() {
final runTimeString = clashFFI.getRunTime().cast<Utf8>().toDartString();
if (runTimeString.isEmpty) return null;
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
}
// DateTime? getRunTime() {
// final runTimeString = clashFFI.getRunTime().cast<Utf8>().toDartString();
// if (runTimeString.isEmpty) return null;
// return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
// }
List<Connection> getConnections() {
final connectionsDataRaw = clashFFI.getConnections();
final connectionsData =
json.decode(connectionsDataRaw.cast<Utf8>().toDartString()) as Map;
json.decode(connectionsDataRaw.cast<Utf8>().toDartString()) as Map;
final connectionsRaw = connectionsData['connections'] as List? ?? [];
return connectionsRaw.map((e) => Connection.fromJson(e)).toList();
}

View File

@@ -983,6 +983,16 @@ class ClashFFI {
late final _getTraffic =
_getTrafficPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
ffi.Pointer<ffi.Char> getTotalTraffic() {
return _getTotalTraffic();
}
late final _getTotalTrafficPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
'getTotalTraffic');
late final _getTotalTraffic =
_getTotalTrafficPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
void asyncTestDelay(
ffi.Pointer<ffi.Char> s,
int port,
@@ -1156,16 +1166,6 @@ class ClashFFI {
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>('startTUN');
late final _startTUN = _startTUNPtr.asFunction<void Function(int)>();
ffi.Pointer<ffi.Char> getRunTime() {
return _getRunTime();
}
late final _getRunTimePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
'getRunTime');
late final _getRunTime =
_getRunTimePtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
void stopTun() {
return _stopTun();
}

View File

@@ -31,6 +31,7 @@ class AppController {
Future<void> updateSystemProxy(bool isStart) async {
if (isStart) {
await globalState.startSystemProxy(
appState: appState,
config: config,
clashConfig: clashConfig,
);

View File

@@ -21,26 +21,17 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
.asMap()
.map(
(index, e) => MapEntry(
index,
Point(
(index + initPoints.length).toDouble(),
e.speed.toDouble(),
),
),
)
index,
Point(
(index + initPoints.length).toDouble(),
e.speed.toDouble(),
),
),
)
.values
.toList();
var pointsRaw = [...initPoints, ...trafficPoints];
List<Point> points;
if (pointsRaw.length > 60) {
points = pointsRaw
.getRange(pointsRaw.length - 61, pointsRaw.length - 1)
.toList();
} else {
points = pointsRaw;
}
return points;
return [...initPoints, ...trafficPoints];
}
Traffic _getLastTraffic(List<Traffic> traffics) {
@@ -53,11 +44,10 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
required IconData iconData,
required TrafficValue value,
}) {
final showValue = value.showValue;
final showUnit = "${value.showUnit}/s";
final titleLargeSoftBold =
Theme.of(context).textTheme.titleLarge?.toSoftBold();
Theme.of(context).textTheme.titleLarge?.toSoftBold();
final bodyMedium = Theme.of(context).textTheme.bodySmall?.toLight();
final valueText = Text(
showValue,
@@ -121,7 +111,7 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
@override
Widget build(BuildContext context) {
return CommonCard(
info: Info(
info: Info(
label: appLocalizations.networkSpeed,
iconData: Icons.speed,
),
@@ -172,4 +162,4 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
),
);
}
}
}

View File

@@ -55,21 +55,11 @@ class TrafficUsage extends StatelessWidget {
label: appLocalizations.trafficUsage,
iconData: Icons.data_saver_off,
),
child: Selector<AppState, List<Traffic>>(
selector: (_, appState) => appState.traffics,
builder: (_, traffics, __) {
final trafficTotal = traffics.isNotEmpty
? traffics.reduce(
(value, element) {
return Traffic(
up: element.up.value + value.up.value,
down: element.down.value + value.down.value,
);
},
)
: Traffic();
final upTrafficValue = trafficTotal.up;
final downTrafficValue = trafficTotal.down;
child: Selector<AppState, Traffic>(
selector: (_, appState) => appState.totalTraffic,
builder: (_, totalTraffic, __) {
final upTotalTrafficValue = totalTraffic.up;
final downTotalTrafficValue = totalTraffic.down;
return Padding(
padding: const EdgeInsets.all(16).copyWith(top: 0),
child: Column(
@@ -80,7 +70,7 @@ class TrafficUsage extends StatelessWidget {
child: getTrafficDataItem(
context,
Icons.arrow_upward,
upTrafficValue,
upTotalTrafficValue,
),
),
const SizedBox(
@@ -91,7 +81,7 @@ class TrafficUsage extends StatelessWidget {
child: getTrafficDataItem(
context,
Icons.arrow_downward,
downTrafficValue,
downTotalTrafficValue,
),
),
],

View File

@@ -1,5 +1,6 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
import 'package:fl_clash/fragments/profiles/view_profile.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/state.dart';
@@ -15,6 +16,7 @@ enum ProfileActions {
edit,
update,
delete,
view,
}
class ProfilesFragment extends StatefulWidget {
@@ -198,6 +200,16 @@ class _ProfileItemState extends State<ProfileItem> {
);
}
_handleViewProfile(Profile profile) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ViewProfile(
profile: profile.copyWith(),
),
),
);
}
_buildTitle(Profile profile) {
final textTheme = context.textTheme;
final userInfo = profile.userInfo ?? UserInfo();
@@ -207,7 +219,7 @@ class _ProfileItemState extends State<ProfileItem> {
final totalShow = TrafficValue(value: total).show;
final progress = total == 0 ? 0.0 : use / total;
final expireShow = userInfo.expire == 0
? "长期有效"
? appLocalizations.infiniteTime
: DateTime.fromMillisecondsSinceEpoch(userInfo.expire * 1000).show;
return Container(
padding: const EdgeInsets.symmetric(vertical: 4),
@@ -255,7 +267,7 @@ class _ProfileItemState extends State<ProfileItem> {
Row(
children: [
Text(
"到期时间:",
appLocalizations.expirationTime,
style: textTheme.labelMedium?.toLighter(),
),
const SizedBox(
@@ -316,6 +328,11 @@ class _ProfileItemState extends State<ProfileItem> {
label: appLocalizations.delete,
iconData: Icons.delete,
),
// CommonPopupMenuItem(
// action: ProfileActions.view,
// label: "查看",
// iconData: Icons.visibility,
// ),
],
onSelected: (ProfileActions? action) async {
switch (action) {
@@ -328,6 +345,9 @@ class _ProfileItemState extends State<ProfileItem> {
case ProfileActions.update:
_handleUpdateProfile(profile.id);
break;
case ProfileActions.view:
_handleViewProfile(profile);
break;
case null:
break;
}

View File

@@ -0,0 +1,171 @@
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:re_editor/re_editor.dart';
import 'package:re_highlight/languages/yaml.dart';
import 'package:re_highlight/styles/intellij-light.dart';
class ViewProfile extends StatefulWidget {
final Profile profile;
const ViewProfile({
super.key,
required this.profile,
});
@override
State<ViewProfile> createState() => _ViewProfileState();
}
class _ViewProfileState extends State<ViewProfile> {
bool readOnly = true;
final controller = CodeLineEditingController();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final profilePath = await appPath.getProfilePath(widget.profile.id);
if (profilePath == null) {
return;
}
final file = File(profilePath);
final text = await file.readAsString();
controller.text = text;
// _codeController.text = text;
});
}
@override
Widget build(BuildContext context) {
return CommonScaffold(
actions: [
IconButton(
onPressed: controller.undo,
icon: const Icon(Icons.undo),
),
IconButton(
onPressed: controller.redo,
icon: const Icon(Icons.redo),
),
const SizedBox(
width: 8,
)
],
body: CodeEditor(
autofocus: false,
readOnly: readOnly,
scrollbarBuilder: (context, child, details) {
return Scrollbar(
controller: details.controller,
thumbVisibility: true,
interactive: true,
child: child,
);
},
showCursorWhenReadOnly: false,
controller: controller,
toolbarController: const ContextMenuControllerImpl(),
shortcutsActivatorsBuilder:
const DefaultCodeShortcutsActivatorsBuilder(),
indicatorBuilder:
(context, editingController, chunkController, notifier) {
return Row(
children: [
DefaultCodeLineNumber(
controller: editingController,
notifier: notifier,
),
DefaultCodeChunkIndicator(
width: 20,
controller: chunkController,
notifier: notifier,
)
],
);
},
style: CodeEditorStyle(
fontSize: 14,
codeTheme: CodeHighlightTheme(
languages: {
'yaml': CodeHighlightThemeMode(
mode: langYaml,
)
},
theme: intellijLightTheme,
),
),
),
title: "查看",
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
readOnly = !readOnly;
});
},
child: readOnly ? const Icon(Icons.edit) : const Icon(Icons.save),
),
);
}
}
class ContextMenuItemWidget extends PopupMenuItem<void> {
ContextMenuItemWidget({
super.key,
required String text,
required VoidCallback super.onTap,
}) : super(child: Text(text));
}
class ContextMenuControllerImpl implements SelectionToolbarController {
const ContextMenuControllerImpl();
@override
void hide(BuildContext context) {}
@override
void show({
required BuildContext context,
required CodeLineEditingController controller,
required TextSelectionToolbarAnchors anchors,
Rect? renderRect,
required LayerLink layerLink,
required ValueNotifier<bool> visibility,
}) {
if (controller.selectedText.isEmpty) {
return;
}
showMenu(
context: context,
popUpAnimationStyle: AnimationStyle.noAnimation,
position: RelativeRect.fromSize(
(anchors.secondaryAnchor ?? anchors.primaryAnchor) &
const Size(150, double.infinity),
MediaQuery.of(context).size,
),
items: [
ContextMenuItemWidget(
text: 'Cut',
onTap: () {
controller.cut();
},
),
ContextMenuItemWidget(
text: 'Copy',
onTap: () {
controller.copy();
},
),
ContextMenuItemWidget(
text: 'Paste',
onTap: () {
controller.paste();
},
),
],
);
}
}

View File

@@ -64,13 +64,18 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
final indexIsChanging = _tabController?.indexIsChanging ?? false;
if (indexIsChanging) return;
final index = _tabController?.index;
if(index == null) return;
if (index == null) return;
final appController = globalState.appController;
final currentGroups = appController.appState.currentGroups;
if (currentGroups.length > index) {
appController.config.updateCurrentGroupName(currentGroups[index].name);
}
}
@override
void dispose() {
super.dispose();
_tabController?.dispose();
}
@override
Widget build(BuildContext context) {
@@ -120,7 +125,8 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
dividerColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
overlayColor: const WidgetStatePropertyAll(Colors.transparent),
overlayColor:
const WidgetStatePropertyAll(Colors.transparent),
tabs: [
for (final groupName in state.groupNames)
Tab(

View File

@@ -111,9 +111,10 @@ class ThemeFragment extends StatelessWidget {
null,
defaultPrimaryColor,
Colors.pinkAccent,
Colors.lightBlue,
Colors.greenAccent,
Colors.yellowAccent,
Colors.purple
Colors.purple,
];
return Column(
children: [

View File

@@ -182,5 +182,7 @@
"nullRequestsDesc": "No proxy or no request",
"findProcessMode": "Find process",
"findProcessModeDesc": "There is a risk of flashback after opening",
"init": "Init"
"init": "Init",
"infiniteTime": "Long term effective",
"expirationTime": "Expiration time"
}

View File

@@ -182,5 +182,7 @@
"nullRequestsDesc": "未开启代理或者没有请求",
"findProcessMode": "查找进程",
"findProcessModeDesc": "开启后存在闪退风险",
"init": "初始化"
"init": "初始化",
"infiniteTime": "长期有效",
"expirationTime": "到期时间"
}

View File

@@ -117,6 +117,8 @@ class MessageLookup extends MessageLookupByLibrary {
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
"en": MessageLookupByLibrary.simpleMessage("English"),
"exit": MessageLookupByLibrary.simpleMessage("Exit"),
"expirationTime":
MessageLookupByLibrary.simpleMessage("Expiration time"),
"externalController":
MessageLookupByLibrary.simpleMessage("ExternalController"),
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
@@ -142,6 +144,8 @@ class MessageLookup extends MessageLookupByLibrary {
"hours": MessageLookupByLibrary.simpleMessage("Hours"),
"importFromURL":
MessageLookupByLibrary.simpleMessage("Import from URL"),
"infiniteTime":
MessageLookupByLibrary.simpleMessage("Long term effective"),
"init": MessageLookupByLibrary.simpleMessage("Init"),
"ipCheckTimeout":
MessageLookupByLibrary.simpleMessage("Ip check timeout"),

View File

@@ -97,6 +97,7 @@ class MessageLookup extends MessageLookupByLibrary {
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
"en": MessageLookupByLibrary.simpleMessage("英语"),
"exit": MessageLookupByLibrary.simpleMessage("退出"),
"expirationTime": MessageLookupByLibrary.simpleMessage("到期时间"),
"externalController": MessageLookupByLibrary.simpleMessage("外部控制器"),
"externalControllerDesc":
MessageLookupByLibrary.simpleMessage("开启后将可以通过9090端口控制clash内核"),
@@ -116,6 +117,7 @@ class MessageLookup extends MessageLookupByLibrary {
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hours": MessageLookupByLibrary.simpleMessage("小时"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"),
"init": MessageLookupByLibrary.simpleMessage("初始化"),
"ipCheckTimeout": MessageLookupByLibrary.simpleMessage("Ip检测超时"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收ipv6流量"),

View File

@@ -1889,6 +1889,26 @@ class AppLocalizations {
args: [],
);
}
/// `Long term effective`
String get infiniteTime {
return Intl.message(
'Long term effective',
name: 'infiniteTime',
desc: '',
args: [],
);
}
/// `Expiration time`
String get expirationTime {
return Intl.message(
'Expiration time',
name: 'expirationTime',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -75,6 +75,7 @@ Future<void> vpnService() async {
handleStart() async {
await app?.tip(appLocalizations.startVpn);
await globalState.startSystemProxy(
appState: appState,
config: config,
clashConfig: clashConfig,
);

View File

@@ -22,6 +22,7 @@ class AppState with ChangeNotifier {
bool _isInit;
VersionInfo? _versionInfo;
List<Traffic> _traffics;
Traffic _totalTraffic;
List<Log> _logs;
String _currentLabel;
SystemColorSchemes _systemColorSchemes;
@@ -48,6 +49,7 @@ class AppState with ChangeNotifier {
_sortNum = 0,
_requests = [],
_mode = mode,
_totalTraffic = Traffic(),
_delayMap = {},
_groups = [],
_isCompatible = isCompatible,
@@ -157,11 +159,24 @@ class AppState with ChangeNotifier {
}
}
addTraffic(Traffic value) {
_traffics = List.from(_traffics)..add(value);
addTraffic(Traffic traffic) {
_traffics = List.from(_traffics)..add(traffic);
const maxLength = 60;
if (_traffics.length > maxLength) {
_traffics = _traffics.sublist(_traffics.length - maxLength);
}
notifyListeners();
}
Traffic get totalTraffic => _totalTraffic;
set totalTraffic(Traffic value) {
if (_totalTraffic != value) {
_totalTraffic = value;
notifyListeners();
}
}
List<Connection> get requests => _requests;
set requests(List<Connection> value) {
@@ -191,10 +206,9 @@ class AppState with ChangeNotifier {
addLog(Log log) {
_logs.add(log);
if (!Platform.isAndroid) {
if (_logs.length > 60) {
_logs = _logs.sublist(_logs.length - 60);
}
final maxLength = Platform.isAndroid ? 1000 : 60;
if (_logs.length > maxLength) {
_logs = _logs.sublist(_logs.length - maxLength);
}
notifyListeners();
}

View File

@@ -57,6 +57,7 @@ class GlobalState {
}
Future<void> startSystemProxy({
required AppState appState,
required Config config,
required ClashConfig clashConfig,
}) async {
@@ -73,6 +74,11 @@ class GlobalState {
args: args,
);
startListenUpdate();
applyProfile(
appState: appState,
config: config,
clashConfig: clashConfig,
);
}
Future<void> stopSystemProxy() async {
@@ -195,6 +201,7 @@ class GlobalState {
final traffic = clashCore.getTraffic();
if (appState != null) {
appState.addTraffic(traffic);
appState.totalTraffic = clashCore.getTotalTraffic();
}
if (Platform.isAndroid) {
final currentProfile = config.currentProfile;

View File

@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
class CommonScaffold extends StatefulWidget {
final Widget body;
final Widget? bottomNavigationBar;
final Widget? floatingActionButton;
final String title;
final Widget? leading;
final List<Widget>? actions;
@@ -19,6 +20,7 @@ class CommonScaffold extends StatefulWidget {
this.leading,
required this.title,
this.actions,
this.floatingActionButton,
this.automaticallyImplyLeading = true,
});
@@ -116,12 +118,13 @@ class CommonScaffoldState extends State<CommonScaffold> {
Widget build(BuildContext context) {
return _platformContainer(
child: Scaffold(
floatingActionButton: ValueListenableBuilder(
valueListenable: _floatingActionButton,
builder: (_, floatingActionButton, __) {
return floatingActionButton ?? Container();
},
),
floatingActionButton: widget.floatingActionButton ??
ValueListenableBuilder(
valueListenable: _floatingActionButton,
builder: (_, floatingActionButton, __) {
return floatingActionButton ?? Container();
},
),
resizeToAvoidBottomInset: true,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),

View File

@@ -517,6 +517,22 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
isolate_contactor:
dependency: transitive
description:
name: isolate_contactor
sha256: f1be0a90f91e4309ef37cc45280b2a84e769e848aae378318dd3dd263cfc482a
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.0"
isolate_manager:
dependency: transitive
description:
name: isolate_manager
sha256: "8fb916c4444fd408f089448f904f083ac3e169ea1789fd4d987b25809af92188"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.3.1"
jovial_misc:
dependency: transitive
description:
@@ -537,10 +553,10 @@ packages:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.1"
version: "0.6.7"
json_annotation:
dependency: "direct main"
description:
@@ -820,6 +836,22 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.1"
re_editor:
dependency: "direct main"
description:
name: re_editor
sha256: db7a82e95f0f74301e85d4d5c805a8b8a5ba43d6c0d26673b7e35dc011f06635
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.0"
re_highlight:
dependency: "direct main"
description:
name: re_highlight
sha256: "6c4ac3f76f939fb7ca9df013df98526634e17d8f7460e028bd23a035870024f2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.3"
screen_retriever:
dependency: transitive
description:

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.24
version: 0.8.25
environment:
sdk: '>=3.1.0 <4.0.0'
@@ -17,7 +17,7 @@ dependencies:
provider: ^6.0.5
window_manager: ^0.3.8
ffi: ^2.1.0
dynamic_color: ^1.6.0
dynamic_color: ^1.7.0
proxy:
path: plugins/proxy
launch_at_startup: ^0.2.2
@@ -39,6 +39,8 @@ dependencies:
webdav_client: ^1.2.2
dio: ^5.4.3+1
country_flags: ^2.2.0
re_editor: ^0.3.0
re_highlight: ^0.0.3
dev_dependencies:
flutter_test:
sdk: flutter