diff --git a/core/Clash.Meta b/core/Clash.Meta index 943970e..48425d7 160000 --- a/core/Clash.Meta +++ b/core/Clash.Meta @@ -1 +1 @@ -Subproject commit 943970ea4db217c98aa4f4395bbe2f0efc439176 +Subproject commit 48425d7cf915dce6547afbb18dff6f2a16ae2805 diff --git a/core/hub.go b/core/hub.go index 9ded3c0..75e5191 100644 --- a/core/hub.go +++ b/core/hub.go @@ -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) diff --git a/core/tun.go b/core/tun.go index 2c7caab..20d971b 100644 --- a/core/tun.go +++ b/core/tun.go @@ -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 } }() diff --git a/lib/clash/core.dart b/lib/clash/core.dart index 41a065e..25491b5 100644 --- a/lib/clash/core.dart +++ b/lib/clash/core.dart @@ -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> getExternalProviders() { final externalProvidersRaw = clashFFI.getExternalProviders(); final externalProvidersRawString = - externalProvidersRaw.cast().toDartString(); + externalProvidersRaw.cast().toDartString(); return Isolate.run>(() { final externalProviders = - (json.decode(externalProvidersRawString) as List) - .map( - (item) => ExternalProvider.fromJson(item), - ) - .toList(); + (json.decode(externalProvidersRawString) as List) + .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().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().toDartString(); - if (runTimeString.isEmpty) return null; - return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString)); - } + // DateTime? getRunTime() { + // final runTimeString = clashFFI.getRunTime().cast().toDartString(); + // if (runTimeString.isEmpty) return null; + // return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString)); + // } List getConnections() { final connectionsDataRaw = clashFFI.getConnections(); final connectionsData = - json.decode(connectionsDataRaw.cast().toDartString()) as Map; + json.decode(connectionsDataRaw.cast().toDartString()) as Map; final connectionsRaw = connectionsData['connections'] as List? ?? []; return connectionsRaw.map((e) => Connection.fromJson(e)).toList(); } diff --git a/lib/clash/generated/clash_ffi.dart b/lib/clash/generated/clash_ffi.dart index 55bfec6..07729b2 100644 --- a/lib/clash/generated/clash_ffi.dart +++ b/lib/clash/generated/clash_ffi.dart @@ -983,6 +983,16 @@ class ClashFFI { late final _getTraffic = _getTrafficPtr.asFunction Function()>(); + ffi.Pointer getTotalTraffic() { + return _getTotalTraffic(); + } + + late final _getTotalTrafficPtr = + _lookup Function()>>( + 'getTotalTraffic'); + late final _getTotalTraffic = + _getTotalTrafficPtr.asFunction Function()>(); + void asyncTestDelay( ffi.Pointer s, int port, @@ -1156,16 +1166,6 @@ class ClashFFI { _lookup>('startTUN'); late final _startTUN = _startTUNPtr.asFunction(); - ffi.Pointer getRunTime() { - return _getRunTime(); - } - - late final _getRunTimePtr = - _lookup Function()>>( - 'getRunTime'); - late final _getRunTime = - _getRunTimePtr.asFunction Function()>(); - void stopTun() { return _stopTun(); } diff --git a/lib/controller.dart b/lib/controller.dart index 19bc80a..701200d 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -31,6 +31,7 @@ class AppController { Future updateSystemProxy(bool isStart) async { if (isStart) { await globalState.startSystemProxy( + appState: appState, config: config, clashConfig: clashConfig, ); diff --git a/lib/fragments/dashboard/network_speed.dart b/lib/fragments/dashboard/network_speed.dart index 5e5c26a..ab80b4f 100644 --- a/lib/fragments/dashboard/network_speed.dart +++ b/lib/fragments/dashboard/network_speed.dart @@ -21,26 +21,17 @@ class _NetworkSpeedState extends State { .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 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 traffics) { @@ -53,11 +44,10 @@ class _NetworkSpeedState extends State { 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 { @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 { ), ); } -} \ No newline at end of file +} diff --git a/lib/fragments/dashboard/traffic_usage.dart b/lib/fragments/dashboard/traffic_usage.dart index a50bdbc..1fdef1e 100644 --- a/lib/fragments/dashboard/traffic_usage.dart +++ b/lib/fragments/dashboard/traffic_usage.dart @@ -55,21 +55,11 @@ class TrafficUsage extends StatelessWidget { label: appLocalizations.trafficUsage, iconData: Icons.data_saver_off, ), - child: Selector>( - 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( + 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, ), ), ], diff --git a/lib/fragments/profiles/profiles.dart b/lib/fragments/profiles/profiles.dart index 001389a..5674e63 100644 --- a/lib/fragments/profiles/profiles.dart +++ b/lib/fragments/profiles/profiles.dart @@ -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 { ); } + _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 { 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 { Row( children: [ Text( - "到期时间:", + appLocalizations.expirationTime, style: textTheme.labelMedium?.toLighter(), ), const SizedBox( @@ -316,6 +328,11 @@ class _ProfileItemState extends State { 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 { case ProfileActions.update: _handleUpdateProfile(profile.id); break; + case ProfileActions.view: + _handleViewProfile(profile); + break; case null: break; } diff --git a/lib/fragments/profiles/view_profile.dart b/lib/fragments/profiles/view_profile.dart new file mode 100644 index 0000000..c88b1b6 --- /dev/null +++ b/lib/fragments/profiles/view_profile.dart @@ -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 createState() => _ViewProfileState(); +} + +class _ViewProfileState extends State { + 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 { + 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 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(); + }, + ), + ], + ); + } +} diff --git a/lib/fragments/proxies.dart b/lib/fragments/proxies.dart index 309499d..a8b8622 100644 --- a/lib/fragments/proxies.dart +++ b/lib/fragments/proxies.dart @@ -64,13 +64,18 @@ class _ProxiesFragmentState extends State 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 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( diff --git a/lib/fragments/theme.dart b/lib/fragments/theme.dart index cb97d9e..62a11f5 100644 --- a/lib/fragments/theme.dart +++ b/lib/fragments/theme.dart @@ -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: [ diff --git a/lib/l10n/arb/intl_en.arb b/lib/l10n/arb/intl_en.arb index f1f88c1..97f5ec5 100644 --- a/lib/l10n/arb/intl_en.arb +++ b/lib/l10n/arb/intl_en.arb @@ -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" } \ No newline at end of file diff --git a/lib/l10n/arb/intl_zh_CN.arb b/lib/l10n/arb/intl_zh_CN.arb index c809e39..678cfcb 100644 --- a/lib/l10n/arb/intl_zh_CN.arb +++ b/lib/l10n/arb/intl_zh_CN.arb @@ -182,5 +182,7 @@ "nullRequestsDesc": "未开启代理或者没有请求", "findProcessMode": "查找进程", "findProcessModeDesc": "开启后存在闪退风险", - "init": "初始化" + "init": "初始化", + "infiniteTime": "长期有效", + "expirationTime": "到期时间" } \ No newline at end of file diff --git a/lib/l10n/intl/messages_en.dart b/lib/l10n/intl/messages_en.dart index 4b03285..09002aa 100644 --- a/lib/l10n/intl/messages_en.dart +++ b/lib/l10n/intl/messages_en.dart @@ -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"), diff --git a/lib/l10n/intl/messages_zh_CN.dart b/lib/l10n/intl/messages_zh_CN.dart index b30ef63..7e313a9 100644 --- a/lib/l10n/intl/messages_zh_CN.dart +++ b/lib/l10n/intl/messages_zh_CN.dart @@ -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流量"), diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 10218e3..e7fe68f 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -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 { diff --git a/lib/main.dart b/lib/main.dart index 5b95f9f..c365ec4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -75,6 +75,7 @@ Future vpnService() async { handleStart() async { await app?.tip(appLocalizations.startVpn); await globalState.startSystemProxy( + appState: appState, config: config, clashConfig: clashConfig, ); diff --git a/lib/models/app.dart b/lib/models/app.dart index dbff945..5e88897 100644 --- a/lib/models/app.dart +++ b/lib/models/app.dart @@ -22,6 +22,7 @@ class AppState with ChangeNotifier { bool _isInit; VersionInfo? _versionInfo; List _traffics; + Traffic _totalTraffic; List _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 get requests => _requests; set requests(List 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(); } diff --git a/lib/state.dart b/lib/state.dart index 0b25dc0..48f06eb 100644 --- a/lib/state.dart +++ b/lib/state.dart @@ -57,6 +57,7 @@ class GlobalState { } Future 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 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; diff --git a/lib/widgets/scaffold.dart b/lib/widgets/scaffold.dart index 1842ecf..0849ee1 100644 --- a/lib/widgets/scaffold.dart +++ b/lib/widgets/scaffold.dart @@ -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? 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 { 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), diff --git a/pubspec.lock b/pubspec.lock index 136b3c4..fe4da1d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index a2255e3..c18444f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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