Compare commits

...

4 Commits

Author SHA1 Message Date
chen08209
de9c5ba9cc Optimize dashboard performance
Fix some issues
2025-03-10 18:41:42 +08:00
chen08209
2aae00cf68 Fix unselected proxy group delay issues 2025-03-10 18:41:42 +08:00
chen08209
68be2d34a1 Fix asn url issues 2025-03-08 04:22:32 +08:00
chen08209
7895ccf720 Update changelog 2025-03-07 16:03:59 +00:00
22 changed files with 659 additions and 180 deletions

View File

@@ -1,3 +1,19 @@
## v0.8.79
- Fix tab delay view issues
- Fix tray action issues
- Fix get profile redirect client ua issues
- Fix proxy card delay view issues
- Add Russian, Japanese adaptation
- Fix some issues
- Update changelog
## v0.8.78 ## v0.8.78
- Fix list form input view issues - Fix list form input view issues

View File

@@ -235,12 +235,12 @@ class ClashCore {
return int.parse(value); return int.parse(value);
} }
Future<ClashConfig?> getProfile(String id) async { Future<ClashConfigSnippet?> getProfile(String id) async {
final res = await clashInterface.getProfile(id); final res = await clashInterface.getProfile(id);
if (res.isEmpty) { if (res.isEmpty) {
return null; return null;
} }
return ClashConfig.fromJson(json.decode(res)); return ClashConfigSnippet.fromJson(json.decode(res));
} }
resetTraffic() { resetTraffic() {

View File

@@ -14,6 +14,7 @@ class Navigation {
const NavigationItem( const NavigationItem(
icon: Icon(Icons.space_dashboard), icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard, label: PageLabel.dashboard,
keep: false,
fragment: DashboardFragment( fragment: DashboardFragment(
key: GlobalObjectKey(PageLabel.dashboard), key: GlobalObjectKey(PageLabel.dashboard),
), ),

View File

@@ -25,7 +25,7 @@ class Render {
debouncer.call( debouncer.call(
DebounceTag.renderPause, DebounceTag.renderPause,
_pause, _pause,
duration: Duration(seconds: 15), duration: Duration(seconds: 5),
); );
} }

View File

@@ -62,12 +62,12 @@ class _MemoryInfoState extends State<MemoryInfo> {
onPressed: () { onPressed: () {
clashCore.requestGc(); clashCore.requestGc();
}, },
child: ValueListenableBuilder( child: Column(
valueListenable: _memoryInfoStateNotifier, children: [
builder: (_, trafficValue, __) { ValueListenableBuilder(
return Column( valueListenable: _memoryInfoStateNotifier,
children: [ builder: (_, trafficValue, __) {
Padding( return Padding(
padding: baseInfoEdgeInsets.copyWith( padding: baseInfoEdgeInsets.copyWith(
bottom: 0, bottom: 0,
top: 12, top: 12,
@@ -87,30 +87,30 @@ class _MemoryInfoState extends State<MemoryInfo> {
) )
], ],
), ),
), );
Flexible( },
child: Stack( ),
children: [ Flexible(
Positioned.fill( child: Stack(
child: WaveView( children: [
waveAmplitude: 12.0, Positioned.fill(
waveFrequency: 0.35, child: WaveView(
waveColor: darkenLighter, waveAmplitude: 12.0,
), waveFrequency: 0.35,
), waveColor: darkenLighter,
Positioned.fill( ),
child: WaveView(
waveAmplitude: 12.0,
waveFrequency: 0.9,
waveColor: darken,
),
),
],
), ),
) Positioned.fill(
], child: WaveView(
); waveAmplitude: 12.0,
}, waveFrequency: 0.9,
waveColor: darken,
),
),
],
),
),
],
), ),
), ),
); );

View File

@@ -59,7 +59,9 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
final isStart = appState.runTime != null; final isStart = appState.runTime != null;
if (_preIsStart == false && if (_preIsStart == false &&
_preIsStart == isStart && _preIsStart == isStart &&
_networkDetectionState.value.ipInfo != null) return; _networkDetectionState.value.ipInfo != null) {
return;
}
_clearSetTimeoutTimer(); _clearSetTimeoutTimer();
_networkDetectionState.value = _networkDetectionState.value.copyWith( _networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: true, isTesting: true,

View File

@@ -11,7 +11,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
class TrafficUsage extends StatelessWidget { class TrafficUsage extends StatelessWidget {
const TrafficUsage({super.key}); const TrafficUsage({super.key});
Widget getTrafficDataItem( Widget _buildTrafficDataItem(
BuildContext context, BuildContext context,
Icon icon, Icon icon,
TrafficValue trafficValue, TrafficValue trafficValue,
@@ -189,7 +189,7 @@ class TrafficUsage extends StatelessWidget {
), ),
), ),
), ),
getTrafficDataItem( _buildTrafficDataItem(
context, context,
Icon( Icon(
Icons.arrow_upward, Icons.arrow_upward,
@@ -201,7 +201,7 @@ class TrafficUsage extends StatelessWidget {
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
getTrafficDataItem( _buildTrafficDataItem(
context, context,
Icon( Icon(
Icons.arrow_downward, Icons.arrow_downward,

View File

@@ -1,54 +0,0 @@
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
class CustomProfile extends StatefulWidget {
final String profileId;
const CustomProfile({
super.key,
required this.profileId,
});
@override
State<CustomProfile> createState() => _CustomProfileState();
}
class _CustomProfileState extends State<CustomProfile> {
final _currentClashConfigNotifier = ValueNotifier<ClashConfig?>(null);
@override
void initState() {
super.initState();
_initCurrentClashConfig();
}
_initCurrentClashConfig() async {
// final currentProfileId = globalState.config.currentProfileId;
// if (currentProfileId == null) {
// return;
// }
// _currentClashConfigNotifier.value =
// await clashCore.getProfile(currentProfileId);
}
@override
Widget build(BuildContext context) {
return CommonScaffold(
body: ValueListenableBuilder(
valueListenable: _currentClashConfigNotifier,
builder: (_, clashConfig, ___) {
if (clashConfig == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return Column(
children: [],
);
},
),
title: "自定义",
);
}
}

View File

@@ -0,0 +1,87 @@
import 'package:fl_clash/clash/core.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
class GenProfile extends StatefulWidget {
final String profileId;
const GenProfile({
super.key,
required this.profileId,
});
@override
State<GenProfile> createState() => _GenProfileState();
}
class _GenProfileState extends State<GenProfile> {
final _currentClashConfigNotifier = ValueNotifier<ClashConfigSnippet?>(null);
@override
void initState() {
super.initState();
_initCurrentClashConfig();
}
_initCurrentClashConfig() async {
final currentProfileId = globalState.config.currentProfileId;
if (currentProfileId == null) {
return;
}
_currentClashConfigNotifier.value =
await clashCore.getProfile(currentProfileId);
}
@override
Widget build(BuildContext context) {
return CommonScaffold(
body: ValueListenableBuilder(
valueListenable: _currentClashConfigNotifier,
builder: (_, clashConfig, ___) {
if (clashConfig == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return Padding(
padding: EdgeInsets.all(16),
child: CustomScrollView(
slivers: [
SliverGrid.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 100,
mainAxisExtent: 50,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
itemCount: clashConfig.proxyGroups.length,
itemBuilder: (BuildContext context, int index) {
return CommonCard(
onPressed: () {},
child: Text(
clashConfig.proxyGroups[index].name,
),
);
},
),
SliverList.builder(
itemBuilder: (BuildContext context, int index) {
final rule = clashConfig.rule[index];
return Text(
rule,
);
},
itemCount: clashConfig.rule.length,
)
],
),
);
},
),
title: "自定义",
);
}
}

View File

@@ -11,6 +11,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'add_profile.dart'; import 'add_profile.dart';
import 'gen_profile.dart';
class ProfilesFragment extends StatefulWidget { class ProfilesFragment extends StatefulWidget {
const ProfilesFragment({super.key}); const ProfilesFragment({super.key});
@@ -273,14 +274,14 @@ class ProfileItem extends StatelessWidget {
} }
} }
// _handlePushCustomPage(BuildContext context, String id) { _handlePushGenProfilePage(BuildContext context, String id) {
// BaseNavigator.push( BaseNavigator.push(
// context, context,
// CustomProfile( GenProfile(
// profileId: id, profileId: id,
// ), ),
// ); );
// } }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -334,7 +335,7 @@ class ProfileItem extends StatelessWidget {
// icon: Icons.extension_outlined, // icon: Icons.extension_outlined,
// label: "自定义", // label: "自定义",
// onPressed: () { // onPressed: () {
// _handlePushCustomPage(context, profile.id); // _handlePushGenProfilePage(context, profile.id);
// }, // },
// ), // ),
ActionItemData( ActionItemData(

View File

@@ -26,6 +26,9 @@ proxyDelayTest(Proxy proxy, [String? testUrl]) async {
final url = state.testUrl.getSafeValue( final url = state.testUrl.getSafeValue(
appController.getRealTestUrl(testUrl), appController.getRealTestUrl(testUrl),
); );
if (state.proxyName.isEmpty) {
return;
}
appController.setDelay( appController.setDelay(
Delay( Delay(
url: url, url: url,
@@ -51,6 +54,9 @@ delayTest(List<Proxy> proxies, [String? testUrl]) async {
appController.getRealTestUrl(testUrl), appController.getRealTestUrl(testUrl),
); );
final name = state.proxyName; final name = state.proxyName;
if (name.isEmpty) {
return;
}
appController.setDelay( appController.setDelay(
Delay( Delay(
url: url, url: url,

View File

@@ -245,7 +245,7 @@ class GeoXUrl with _$GeoXUrl {
) )
String mmdb, String mmdb,
@Default( @Default(
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb",
) )
String asn, String asn,
@Default( @Default(
@@ -273,6 +273,17 @@ class GeoXUrl with _$GeoXUrl {
} }
} }
@freezed
class ClashConfigSnippet with _$ClashConfigSnippet {
const factory ClashConfigSnippet({
@Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@Default([]) List<String> rule,
}) = _ClashConfigSnippet;
factory ClashConfigSnippet.fromJson(Map<String, Object?> json) =>
_$ClashConfigSnippetFromJson(json);
}
@freezed @freezed
class ClashConfig with _$ClashConfig { class ClashConfig with _$ClashConfig {
const factory ClashConfig({ const factory ClashConfig({
@@ -301,7 +312,7 @@ class ClashConfig with _$ClashConfig {
@JsonKey(name: "geodata-loader") @JsonKey(name: "geodata-loader")
GeodataLoader geodataLoader, GeodataLoader geodataLoader,
@Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups, @Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@Default([]) List<String> rules, @Default([]) List<String> rule,
@JsonKey(name: "global-ua") String? globalUa, @JsonKey(name: "global-ua") String? globalUa,
@Default(ExternalControllerStatus.close) @Default(ExternalControllerStatus.close)
@JsonKey(name: "external-controller") @JsonKey(name: "external-controller")

View File

@@ -1750,7 +1750,7 @@ class _$GeoXUrlImpl implements _GeoXUrl {
{this.mmdb = {this.mmdb =
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb",
this.asn = this.asn =
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb",
this.geoip = this.geoip =
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat",
this.geosite = this.geosite =
@@ -1834,6 +1834,202 @@ abstract class _GeoXUrl implements GeoXUrl {
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
return _ClashConfigSnippet.fromJson(json);
}
/// @nodoc
mixin _$ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError;
List<String> get rule => throw _privateConstructorUsedError;
/// Serializes this ClashConfigSnippet to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ClashConfigSnippetCopyWith<ClashConfigSnippet> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ClashConfigSnippetCopyWith<$Res> {
factory $ClashConfigSnippetCopyWith(
ClashConfigSnippet value, $Res Function(ClashConfigSnippet) then) =
_$ClashConfigSnippetCopyWithImpl<$Res, ClashConfigSnippet>;
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
List<String> rule});
}
/// @nodoc
class _$ClashConfigSnippetCopyWithImpl<$Res, $Val extends ClashConfigSnippet>
implements $ClashConfigSnippetCopyWith<$Res> {
_$ClashConfigSnippetCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? proxyGroups = null,
Object? rule = null,
}) {
return _then(_value.copyWith(
proxyGroups: null == proxyGroups
? _value.proxyGroups
: proxyGroups // ignore: cast_nullable_to_non_nullable
as List<ProxyGroup>,
rule: null == rule
? _value.rule
: rule // ignore: cast_nullable_to_non_nullable
as List<String>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ClashConfigSnippetImplCopyWith<$Res>
implements $ClashConfigSnippetCopyWith<$Res> {
factory _$$ClashConfigSnippetImplCopyWith(_$ClashConfigSnippetImpl value,
$Res Function(_$ClashConfigSnippetImpl) then) =
__$$ClashConfigSnippetImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
List<String> rule});
}
/// @nodoc
class __$$ClashConfigSnippetImplCopyWithImpl<$Res>
extends _$ClashConfigSnippetCopyWithImpl<$Res, _$ClashConfigSnippetImpl>
implements _$$ClashConfigSnippetImplCopyWith<$Res> {
__$$ClashConfigSnippetImplCopyWithImpl(_$ClashConfigSnippetImpl _value,
$Res Function(_$ClashConfigSnippetImpl) _then)
: super(_value, _then);
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? proxyGroups = null,
Object? rule = null,
}) {
return _then(_$ClashConfigSnippetImpl(
proxyGroups: null == proxyGroups
? _value._proxyGroups
: proxyGroups // ignore: cast_nullable_to_non_nullable
as List<ProxyGroup>,
rule: null == rule
? _value._rule
: rule // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
const _$ClashConfigSnippetImpl(
{@JsonKey(name: "proxy-groups")
final List<ProxyGroup> proxyGroups = const [],
final List<String> rule = const []})
: _proxyGroups = proxyGroups,
_rule = rule;
factory _$ClashConfigSnippetImpl.fromJson(Map<String, dynamic> json) =>
_$$ClashConfigSnippetImplFromJson(json);
final List<ProxyGroup> _proxyGroups;
@override
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups {
if (_proxyGroups is EqualUnmodifiableListView) return _proxyGroups;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_proxyGroups);
}
final List<String> _rule;
@override
@JsonKey()
List<String> get rule {
if (_rule is EqualUnmodifiableListView) return _rule;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_rule);
}
@override
String toString() {
return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ClashConfigSnippetImpl &&
const DeepCollectionEquality()
.equals(other._proxyGroups, _proxyGroups) &&
const DeepCollectionEquality().equals(other._rule, _rule));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_proxyGroups),
const DeepCollectionEquality().hash(_rule));
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ClashConfigSnippetImplCopyWith<_$ClashConfigSnippetImpl> get copyWith =>
__$$ClashConfigSnippetImplCopyWithImpl<_$ClashConfigSnippetImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ClashConfigSnippetImplToJson(
this,
);
}
}
abstract class _ClashConfigSnippet implements ClashConfigSnippet {
const factory _ClashConfigSnippet(
{@JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups,
final List<String> rule}) = _$ClashConfigSnippetImpl;
factory _ClashConfigSnippet.fromJson(Map<String, dynamic> json) =
_$ClashConfigSnippetImpl.fromJson;
@override
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups;
@override
List<String> get rule;
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ClashConfigSnippetImplCopyWith<_$ClashConfigSnippetImpl> get copyWith =>
throw _privateConstructorUsedError;
}
ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) { ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) {
return _ClashConfig.fromJson(json); return _ClashConfig.fromJson(json);
} }
@@ -1866,7 +2062,7 @@ mixin _$ClashConfig {
GeodataLoader get geodataLoader => throw _privateConstructorUsedError; GeodataLoader get geodataLoader => throw _privateConstructorUsedError;
@JsonKey(name: "proxy-groups") @JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError; List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError;
List<String> get rules => throw _privateConstructorUsedError; List<String> get rule => throw _privateConstructorUsedError;
@JsonKey(name: "global-ua") @JsonKey(name: "global-ua")
String? get globalUa => throw _privateConstructorUsedError; String? get globalUa => throw _privateConstructorUsedError;
@JsonKey(name: "external-controller") @JsonKey(name: "external-controller")
@@ -1907,7 +2103,7 @@ abstract class $ClashConfigCopyWith<$Res> {
GeoXUrl geoXUrl, GeoXUrl geoXUrl,
@JsonKey(name: "geodata-loader") GeodataLoader geodataLoader, @JsonKey(name: "geodata-loader") GeodataLoader geodataLoader,
@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups, @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
List<String> rules, List<String> rule,
@JsonKey(name: "global-ua") String? globalUa, @JsonKey(name: "global-ua") String? globalUa,
@JsonKey(name: "external-controller") @JsonKey(name: "external-controller")
ExternalControllerStatus externalController, ExternalControllerStatus externalController,
@@ -1947,7 +2143,7 @@ class _$ClashConfigCopyWithImpl<$Res, $Val extends ClashConfig>
Object? geoXUrl = null, Object? geoXUrl = null,
Object? geodataLoader = null, Object? geodataLoader = null,
Object? proxyGroups = null, Object? proxyGroups = null,
Object? rules = null, Object? rule = null,
Object? globalUa = freezed, Object? globalUa = freezed,
Object? externalController = null, Object? externalController = null,
Object? hosts = null, Object? hosts = null,
@@ -2009,9 +2205,9 @@ class _$ClashConfigCopyWithImpl<$Res, $Val extends ClashConfig>
? _value.proxyGroups ? _value.proxyGroups
: proxyGroups // ignore: cast_nullable_to_non_nullable : proxyGroups // ignore: cast_nullable_to_non_nullable
as List<ProxyGroup>, as List<ProxyGroup>,
rules: null == rules rule: null == rule
? _value.rules ? _value.rule
: rules // ignore: cast_nullable_to_non_nullable : rule // ignore: cast_nullable_to_non_nullable
as List<String>, as List<String>,
globalUa: freezed == globalUa globalUa: freezed == globalUa
? _value.globalUa ? _value.globalUa
@@ -2084,7 +2280,7 @@ abstract class _$$ClashConfigImplCopyWith<$Res>
GeoXUrl geoXUrl, GeoXUrl geoXUrl,
@JsonKey(name: "geodata-loader") GeodataLoader geodataLoader, @JsonKey(name: "geodata-loader") GeodataLoader geodataLoader,
@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups, @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
List<String> rules, List<String> rule,
@JsonKey(name: "global-ua") String? globalUa, @JsonKey(name: "global-ua") String? globalUa,
@JsonKey(name: "external-controller") @JsonKey(name: "external-controller")
ExternalControllerStatus externalController, ExternalControllerStatus externalController,
@@ -2125,7 +2321,7 @@ class __$$ClashConfigImplCopyWithImpl<$Res>
Object? geoXUrl = null, Object? geoXUrl = null,
Object? geodataLoader = null, Object? geodataLoader = null,
Object? proxyGroups = null, Object? proxyGroups = null,
Object? rules = null, Object? rule = null,
Object? globalUa = freezed, Object? globalUa = freezed,
Object? externalController = null, Object? externalController = null,
Object? hosts = null, Object? hosts = null,
@@ -2187,9 +2383,9 @@ class __$$ClashConfigImplCopyWithImpl<$Res>
? _value._proxyGroups ? _value._proxyGroups
: proxyGroups // ignore: cast_nullable_to_non_nullable : proxyGroups // ignore: cast_nullable_to_non_nullable
as List<ProxyGroup>, as List<ProxyGroup>,
rules: null == rules rule: null == rule
? _value._rules ? _value._rule
: rules // ignore: cast_nullable_to_non_nullable : rule // ignore: cast_nullable_to_non_nullable
as List<String>, as List<String>,
globalUa: freezed == globalUa globalUa: freezed == globalUa
? _value.globalUa ? _value.globalUa
@@ -2230,13 +2426,13 @@ class _$ClashConfigImpl implements _ClashConfig {
this.geodataLoader = GeodataLoader.memconservative, this.geodataLoader = GeodataLoader.memconservative,
@JsonKey(name: "proxy-groups") @JsonKey(name: "proxy-groups")
final List<ProxyGroup> proxyGroups = const [], final List<ProxyGroup> proxyGroups = const [],
final List<String> rules = const [], final List<String> rule = const [],
@JsonKey(name: "global-ua") this.globalUa, @JsonKey(name: "global-ua") this.globalUa,
@JsonKey(name: "external-controller") @JsonKey(name: "external-controller")
this.externalController = ExternalControllerStatus.close, this.externalController = ExternalControllerStatus.close,
final Map<String, String> hosts = const {}}) final Map<String, String> hosts = const {}})
: _proxyGroups = proxyGroups, : _proxyGroups = proxyGroups,
_rules = rules, _rule = rule,
_hosts = hosts; _hosts = hosts;
factory _$ClashConfigImpl.fromJson(Map<String, dynamic> json) => factory _$ClashConfigImpl.fromJson(Map<String, dynamic> json) =>
@@ -2290,13 +2486,13 @@ class _$ClashConfigImpl implements _ClashConfig {
return EqualUnmodifiableListView(_proxyGroups); return EqualUnmodifiableListView(_proxyGroups);
} }
final List<String> _rules; final List<String> _rule;
@override @override
@JsonKey() @JsonKey()
List<String> get rules { List<String> get rule {
if (_rules is EqualUnmodifiableListView) return _rules; if (_rule is EqualUnmodifiableListView) return _rule;
// ignore: implicit_dynamic_type // ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_rules); return EqualUnmodifiableListView(_rule);
} }
@override @override
@@ -2316,7 +2512,7 @@ class _$ClashConfigImpl implements _ClashConfig {
@override @override
String toString() { String toString() {
return 'ClashConfig(mixedPort: $mixedPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, proxyGroups: $proxyGroups, rules: $rules, globalUa: $globalUa, externalController: $externalController, hosts: $hosts)'; return 'ClashConfig(mixedPort: $mixedPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, proxyGroups: $proxyGroups, rule: $rule, globalUa: $globalUa, externalController: $externalController, hosts: $hosts)';
} }
@override @override
@@ -2347,7 +2543,7 @@ class _$ClashConfigImpl implements _ClashConfig {
other.geodataLoader == geodataLoader) && other.geodataLoader == geodataLoader) &&
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other._proxyGroups, _proxyGroups) && .equals(other._proxyGroups, _proxyGroups) &&
const DeepCollectionEquality().equals(other._rules, _rules) && const DeepCollectionEquality().equals(other._rule, _rule) &&
(identical(other.globalUa, globalUa) || (identical(other.globalUa, globalUa) ||
other.globalUa == globalUa) && other.globalUa == globalUa) &&
(identical(other.externalController, externalController) || (identical(other.externalController, externalController) ||
@@ -2373,7 +2569,7 @@ class _$ClashConfigImpl implements _ClashConfig {
geoXUrl, geoXUrl,
geodataLoader, geodataLoader,
const DeepCollectionEquality().hash(_proxyGroups), const DeepCollectionEquality().hash(_proxyGroups),
const DeepCollectionEquality().hash(_rules), const DeepCollectionEquality().hash(_rule),
globalUa, globalUa,
externalController, externalController,
const DeepCollectionEquality().hash(_hosts)); const DeepCollectionEquality().hash(_hosts));
@@ -2412,7 +2608,7 @@ abstract class _ClashConfig implements ClashConfig {
final GeoXUrl geoXUrl, final GeoXUrl geoXUrl,
@JsonKey(name: "geodata-loader") final GeodataLoader geodataLoader, @JsonKey(name: "geodata-loader") final GeodataLoader geodataLoader,
@JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups, @JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups,
final List<String> rules, final List<String> rule,
@JsonKey(name: "global-ua") final String? globalUa, @JsonKey(name: "global-ua") final String? globalUa,
@JsonKey(name: "external-controller") @JsonKey(name: "external-controller")
final ExternalControllerStatus externalController, final ExternalControllerStatus externalController,
@@ -2462,7 +2658,7 @@ abstract class _ClashConfig implements ClashConfig {
@JsonKey(name: "proxy-groups") @JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups; List<ProxyGroup> get proxyGroups;
@override @override
List<String> get rules; List<String> get rule;
@override @override
@JsonKey(name: "global-ua") @JsonKey(name: "global-ua")
String? get globalUa; String? get globalUa;

View File

@@ -191,7 +191,7 @@ _$GeoXUrlImpl _$$GeoXUrlImplFromJson(Map<String, dynamic> json) =>
mmdb: json['mmdb'] as String? ?? mmdb: json['mmdb'] as String? ??
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb",
asn: json['asn'] as String? ?? asn: json['asn'] as String? ??
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb",
geoip: json['geoip'] as String? ?? geoip: json['geoip'] as String? ??
"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat",
geosite: json['geosite'] as String? ?? geosite: json['geosite'] as String? ??
@@ -206,6 +206,25 @@ Map<String, dynamic> _$$GeoXUrlImplToJson(_$GeoXUrlImpl instance) =>
'geosite': instance.geosite, 'geosite': instance.geosite,
}; };
_$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
Map<String, dynamic> json) =>
_$ClashConfigSnippetImpl(
proxyGroups: (json['proxy-groups'] as List<dynamic>?)
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
rule:
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [],
);
Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
_$ClashConfigSnippetImpl instance) =>
<String, dynamic>{
'proxy-groups': instance.proxyGroups,
'rule': instance.rule,
};
_$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) => _$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) =>
_$ClashConfigImpl( _$ClashConfigImpl(
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort, mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
@@ -238,8 +257,8 @@ _$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) =>
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>)) ?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], const [],
rules: rule:
(json['rules'] as List<dynamic>?)?.map((e) => e as String).toList() ?? (json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [], const [],
globalUa: json['global-ua'] as String?, globalUa: json['global-ua'] as String?,
externalController: $enumDecodeNullable( externalController: $enumDecodeNullable(
@@ -267,7 +286,7 @@ Map<String, dynamic> _$$ClashConfigImplToJson(_$ClashConfigImpl instance) =>
'geox-url': instance.geoXUrl, 'geox-url': instance.geoXUrl,
'geodata-loader': _$GeodataLoaderEnumMap[instance.geodataLoader]!, 'geodata-loader': _$GeodataLoaderEnumMap[instance.geodataLoader]!,
'proxy-groups': instance.proxyGroups, 'proxy-groups': instance.proxyGroups,
'rules': instance.rules, 'rule': instance.rule,
'global-ua': instance.globalUa, 'global-ua': instance.globalUa,
'external-controller': 'external-controller':
_$ExternalControllerStatusEnumMap[instance.externalController]!, _$ExternalControllerStatusEnumMap[instance.externalController]!,

View File

@@ -14,6 +14,153 @@ T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError( final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$VM2<A, B> {
A get a => throw _privateConstructorUsedError;
B get b => throw _privateConstructorUsedError;
/// Create a copy of VM2
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$VM2CopyWith<A, B, VM2<A, B>> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $VM2CopyWith<A, B, $Res> {
factory $VM2CopyWith(VM2<A, B> value, $Res Function(VM2<A, B>) then) =
_$VM2CopyWithImpl<A, B, $Res, VM2<A, B>>;
@useResult
$Res call({A a, B b});
}
/// @nodoc
class _$VM2CopyWithImpl<A, B, $Res, $Val extends VM2<A, B>>
implements $VM2CopyWith<A, B, $Res> {
_$VM2CopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of VM2
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? a = freezed,
Object? b = freezed,
}) {
return _then(_value.copyWith(
a: freezed == a
? _value.a
: a // ignore: cast_nullable_to_non_nullable
as A,
b: freezed == b
? _value.b
: b // ignore: cast_nullable_to_non_nullable
as B,
) as $Val);
}
}
/// @nodoc
abstract class _$$VM2ImplCopyWith<A, B, $Res>
implements $VM2CopyWith<A, B, $Res> {
factory _$$VM2ImplCopyWith(
_$VM2Impl<A, B> value, $Res Function(_$VM2Impl<A, B>) then) =
__$$VM2ImplCopyWithImpl<A, B, $Res>;
@override
@useResult
$Res call({A a, B b});
}
/// @nodoc
class __$$VM2ImplCopyWithImpl<A, B, $Res>
extends _$VM2CopyWithImpl<A, B, $Res, _$VM2Impl<A, B>>
implements _$$VM2ImplCopyWith<A, B, $Res> {
__$$VM2ImplCopyWithImpl(
_$VM2Impl<A, B> _value, $Res Function(_$VM2Impl<A, B>) _then)
: super(_value, _then);
/// Create a copy of VM2
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? a = freezed,
Object? b = freezed,
}) {
return _then(_$VM2Impl<A, B>(
a: freezed == a
? _value.a
: a // ignore: cast_nullable_to_non_nullable
as A,
b: freezed == b
? _value.b
: b // ignore: cast_nullable_to_non_nullable
as B,
));
}
}
/// @nodoc
class _$VM2Impl<A, B> implements _VM2<A, B> {
const _$VM2Impl({required this.a, required this.b});
@override
final A a;
@override
final B b;
@override
String toString() {
return 'VM2<$A, $B>(a: $a, b: $b)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$VM2Impl<A, B> &&
const DeepCollectionEquality().equals(other.a, a) &&
const DeepCollectionEquality().equals(other.b, b));
}
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(a),
const DeepCollectionEquality().hash(b));
/// Create a copy of VM2
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$VM2ImplCopyWith<A, B, _$VM2Impl<A, B>> get copyWith =>
__$$VM2ImplCopyWithImpl<A, B, _$VM2Impl<A, B>>(this, _$identity);
}
abstract class _VM2<A, B> implements VM2<A, B> {
const factory _VM2({required final A a, required final B b}) =
_$VM2Impl<A, B>;
@override
A get a;
@override
B get b;
/// Create a copy of VM2
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$VM2ImplCopyWith<A, B, _$VM2Impl<A, B>> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
mixin _$StartButtonSelectorState { mixin _$StartButtonSelectorState {
bool get isInit => throw _privateConstructorUsedError; bool get isInit => throw _privateConstructorUsedError;

View File

@@ -7,6 +7,15 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/selector.freezed.dart'; part 'generated/selector.freezed.dart';
@freezed
class VM2<A, B> with _$VM2<A, B> {
const factory VM2({
required A a,
required B b,
}) = _VM2;
}
@freezed @freezed
class StartButtonSelectorState with _$StartButtonSelectorState { class StartButtonSelectorState with _$StartButtonSelectorState {
const factory StartButtonSelectorState({ const factory StartButtonSelectorState({
@@ -134,18 +143,19 @@ extension PackageListSelectorStateExt on PackageListSelectorState {
return packages return packages
.where((item) => isFilterSystemApp ? item.isSystem == false : true) .where((item) => isFilterSystemApp ? item.isSystem == false : true)
.sorted( .sorted(
(a, b) { (a, b) {
return switch (sort) { return switch (sort) {
AccessSortType.none => 0, AccessSortType.none => 0,
AccessSortType.name => other.sortByChar( AccessSortType.name =>
other.getPinyin(a.label), other.sortByChar(
other.getPinyin(b.label), other.getPinyin(a.label),
), other.getPinyin(b.label),
),
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime), AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
}; };
}, },
).sorted( ).sorted(
(a, b) { (a, b) {
final isSelectA = selectedList.contains(a.packageName); final isSelectA = selectedList.contains(a.packageName);
final isSelectB = selectedList.contains(b.packageName); final isSelectB = selectedList.contains(b.packageName);
if (isSelectA && isSelectB) return 0; if (isSelectA && isSelectB) return 0;

View File

@@ -95,7 +95,7 @@ final clashConfigStateProvider = AutoDisposeProvider<ClashConfigState>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
typedef ClashConfigStateRef = AutoDisposeProviderRef<ClashConfigState>; typedef ClashConfigStateRef = AutoDisposeProviderRef<ClashConfigState>;
String _$proxyStateHash() => r'61ec20fcf35118aca445719c83e77e7d237f5570'; String _$proxyStateHash() => r'22478fb593aaca11dfe2cf64472013190475a5bc';
/// See also [proxyState]. /// See also [proxyState].
@ProviderFor(proxyState) @ProviderFor(proxyState)

View File

@@ -81,14 +81,19 @@ ClashConfigState clashConfigState(Ref ref) {
@riverpod @riverpod
ProxyState proxyState(Ref ref) { ProxyState proxyState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null)); final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final networkProps = ref.watch(networkSettingProvider); final vm2 = ref.watch(networkSettingProvider.select(
(state) => VM2(
a: state.systemProxy,
b: state.bypassDomain,
),
));
final mixedPort = ref.watch( final mixedPort = ref.watch(
patchClashConfigProvider.select((state) => state.mixedPort), patchClashConfigProvider.select((state) => state.mixedPort),
); );
return ProxyState( return ProxyState(
isStart: isStart, isStart: isStart,
systemProxy: networkProps.systemProxy, systemProxy: vm2.a,
bassDomain: networkProps.bypassDomain, bassDomain: vm2.b,
port: mixedPort, port: mixedPort,
); );
} }

View File

@@ -1,4 +1,5 @@
import 'dart:math'; import 'dart:math';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -18,6 +19,17 @@ class DonutChartData {
String toString() { String toString() {
return 'DonutChartData{_value: $_value}'; return 'DonutChartData{_value: $_value}';
} }
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DonutChartData &&
runtimeType == other.runtimeType &&
_value == other._value &&
color == other.color;
@override
int get hashCode => _value.hashCode ^ color.hashCode;
} }
class DonutChart extends StatefulWidget { class DonutChart extends StatefulWidget {

View File

@@ -1,4 +1,5 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class WaveView extends StatefulWidget { class WaveView extends StatefulWidget {
@@ -40,23 +41,25 @@ class _WaveViewState extends State<WaveView>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (_, container) { return LayoutBuilder(builder: (_, constraints) {
return AnimatedBuilder( return RepaintBoundary(
animation: _controller, child: AnimatedBuilder(
builder: (context, child) { animation: _controller,
return CustomPaint( builder: (context, child) {
painter: WavePainter( return CustomPaint(
animationValue: _controller.value, painter: WavePainter(
waveAmplitude: widget.waveAmplitude, animationValue: _controller.value,
waveFrequency: widget.waveFrequency, waveAmplitude: widget.waveAmplitude,
waveColor: widget.waveColor, waveFrequency: widget.waveFrequency,
), waveColor: widget.waveColor,
size: Size( ),
container.maxHeight, size: Size(
container.maxHeight, constraints.maxWidth,
), constraints.maxHeight,
); ),
}, );
},
),
); );
}); });
} }
@@ -68,41 +71,60 @@ class WavePainter extends CustomPainter {
final double waveFrequency; final double waveFrequency;
final Color waveColor; final Color waveColor;
late Paint _paint;
final Path _path = Path();
Color _lastColor;
WavePainter({ WavePainter({
required this.animationValue, required this.animationValue,
required this.waveAmplitude, required this.waveAmplitude,
required this.waveFrequency, required this.waveFrequency,
required this.waveColor, required this.waveColor,
}); }) : _lastColor = waveColor {
_paint = Paint()
..color = waveColor
..style = PaintingStyle.fill;
}
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final paint = Paint() if (waveColor != _lastColor) {
..color = waveColor _paint = Paint()
..style = PaintingStyle.fill; ..color = waveColor
..style = PaintingStyle.fill;
final path = Path(); _lastColor = waveColor;
final baseHeight = size.height / 3;
path.moveTo(0, baseHeight);
for (double x = 0; x <= size.width; x++) {
final y = waveAmplitude *
sin((x / size.width * 2 * pi * waveFrequency) +
(animationValue * 2 * pi));
path.lineTo(x, baseHeight + y);
} }
path.lineTo(size.width, size.height); _path.reset();
path.lineTo(0, size.height);
path.close();
canvas.drawPath(path, paint); final baseHeight = size.height / 3;
final phase = animationValue * 2 * pi;
final widthFactor = 2 * pi * waveFrequency / size.width;
_path.moveTo(0, baseHeight);
for (double x = 0; x <= size.width; x += size.width / 20) {
final y = waveAmplitude * sin(x * widthFactor + phase);
_path.lineTo(x, baseHeight + y);
}
_path.lineTo(
size.width,
baseHeight + waveAmplitude * sin(size.width * widthFactor + phase),
);
_path.lineTo(size.width, size.height);
_path.lineTo(0, size.height);
_path.close();
canvas.drawPath(_path, _paint);
} }
@override @override
bool shouldRepaint(covariant CustomPainter oldDelegate) { bool shouldRepaint(covariant WavePainter oldDelegate) {
return true; return oldDelegate.animationValue != animationValue ||
oldDelegate.waveAmplitude != waveAmplitude ||
oldDelegate.waveFrequency != waveFrequency ||
oldDelegate.waveColor != waveColor;
} }
} }

View File

@@ -43,7 +43,5 @@
<string>MainMenu</string> <string>MainMenu</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
<key>FLTEnableImpeller</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -1,7 +1,7 @@
name: fl_clash name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none' publish_to: 'none'
version: 0.8.79+202503071 version: 0.8.80+202503101
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'