Optimize app layout
This commit is contained in:
+64
-80
@@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/desktop/helper_client.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -13,6 +14,7 @@ import 'package:path/path.dart';
|
||||
|
||||
class System {
|
||||
static System? _instance;
|
||||
bool _isTV = false;
|
||||
|
||||
System._internal();
|
||||
|
||||
@@ -31,8 +33,19 @@ class System {
|
||||
|
||||
bool get isLinux => Platform.isLinux;
|
||||
|
||||
Future<int> get version async {
|
||||
bool get isTV => _isTV;
|
||||
|
||||
Future<int> init() async {
|
||||
final deviceInfo = await DeviceInfoPlugin().deviceInfo;
|
||||
_isTV = switch (deviceInfo) {
|
||||
AndroidDeviceInfo(:final systemFeatures) => systemFeatures.any(
|
||||
const {
|
||||
'android.hardware.type.television',
|
||||
'android.software.leanback',
|
||||
}.contains,
|
||||
),
|
||||
_ => false,
|
||||
};
|
||||
return switch (Platform.operatingSystem) {
|
||||
'macos' => (deviceInfo as MacOsDeviceInfo).majorVersion,
|
||||
'android' => (deviceInfo as AndroidDeviceInfo).version.sdkInt,
|
||||
@@ -41,11 +54,15 @@ class System {
|
||||
};
|
||||
}
|
||||
|
||||
Future<bool> didCrashOnPreviousExecution() async {
|
||||
if (!isAndroid) return false;
|
||||
return await app?.didCrashOnPreviousExecution() ?? false;
|
||||
}
|
||||
|
||||
Future<bool> checkIsAdmin() async {
|
||||
final corePath = appPath.corePath.replaceAll(' ', '\\\\ ');
|
||||
if (system.isWindows) {
|
||||
final result = await windows?.checkService();
|
||||
return result == WindowsHelperServiceStatus.running;
|
||||
return windowsHelperClient.isReady();
|
||||
} else if (system.isMacOS) {
|
||||
final result = await Process.run('stat', ['-f', '%Su:%Sg %Sp', corePath]);
|
||||
final output = result.stdout.trim();
|
||||
@@ -72,19 +89,14 @@ class System {
|
||||
if (system.isAndroid) {
|
||||
return AuthorizeCode.error;
|
||||
}
|
||||
if (system.isWindows) {
|
||||
return await windows?.registerService() ?? AuthorizeCode.error;
|
||||
}
|
||||
final isAdmin = await checkIsAdmin();
|
||||
if (isAdmin) {
|
||||
return AuthorizeCode.none;
|
||||
}
|
||||
|
||||
if (system.isWindows) {
|
||||
final result = await windows?.registerService();
|
||||
if (result == true) {
|
||||
return AuthorizeCode.success;
|
||||
}
|
||||
return AuthorizeCode.error;
|
||||
}
|
||||
|
||||
if (system.isMacOS) {
|
||||
final escapedPath = _shellEscape(appPath.corePath);
|
||||
final shell = 'chown root:admin $escapedPath && chmod +sx $escapedPath';
|
||||
@@ -134,7 +146,6 @@ class System {
|
||||
if (system.isAndroid) {
|
||||
await SystemNavigator.pop();
|
||||
}
|
||||
await window?.close();
|
||||
window?.forceExit();
|
||||
}
|
||||
}
|
||||
@@ -203,79 +214,52 @@ class Windows {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Future<void> _killProcess(int port) async {
|
||||
// final result = await Process.run('netstat', ['-ano']);
|
||||
// final lines = result.stdout.toString().trim().split('\n');
|
||||
// for (final line in lines) {
|
||||
// if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
// continue;
|
||||
// }
|
||||
// final parts = line.trim().split(RegExp(r'\s+'));
|
||||
// final pid = int.tryParse(parts.last);
|
||||
// if (pid != null) {
|
||||
// await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Future<AuthorizeCode> registerService() async {
|
||||
if (await windowsHelperClient.isReady()) {
|
||||
commonPrint.log('helper service is ready');
|
||||
return AuthorizeCode.none;
|
||||
}
|
||||
|
||||
Future<WindowsHelperServiceStatus> checkService() async {
|
||||
// final qcResult = await Process.run('sc', ['qc', appHelperService]);
|
||||
// final qcOutput = qcResult.stdout.toString();
|
||||
// if (qcResult.exitCode != 0 || !qcOutput.contains(appPath.helperPath)) {
|
||||
// return WindowsHelperServiceStatus.none;
|
||||
// }
|
||||
final result = await Process.run('sc', ['query', appHelperService]);
|
||||
if (result.exitCode != 0) {
|
||||
return WindowsHelperServiceStatus.none;
|
||||
commonPrint.log(
|
||||
'helper service is unavailable, requesting elevated installation',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
if (!runas(appPath.helperPath, 'install')) {
|
||||
commonPrint.log(
|
||||
'failed to launch elevated helper installation',
|
||||
logLevel: LogLevel.error,
|
||||
);
|
||||
return AuthorizeCode.error;
|
||||
}
|
||||
final output = result.stdout.toString();
|
||||
if (output.contains('RUNNING') && await request.pingHelper()) {
|
||||
return WindowsHelperServiceStatus.running;
|
||||
}
|
||||
return WindowsHelperServiceStatus.presence;
|
||||
|
||||
final isRunning = await _waitForHelperService();
|
||||
commonPrint.log(
|
||||
isRunning
|
||||
? 'helper service installation completed'
|
||||
: 'helper service did not become ready after installation',
|
||||
logLevel: isRunning ? LogLevel.info : LogLevel.error,
|
||||
);
|
||||
return isRunning ? AuthorizeCode.success : AuthorizeCode.error;
|
||||
}
|
||||
|
||||
Future<bool> registerService() async {
|
||||
final status = await checkService();
|
||||
|
||||
if (status == WindowsHelperServiceStatus.running) {
|
||||
return true;
|
||||
Future<bool> _waitForHelperService() async {
|
||||
const timeout = Duration(seconds: 6);
|
||||
const interval = Duration(seconds: 1);
|
||||
const maxAttempts = 6;
|
||||
final stopwatch = Stopwatch()..start();
|
||||
for (var attempt = 0; attempt < maxAttempts; attempt++) {
|
||||
final remaining = timeout - stopwatch.elapsed;
|
||||
if (remaining <= Duration.zero) return false;
|
||||
final isRunning = await windowsHelperClient.isReady(
|
||||
timeout: remaining,
|
||||
logFailure: false,
|
||||
);
|
||||
if (isRunning) return true;
|
||||
final delay = timeout - stopwatch.elapsed;
|
||||
if (delay <= Duration.zero || attempt == maxAttempts - 1) return false;
|
||||
await Future.delayed(delay < interval ? delay : interval);
|
||||
}
|
||||
|
||||
final command = [
|
||||
'/c',
|
||||
if (status == WindowsHelperServiceStatus.presence) ...[
|
||||
'taskkill',
|
||||
'/F',
|
||||
'/IM',
|
||||
'$appHelperService.exe'
|
||||
' & '
|
||||
'sc',
|
||||
'delete',
|
||||
appHelperService,
|
||||
'&',
|
||||
],
|
||||
'sc',
|
||||
'create',
|
||||
appHelperService,
|
||||
'binPath= "${appPath.helperPath}"',
|
||||
'start= auto',
|
||||
'&&',
|
||||
'sc',
|
||||
'start',
|
||||
appHelperService,
|
||||
].join(' ');
|
||||
|
||||
final res = runas('cmd.exe', command);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
final retryStatus = await retry(
|
||||
task: checkService,
|
||||
maxAttempts: 5,
|
||||
retryIf: (status) => status != WindowsHelperServiceStatus.running,
|
||||
delay: const Duration(seconds: 1),
|
||||
);
|
||||
return res && retryStatus == WindowsHelperServiceStatus.running;
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> registerTask(String appName) async {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ Future<void> main() async {
|
||||
if (system.isDesktop) {
|
||||
await RustLib.init();
|
||||
}
|
||||
final version = await system.version;
|
||||
final version = await system.init();
|
||||
final container = await globalState.init(version);
|
||||
HttpOverrides.global = FlClashHttpOverrides();
|
||||
runApp(
|
||||
|
||||
@@ -3520,7 +3520,7 @@ as bool,
|
||||
/// @nodoc
|
||||
mixin _$ProfilesState {
|
||||
|
||||
List<Profile> get profiles; int? get currentProfileId; int get columns;
|
||||
List<Profile> get profiles; int? get currentProfileId;
|
||||
/// Create a copy of ProfilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -3531,16 +3531,16 @@ $ProfilesStateCopyWith<ProfilesState> get copyWith => _$ProfilesStateCopyWithImp
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfilesState&&const DeepCollectionEquality().equals(other.profiles, profiles)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfilesState&&const DeepCollectionEquality().equals(other.profiles, profiles)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(profiles),currentProfileId,columns);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(profiles),currentProfileId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProfilesState(profiles: $profiles, currentProfileId: $currentProfileId, columns: $columns)';
|
||||
return 'ProfilesState(profiles: $profiles, currentProfileId: $currentProfileId)';
|
||||
}
|
||||
|
||||
|
||||
@@ -3551,7 +3551,7 @@ abstract mixin class $ProfilesStateCopyWith<$Res> {
|
||||
factory $ProfilesStateCopyWith(ProfilesState value, $Res Function(ProfilesState) _then) = _$ProfilesStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Profile> profiles, int? currentProfileId, int columns
|
||||
List<Profile> profiles, int? currentProfileId
|
||||
});
|
||||
|
||||
|
||||
@@ -3568,12 +3568,11 @@ class _$ProfilesStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProfilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? profiles = null,Object? currentProfileId = freezed,Object? columns = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? profiles = null,Object? currentProfileId = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
profiles: null == profiles ? _self.profiles : profiles // ignore: cast_nullable_to_non_nullable
|
||||
as List<Profile>,currentProfileId: freezed == currentProfileId ? _self.currentProfileId : currentProfileId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -3658,10 +3657,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Profile> profiles, int? currentProfileId, int columns)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Profile> profiles, int? currentProfileId)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProfilesState() when $default != null:
|
||||
return $default(_that.profiles,_that.currentProfileId,_that.columns);case _:
|
||||
return $default(_that.profiles,_that.currentProfileId);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -3679,10 +3678,10 @@ return $default(_that.profiles,_that.currentProfileId,_that.columns);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Profile> profiles, int? currentProfileId, int columns) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Profile> profiles, int? currentProfileId) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProfilesState():
|
||||
return $default(_that.profiles,_that.currentProfileId,_that.columns);case _:
|
||||
return $default(_that.profiles,_that.currentProfileId);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -3699,10 +3698,10 @@ return $default(_that.profiles,_that.currentProfileId,_that.columns);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Profile> profiles, int? currentProfileId, int columns)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Profile> profiles, int? currentProfileId)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProfilesState() when $default != null:
|
||||
return $default(_that.profiles,_that.currentProfileId,_that.columns);case _:
|
||||
return $default(_that.profiles,_that.currentProfileId);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -3714,7 +3713,7 @@ return $default(_that.profiles,_that.currentProfileId,_that.columns);case _:
|
||||
|
||||
|
||||
class _ProfilesState implements ProfilesState {
|
||||
const _ProfilesState({required final List<Profile> profiles, required this.currentProfileId, required this.columns}): _profiles = profiles;
|
||||
const _ProfilesState({required final List<Profile> profiles, required this.currentProfileId}): _profiles = profiles;
|
||||
|
||||
|
||||
final List<Profile> _profiles;
|
||||
@@ -3725,7 +3724,6 @@ class _ProfilesState implements ProfilesState {
|
||||
}
|
||||
|
||||
@override final int? currentProfileId;
|
||||
@override final int columns;
|
||||
|
||||
/// Create a copy of ProfilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -3737,16 +3735,16 @@ _$ProfilesStateCopyWith<_ProfilesState> get copyWith => __$ProfilesStateCopyWith
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfilesState&&const DeepCollectionEquality().equals(other._profiles, _profiles)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfilesState&&const DeepCollectionEquality().equals(other._profiles, _profiles)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_profiles),currentProfileId,columns);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_profiles),currentProfileId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProfilesState(profiles: $profiles, currentProfileId: $currentProfileId, columns: $columns)';
|
||||
return 'ProfilesState(profiles: $profiles, currentProfileId: $currentProfileId)';
|
||||
}
|
||||
|
||||
|
||||
@@ -3757,7 +3755,7 @@ abstract mixin class _$ProfilesStateCopyWith<$Res> implements $ProfilesStateCopy
|
||||
factory _$ProfilesStateCopyWith(_ProfilesState value, $Res Function(_ProfilesState) _then) = __$ProfilesStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<Profile> profiles, int? currentProfileId, int columns
|
||||
List<Profile> profiles, int? currentProfileId
|
||||
});
|
||||
|
||||
|
||||
@@ -3774,12 +3772,11 @@ class __$ProfilesStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProfilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? profiles = null,Object? currentProfileId = freezed,Object? columns = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? profiles = null,Object? currentProfileId = freezed,}) {
|
||||
return _then(_ProfilesState(
|
||||
profiles: null == profiles ? _self._profiles : profiles // ignore: cast_nullable_to_non_nullable
|
||||
as List<Profile>,currentProfileId: freezed == currentProfileId ? _self.currentProfileId : currentProfileId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -5451,7 +5448,7 @@ as List<NavigationItem>,
|
||||
/// @nodoc
|
||||
mixin _$ProxiesListState {
|
||||
|
||||
List<Group> get groups; Set<String> get currentUnfoldSet; ProxyCardType get proxyCardType; int get columns;
|
||||
List<Group> get groups; Set<String> get currentUnfoldSet; ProxyCardType get proxyCardType;
|
||||
/// Create a copy of ProxiesListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -5462,16 +5459,16 @@ $ProxiesListStateCopyWith<ProxiesListState> get copyWith => _$ProxiesListStateCo
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesListState&&const DeepCollectionEquality().equals(other.groups, groups)&&const DeepCollectionEquality().equals(other.currentUnfoldSet, currentUnfoldSet)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesListState&&const DeepCollectionEquality().equals(other.groups, groups)&&const DeepCollectionEquality().equals(other.currentUnfoldSet, currentUnfoldSet)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),const DeepCollectionEquality().hash(currentUnfoldSet),proxyCardType,columns);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),const DeepCollectionEquality().hash(currentUnfoldSet),proxyCardType);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxyCardType: $proxyCardType, columns: $columns)';
|
||||
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxyCardType: $proxyCardType)';
|
||||
}
|
||||
|
||||
|
||||
@@ -5482,7 +5479,7 @@ abstract mixin class $ProxiesListStateCopyWith<$Res> {
|
||||
factory $ProxiesListStateCopyWith(ProxiesListState value, $Res Function(ProxiesListState) _then) = _$ProxiesListStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns
|
||||
List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType
|
||||
});
|
||||
|
||||
|
||||
@@ -5499,13 +5496,12 @@ class _$ProxiesListStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProxiesListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxyCardType = null,Object? columns = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxyCardType = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,currentUnfoldSet: null == currentUnfoldSet ? _self.currentUnfoldSet : currentUnfoldSet // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
|
||||
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as ProxyCardType,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -5590,10 +5586,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxiesListState() when $default != null:
|
||||
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.columns);case _:
|
||||
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -5611,10 +5607,10 @@ return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.co
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxiesListState():
|
||||
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.columns);case _:
|
||||
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -5631,10 +5627,10 @@ return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.co
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxiesListState() when $default != null:
|
||||
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.columns);case _:
|
||||
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -5646,7 +5642,7 @@ return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.co
|
||||
|
||||
|
||||
class _ProxiesListState implements ProxiesListState {
|
||||
const _ProxiesListState({required final List<Group> groups, required final Set<String> currentUnfoldSet, required this.proxyCardType, required this.columns}): _groups = groups,_currentUnfoldSet = currentUnfoldSet;
|
||||
const _ProxiesListState({required final List<Group> groups, required final Set<String> currentUnfoldSet, required this.proxyCardType}): _groups = groups,_currentUnfoldSet = currentUnfoldSet;
|
||||
|
||||
|
||||
final List<Group> _groups;
|
||||
@@ -5664,7 +5660,6 @@ class _ProxiesListState implements ProxiesListState {
|
||||
}
|
||||
|
||||
@override final ProxyCardType proxyCardType;
|
||||
@override final int columns;
|
||||
|
||||
/// Create a copy of ProxiesListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -5676,16 +5671,16 @@ _$ProxiesListStateCopyWith<_ProxiesListState> get copyWith => __$ProxiesListStat
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesListState&&const DeepCollectionEquality().equals(other._groups, _groups)&&const DeepCollectionEquality().equals(other._currentUnfoldSet, _currentUnfoldSet)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesListState&&const DeepCollectionEquality().equals(other._groups, _groups)&&const DeepCollectionEquality().equals(other._currentUnfoldSet, _currentUnfoldSet)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),const DeepCollectionEquality().hash(_currentUnfoldSet),proxyCardType,columns);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),const DeepCollectionEquality().hash(_currentUnfoldSet),proxyCardType);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxyCardType: $proxyCardType, columns: $columns)';
|
||||
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxyCardType: $proxyCardType)';
|
||||
}
|
||||
|
||||
|
||||
@@ -5696,7 +5691,7 @@ abstract mixin class _$ProxiesListStateCopyWith<$Res> implements $ProxiesListSta
|
||||
factory _$ProxiesListStateCopyWith(_ProxiesListState value, $Res Function(_ProxiesListState) _then) = __$ProxiesListStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns
|
||||
List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType
|
||||
});
|
||||
|
||||
|
||||
@@ -5713,13 +5708,12 @@ class __$ProxiesListStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProxiesListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxyCardType = null,Object? columns = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxyCardType = null,}) {
|
||||
return _then(_ProxiesListState(
|
||||
groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,currentUnfoldSet: null == currentUnfoldSet ? _self._currentUnfoldSet : currentUnfoldSet // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
|
||||
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as ProxyCardType,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -5729,7 +5723,7 @@ as int,
|
||||
/// @nodoc
|
||||
mixin _$ProxiesTabState {
|
||||
|
||||
List<Group> get groups; String? get currentGroupName; ProxyCardType get proxyCardType; int get columns;
|
||||
List<Group> get groups; String? get currentGroupName; ProxyCardType get proxyCardType;
|
||||
/// Create a copy of ProxiesTabState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -5740,16 +5734,16 @@ $ProxiesTabStateCopyWith<ProxiesTabState> get copyWith => _$ProxiesTabStateCopyW
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesTabState&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesTabState&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),currentGroupName,proxyCardType,columns);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),currentGroupName,proxyCardType);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxyCardType: $proxyCardType, columns: $columns)';
|
||||
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxyCardType: $proxyCardType)';
|
||||
}
|
||||
|
||||
|
||||
@@ -5760,7 +5754,7 @@ abstract mixin class $ProxiesTabStateCopyWith<$Res> {
|
||||
factory $ProxiesTabStateCopyWith(ProxiesTabState value, $Res Function(ProxiesTabState) _then) = _$ProxiesTabStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns
|
||||
List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType
|
||||
});
|
||||
|
||||
|
||||
@@ -5777,13 +5771,12 @@ class _$ProxiesTabStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProxiesTabState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxyCardType = null,Object? columns = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxyCardType = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
|
||||
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as ProxyCardType,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -5868,10 +5861,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxiesTabState() when $default != null:
|
||||
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.columns);case _:
|
||||
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -5889,10 +5882,10 @@ return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.co
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxiesTabState():
|
||||
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.columns);case _:
|
||||
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -5909,10 +5902,10 @@ return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.co
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxiesTabState() when $default != null:
|
||||
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.columns);case _:
|
||||
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -5924,7 +5917,7 @@ return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.co
|
||||
|
||||
|
||||
class _ProxiesTabState implements ProxiesTabState {
|
||||
const _ProxiesTabState({required final List<Group> groups, required this.currentGroupName, required this.proxyCardType, required this.columns}): _groups = groups;
|
||||
const _ProxiesTabState({required final List<Group> groups, required this.currentGroupName, required this.proxyCardType}): _groups = groups;
|
||||
|
||||
|
||||
final List<Group> _groups;
|
||||
@@ -5936,7 +5929,6 @@ class _ProxiesTabState implements ProxiesTabState {
|
||||
|
||||
@override final String? currentGroupName;
|
||||
@override final ProxyCardType proxyCardType;
|
||||
@override final int columns;
|
||||
|
||||
/// Create a copy of ProxiesTabState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -5948,16 +5940,16 @@ _$ProxiesTabStateCopyWith<_ProxiesTabState> get copyWith => __$ProxiesTabStateCo
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesTabState&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesTabState&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),currentGroupName,proxyCardType,columns);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),currentGroupName,proxyCardType);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxyCardType: $proxyCardType, columns: $columns)';
|
||||
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxyCardType: $proxyCardType)';
|
||||
}
|
||||
|
||||
|
||||
@@ -5968,7 +5960,7 @@ abstract mixin class _$ProxiesTabStateCopyWith<$Res> implements $ProxiesTabState
|
||||
factory _$ProxiesTabStateCopyWith(_ProxiesTabState value, $Res Function(_ProxiesTabState) _then) = __$ProxiesTabStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns
|
||||
List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType
|
||||
});
|
||||
|
||||
|
||||
@@ -5985,13 +5977,12 @@ class __$ProxiesTabStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProxiesTabState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxyCardType = null,Object? columns = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxyCardType = null,}) {
|
||||
return _then(_ProxiesTabState(
|
||||
groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
|
||||
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as ProxyCardType,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -6001,7 +5992,7 @@ as int,
|
||||
/// @nodoc
|
||||
mixin _$ProxyGroupSelectorState {
|
||||
|
||||
String? get testUrl; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; GroupType get groupType; List<Proxy> get proxies; int get columns;
|
||||
String? get testUrl; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; GroupType get groupType; List<Proxy> get proxies;
|
||||
/// Create a copy of ProxyGroupSelectorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -6012,16 +6003,16 @@ $ProxyGroupSelectorStateCopyWith<ProxyGroupSelectorState> get copyWith => _$Prox
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyGroupSelectorState&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.groupType, groupType) || other.groupType == groupType)&&const DeepCollectionEquality().equals(other.proxies, proxies)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyGroupSelectorState&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.groupType, groupType) || other.groupType == groupType)&&const DeepCollectionEquality().equals(other.proxies, proxies));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,testUrl,proxiesSortType,proxyCardType,sortNum,groupType,const DeepCollectionEquality().hash(proxies),columns);
|
||||
int get hashCode => Object.hash(runtimeType,testUrl,proxiesSortType,proxyCardType,sortNum,groupType,const DeepCollectionEquality().hash(proxies));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxyGroupSelectorState(testUrl: $testUrl, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, groupType: $groupType, proxies: $proxies, columns: $columns)';
|
||||
return 'ProxyGroupSelectorState(testUrl: $testUrl, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, groupType: $groupType, proxies: $proxies)';
|
||||
}
|
||||
|
||||
|
||||
@@ -6032,7 +6023,7 @@ abstract mixin class $ProxyGroupSelectorStateCopyWith<$Res> {
|
||||
factory $ProxyGroupSelectorStateCopyWith(ProxyGroupSelectorState value, $Res Function(ProxyGroupSelectorState) _then) = _$ProxyGroupSelectorStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns
|
||||
String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies
|
||||
});
|
||||
|
||||
|
||||
@@ -6049,7 +6040,7 @@ class _$ProxyGroupSelectorStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProxyGroupSelectorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? testUrl = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? groupType = null,Object? proxies = null,Object? columns = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? testUrl = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? groupType = null,Object? proxies = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable
|
||||
@@ -6057,8 +6048,7 @@ as ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType :
|
||||
as ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as num,groupType: null == groupType ? _self.groupType : groupType // ignore: cast_nullable_to_non_nullable
|
||||
as GroupType,proxies: null == proxies ? _self.proxies : proxies // ignore: cast_nullable_to_non_nullable
|
||||
as List<Proxy>,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as List<Proxy>,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -6143,10 +6133,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxyGroupSelectorState() when $default != null:
|
||||
return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies,_that.columns);case _:
|
||||
return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -6164,10 +6154,10 @@ return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.so
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxyGroupSelectorState():
|
||||
return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies,_that.columns);case _:
|
||||
return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -6184,10 +6174,10 @@ return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.so
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ProxyGroupSelectorState() when $default != null:
|
||||
return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies,_that.columns);case _:
|
||||
return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -6199,7 +6189,7 @@ return $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.so
|
||||
|
||||
|
||||
class _ProxyGroupSelectorState implements ProxyGroupSelectorState {
|
||||
const _ProxyGroupSelectorState({required this.testUrl, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.groupType, required final List<Proxy> proxies, required this.columns}): _proxies = proxies;
|
||||
const _ProxyGroupSelectorState({required this.testUrl, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.groupType, required final List<Proxy> proxies}): _proxies = proxies;
|
||||
|
||||
|
||||
@override final String? testUrl;
|
||||
@@ -6214,7 +6204,6 @@ class _ProxyGroupSelectorState implements ProxyGroupSelectorState {
|
||||
return EqualUnmodifiableListView(_proxies);
|
||||
}
|
||||
|
||||
@override final int columns;
|
||||
|
||||
/// Create a copy of ProxyGroupSelectorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -6226,16 +6215,16 @@ _$ProxyGroupSelectorStateCopyWith<_ProxyGroupSelectorState> get copyWith => __$P
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyGroupSelectorState&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.groupType, groupType) || other.groupType == groupType)&&const DeepCollectionEquality().equals(other._proxies, _proxies)&&(identical(other.columns, columns) || other.columns == columns));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyGroupSelectorState&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.groupType, groupType) || other.groupType == groupType)&&const DeepCollectionEquality().equals(other._proxies, _proxies));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,testUrl,proxiesSortType,proxyCardType,sortNum,groupType,const DeepCollectionEquality().hash(_proxies),columns);
|
||||
int get hashCode => Object.hash(runtimeType,testUrl,proxiesSortType,proxyCardType,sortNum,groupType,const DeepCollectionEquality().hash(_proxies));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxyGroupSelectorState(testUrl: $testUrl, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, groupType: $groupType, proxies: $proxies, columns: $columns)';
|
||||
return 'ProxyGroupSelectorState(testUrl: $testUrl, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, groupType: $groupType, proxies: $proxies)';
|
||||
}
|
||||
|
||||
|
||||
@@ -6246,7 +6235,7 @@ abstract mixin class _$ProxyGroupSelectorStateCopyWith<$Res> implements $ProxyGr
|
||||
factory _$ProxyGroupSelectorStateCopyWith(_ProxyGroupSelectorState value, $Res Function(_ProxyGroupSelectorState) _then) = __$ProxyGroupSelectorStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns
|
||||
String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies
|
||||
});
|
||||
|
||||
|
||||
@@ -6263,7 +6252,7 @@ class __$ProxyGroupSelectorStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ProxyGroupSelectorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? testUrl = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? groupType = null,Object? proxies = null,Object? columns = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? testUrl = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? groupType = null,Object? proxies = null,}) {
|
||||
return _then(_ProxyGroupSelectorState(
|
||||
testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable
|
||||
@@ -6271,8 +6260,7 @@ as ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType :
|
||||
as ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as num,groupType: null == groupType ? _self.groupType : groupType // ignore: cast_nullable_to_non_nullable
|
||||
as GroupType,proxies: null == proxies ? _self._proxies : proxies // ignore: cast_nullable_to_non_nullable
|
||||
as List<Proxy>,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
as List<Proxy>,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -7624,7 +7612,7 @@ as int,
|
||||
/// @nodoc
|
||||
mixin _$DashboardState {
|
||||
|
||||
List<DashboardWidget> get dashboardWidgets; double get contentWidth;
|
||||
List<DashboardWidget> get dashboardWidgets;
|
||||
/// Create a copy of DashboardState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -7635,16 +7623,16 @@ $DashboardStateCopyWith<DashboardState> get copyWith => _$DashboardStateCopyWith
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardState&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.contentWidth, contentWidth) || other.contentWidth == contentWidth));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardState&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(dashboardWidgets),contentWidth);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(dashboardWidgets));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DashboardState(dashboardWidgets: $dashboardWidgets, contentWidth: $contentWidth)';
|
||||
return 'DashboardState(dashboardWidgets: $dashboardWidgets)';
|
||||
}
|
||||
|
||||
|
||||
@@ -7655,7 +7643,7 @@ abstract mixin class $DashboardStateCopyWith<$Res> {
|
||||
factory $DashboardStateCopyWith(DashboardState value, $Res Function(DashboardState) _then) = _$DashboardStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<DashboardWidget> dashboardWidgets, double contentWidth
|
||||
List<DashboardWidget> dashboardWidgets
|
||||
});
|
||||
|
||||
|
||||
@@ -7672,11 +7660,10 @@ class _$DashboardStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of DashboardState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? dashboardWidgets = null,Object? contentWidth = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? dashboardWidgets = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
dashboardWidgets: null == dashboardWidgets ? _self.dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable
|
||||
as List<DashboardWidget>,contentWidth: null == contentWidth ? _self.contentWidth : contentWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
as List<DashboardWidget>,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -7761,10 +7748,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets, double contentWidth)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DashboardState() when $default != null:
|
||||
return $default(_that.dashboardWidgets,_that.contentWidth);case _:
|
||||
return $default(_that.dashboardWidgets);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -7782,10 +7769,10 @@ return $default(_that.dashboardWidgets,_that.contentWidth);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets, double contentWidth) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DashboardState():
|
||||
return $default(_that.dashboardWidgets,_that.contentWidth);case _:
|
||||
return $default(_that.dashboardWidgets);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -7802,10 +7789,10 @@ return $default(_that.dashboardWidgets,_that.contentWidth);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<DashboardWidget> dashboardWidgets, double contentWidth)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<DashboardWidget> dashboardWidgets)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DashboardState() when $default != null:
|
||||
return $default(_that.dashboardWidgets,_that.contentWidth);case _:
|
||||
return $default(_that.dashboardWidgets);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -7817,7 +7804,7 @@ return $default(_that.dashboardWidgets,_that.contentWidth);case _:
|
||||
|
||||
|
||||
class _DashboardState implements DashboardState {
|
||||
const _DashboardState({required final List<DashboardWidget> dashboardWidgets, required this.contentWidth}): _dashboardWidgets = dashboardWidgets;
|
||||
const _DashboardState({required final List<DashboardWidget> dashboardWidgets}): _dashboardWidgets = dashboardWidgets;
|
||||
|
||||
|
||||
final List<DashboardWidget> _dashboardWidgets;
|
||||
@@ -7827,7 +7814,6 @@ class _DashboardState implements DashboardState {
|
||||
return EqualUnmodifiableListView(_dashboardWidgets);
|
||||
}
|
||||
|
||||
@override final double contentWidth;
|
||||
|
||||
/// Create a copy of DashboardState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -7839,16 +7825,16 @@ _$DashboardStateCopyWith<_DashboardState> get copyWith => __$DashboardStateCopyW
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardState&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.contentWidth, contentWidth) || other.contentWidth == contentWidth));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardState&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_dashboardWidgets),contentWidth);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_dashboardWidgets));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DashboardState(dashboardWidgets: $dashboardWidgets, contentWidth: $contentWidth)';
|
||||
return 'DashboardState(dashboardWidgets: $dashboardWidgets)';
|
||||
}
|
||||
|
||||
|
||||
@@ -7859,7 +7845,7 @@ abstract mixin class _$DashboardStateCopyWith<$Res> implements $DashboardStateCo
|
||||
factory _$DashboardStateCopyWith(_DashboardState value, $Res Function(_DashboardState) _then) = __$DashboardStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<DashboardWidget> dashboardWidgets, double contentWidth
|
||||
List<DashboardWidget> dashboardWidgets
|
||||
});
|
||||
|
||||
|
||||
@@ -7876,11 +7862,10 @@ class __$DashboardStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of DashboardState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? dashboardWidgets = null,Object? contentWidth = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? dashboardWidgets = null,}) {
|
||||
return _then(_DashboardState(
|
||||
dashboardWidgets: null == dashboardWidgets ? _self._dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable
|
||||
as List<DashboardWidget>,contentWidth: null == contentWidth ? _self.contentWidth : contentWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
as List<DashboardWidget>,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ abstract class ProfilesState with _$ProfilesState {
|
||||
const factory ProfilesState({
|
||||
required List<Profile> profiles,
|
||||
required int? currentProfileId,
|
||||
required int columns,
|
||||
}) = _ProfilesState;
|
||||
}
|
||||
|
||||
@@ -173,7 +172,6 @@ abstract class ProxiesListState with _$ProxiesListState {
|
||||
required List<Group> groups,
|
||||
required Set<String> currentUnfoldSet,
|
||||
required ProxyCardType proxyCardType,
|
||||
required int columns,
|
||||
}) = _ProxiesListState;
|
||||
}
|
||||
|
||||
@@ -183,7 +181,6 @@ abstract class ProxiesTabState with _$ProxiesTabState {
|
||||
required List<Group> groups,
|
||||
required String? currentGroupName,
|
||||
required ProxyCardType proxyCardType,
|
||||
required int columns,
|
||||
}) = _ProxiesTabState;
|
||||
}
|
||||
|
||||
@@ -196,7 +193,6 @@ abstract class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
|
||||
required num sortNum,
|
||||
required GroupType groupType,
|
||||
required List<Proxy> proxies,
|
||||
required int columns,
|
||||
}) = _ProxyGroupSelectorState;
|
||||
}
|
||||
|
||||
@@ -279,7 +275,6 @@ abstract class ProxyState with _$ProxyState {
|
||||
abstract class DashboardState with _$DashboardState {
|
||||
const factory DashboardState({
|
||||
required List<DashboardWidget> dashboardWidgets,
|
||||
required double contentWidth,
|
||||
}) = _DashboardState;
|
||||
}
|
||||
|
||||
|
||||
+43
-17
@@ -49,21 +49,25 @@ class HomePage extends StatelessWidget {
|
||||
selectedIndex: currentIndex,
|
||||
),
|
||||
);
|
||||
if (isMobile) {
|
||||
return Column(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
return Column(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: FocusTraversalGroup(
|
||||
policy: PageTraversalPolicy(),
|
||||
child: MediaQuery.removePadding(
|
||||
removeTop: false,
|
||||
removeBottom: true,
|
||||
removeLeft: true,
|
||||
removeRight: true,
|
||||
removeBottom: isMobile,
|
||||
removeLeft: isMobile,
|
||||
removeRight: isMobile,
|
||||
context: context,
|
||||
child: child!,
|
||||
),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
),
|
||||
AnimatedVisibility.bottomNavigation(
|
||||
visible: isMobile,
|
||||
child: MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: false,
|
||||
removeLeft: true,
|
||||
@@ -71,11 +75,9 @@ class HomePage extends StatelessWidget {
|
||||
context: context,
|
||||
child: bottomNavigationBar,
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return child!;
|
||||
}
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
@@ -88,12 +90,17 @@ class HomePage extends StatelessWidget {
|
||||
pageBuilder: (_, index) {
|
||||
final navigationItem = navigationItems[index];
|
||||
final navigationView = navigationItem.builder(context);
|
||||
final scopedView = PageFocusScope(child: navigationView);
|
||||
final view = KeepScope(
|
||||
key: ValueKey(navigationItem.label),
|
||||
keep: navigationItem.keep,
|
||||
child: isMobile
|
||||
? navigationView
|
||||
? scopedView
|
||||
: Navigator(
|
||||
pages: [MaterialPage(child: navigationView)],
|
||||
key: ValueKey(
|
||||
'${navigationItem.label.name}_navigator',
|
||||
),
|
||||
pages: [MaterialPage(child: scopedView)],
|
||||
onDidRemovePage: (_) {},
|
||||
),
|
||||
);
|
||||
@@ -191,12 +198,31 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
|
||||
final itemCount = ref.watch(
|
||||
currentNavigationItemsStateProvider.select((state) => state.value.length),
|
||||
);
|
||||
final currentIndex = ref.watch(
|
||||
currentPageLabelProvider.select(
|
||||
(label) =>
|
||||
widget.navigationItems.indexWhere((item) => item.label == label),
|
||||
),
|
||||
);
|
||||
return PageView.builder(
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: itemCount,
|
||||
findChildIndexCallback: (key) {
|
||||
if (key is! ValueKey<PageLabel>) {
|
||||
return null;
|
||||
}
|
||||
final index = widget.navigationItems.indexWhere(
|
||||
(item) => item.label == key.value,
|
||||
);
|
||||
return index == -1 ? null : index;
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
return widget.pageBuilder(context, index);
|
||||
return ExcludeFocus(
|
||||
key: ValueKey(widget.navigationItems[index].label),
|
||||
excluding: index != currentIndex,
|
||||
child: widget.pageBuilder(context, index),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -395,47 +395,6 @@ final class NavigationStateProvider
|
||||
|
||||
String _$navigationStateHash() => r'657dc47ecc35ba0807b58cb37e7f1baa14f6c2f9';
|
||||
|
||||
@ProviderFor(contentWidth)
|
||||
final contentWidthProvider = ContentWidthProvider._();
|
||||
|
||||
final class ContentWidthProvider
|
||||
extends $FunctionalProvider<double, double, double>
|
||||
with $Provider<double> {
|
||||
ContentWidthProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'contentWidthProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$contentWidthHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<double> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
double create(Ref ref) {
|
||||
return contentWidth(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(double value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<double>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$contentWidthHash() => r'4710fee8f91b08dc88520798fb6b1d4f61f090e6';
|
||||
|
||||
@ProviderFor(dashboardState)
|
||||
final dashboardStateProvider = DashboardStateProvider._();
|
||||
|
||||
@@ -475,7 +434,7 @@ final class DashboardStateProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$dashboardStateHash() => r'e8616e327c1b96658d917e4e4950f70ecb099d85';
|
||||
String _$dashboardStateHash() => r'33838f85f2b6a0ab601891aa2f26adc8870302b6';
|
||||
|
||||
@ProviderFor(proxiesActionsState)
|
||||
final proxiesActionsStateProvider = ProxiesActionsStateProvider._();
|
||||
@@ -564,7 +523,7 @@ final class ProfilesStateProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$profilesStateHash() => r'8b07eeacb83b9002ba7e6283ff7a7f451a0845a6';
|
||||
String _$profilesStateHash() => r'6bcfd61de84c930251ade72b9fe804c4f5ac2be9';
|
||||
|
||||
@ProviderFor(filterGroupsState)
|
||||
final filterGroupsStateProvider = FilterGroupsStateFamily._();
|
||||
@@ -687,7 +646,7 @@ final class ProxiesListStateProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxiesListStateHash() => r'4d83ff3e3d337c9a6eadddfa747c11939676b8bd';
|
||||
String _$proxiesListStateHash() => r'212d21f79e9c149076e13d1d19ccd83ccb4b471b';
|
||||
|
||||
@ProviderFor(proxiesTabState)
|
||||
final proxiesTabStateProvider = ProxiesTabStateProvider._();
|
||||
@@ -729,7 +688,7 @@ final class ProxiesTabStateProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxiesTabStateHash() => r'b16734b2578c4e9a91880af8af41faa2e293b257';
|
||||
String _$proxiesTabStateHash() => r'e4eccd77c3848489c8ec620f4e515cec7cdd5a31';
|
||||
|
||||
@ProviderFor(isStart)
|
||||
final isStartProvider = IsStartProvider._();
|
||||
@@ -884,7 +843,7 @@ final class ProxyGroupSelectorStateProvider
|
||||
}
|
||||
|
||||
String _$proxyGroupSelectorStateHash() =>
|
||||
r'd393702a1fea16f8ab0362c2888b70300795e829';
|
||||
r'26326b400a0a2570188560a553ba8c600ad13b80';
|
||||
|
||||
final class ProxyGroupSelectorStateFamily extends $Family
|
||||
with $FunctionalFamilyOverride<ProxyGroupSelectorState, (String, String)> {
|
||||
@@ -1463,46 +1422,6 @@ final class CurrentProfileProvider
|
||||
|
||||
String _$currentProfileHash() => r'55f3cb9570a0aa6b9e0b83a36693b69d52e753ab';
|
||||
|
||||
@ProviderFor(proxiesColumns)
|
||||
final proxiesColumnsProvider = ProxiesColumnsProvider._();
|
||||
|
||||
final class ProxiesColumnsProvider extends $FunctionalProvider<int, int, int>
|
||||
with $Provider<int> {
|
||||
ProxiesColumnsProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'proxiesColumnsProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$proxiesColumnsHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<int> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
int create(Ref ref) {
|
||||
return proxiesColumns(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(int value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<int>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxiesColumnsHash() => r'e93f980da3d857bd38957635ef944a0db60aaef3';
|
||||
|
||||
@ProviderFor(realSelectedProxyState)
|
||||
final realSelectedProxyStateProvider = RealSelectedProxyStateFamily._();
|
||||
|
||||
@@ -2041,7 +1960,7 @@ final class AutoSetSystemDnsStateProvider
|
||||
}
|
||||
|
||||
String _$autoSetSystemDnsStateHash() =>
|
||||
r'a8805965efe78241613bfde55f4ea8fa12a6ea32';
|
||||
r'60135ce263c5c31200006f4a9f342f8241a872cc';
|
||||
|
||||
@ProviderFor(needUpdateGroups)
|
||||
final needUpdateGroupsProvider = NeedUpdateGroupsProvider._();
|
||||
@@ -3021,7 +2940,7 @@ abstract class _$AccessControlState extends $Notifier<AccessControlProps> {
|
||||
AccessControlProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
WhenComplete runBuild() {
|
||||
final ref = this.ref as $Ref<AccessControlProps, AccessControlProps>;
|
||||
final element =
|
||||
ref.element
|
||||
@@ -3031,7 +2950,7 @@ abstract class _$AccessControlState extends $Notifier<AccessControlProps> {
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
return element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3074,7 +2993,7 @@ abstract class _$ProxyGroupProvider extends $Notifier<ProxyGroup> {
|
||||
ProxyGroup build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
WhenComplete runBuild() {
|
||||
final ref = this.ref as $Ref<ProxyGroup, ProxyGroup>;
|
||||
final element =
|
||||
ref.element
|
||||
@@ -3084,7 +3003,7 @@ abstract class _$ProxyGroupProvider extends $Notifier<ProxyGroup> {
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
return element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3125,7 +3044,7 @@ abstract class _$RuleProvider extends $Notifier<Rule> {
|
||||
Rule build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
WhenComplete runBuild() {
|
||||
final ref = this.ref as $Ref<Rule, Rule>;
|
||||
final element =
|
||||
ref.element
|
||||
@@ -3135,7 +3054,7 @@ abstract class _$RuleProvider extends $Notifier<Rule> {
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
return element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,23 +195,12 @@ NavigationState navigationState(Ref ref) {
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
double contentWidth(Ref ref) {
|
||||
final viewWidth = ref.watch(viewWidthProvider);
|
||||
final sideWidth = ref.watch(sideWidthProvider);
|
||||
return viewWidth - sideWidth;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
DashboardState dashboardState(Ref ref) {
|
||||
final dashboardWidgets = ref.watch(
|
||||
appSettingProvider.select((state) => state.dashboardWidgets),
|
||||
);
|
||||
final contentWidth = ref.watch(contentWidthProvider);
|
||||
return DashboardState(
|
||||
dashboardWidgets: dashboardWidgets,
|
||||
contentWidth: contentWidth,
|
||||
);
|
||||
return DashboardState(dashboardWidgets: dashboardWidgets);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -234,14 +223,7 @@ ProxiesActionsState proxiesActionsState(Ref ref) {
|
||||
ProfilesState profilesState(Ref ref) {
|
||||
final currentProfileId = ref.watch(currentProfileIdProvider);
|
||||
final profiles = ref.watch(profilesProvider);
|
||||
final columns = ref.watch(
|
||||
contentWidthProvider.select((state) => utils.getProfilesColumns(state)),
|
||||
);
|
||||
return ProfilesState(
|
||||
profiles: profiles,
|
||||
currentProfileId: currentProfileId,
|
||||
columns: columns,
|
||||
);
|
||||
return ProfilesState(profiles: profiles, currentProfileId: currentProfileId);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -272,13 +254,10 @@ ProxiesListState proxiesListState(Ref ref) {
|
||||
final cardType = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.cardType),
|
||||
);
|
||||
|
||||
final columns = ref.watch(proxiesColumnsProvider);
|
||||
return ProxiesListState(
|
||||
groups: currentGroups.value,
|
||||
currentUnfoldSet: currentUnfoldSet,
|
||||
proxyCardType: cardType,
|
||||
columns: columns,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -292,12 +271,10 @@ ProxiesTabState proxiesTabState(Ref ref) {
|
||||
final cardType = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.cardType),
|
||||
);
|
||||
final columns = ref.watch(proxiesColumnsProvider);
|
||||
return ProxiesTabState(
|
||||
groups: currentGroups.value,
|
||||
currentGroupName: currentGroupName,
|
||||
proxyCardType: cardType,
|
||||
columns: columns,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -331,7 +308,6 @@ ProxyGroupSelectorState proxyGroupSelectorState(
|
||||
),
|
||||
);
|
||||
final sortNum = ref.watch(sortNumProvider);
|
||||
final columns = ref.watch(proxiesColumnsProvider);
|
||||
final lowQuery = query.toLowerCase();
|
||||
final proxies =
|
||||
group?.all.where((item) {
|
||||
@@ -345,7 +321,6 @@ ProxyGroupSelectorState proxyGroupSelectorState(
|
||||
sortNum: sortNum,
|
||||
groupType: group?.type ?? GroupType.Selector,
|
||||
proxies: proxies,
|
||||
columns: columns,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -457,15 +432,6 @@ Profile? currentProfile(Ref ref) {
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
int proxiesColumns(Ref ref) {
|
||||
final contentWidth = ref.watch(contentWidthProvider);
|
||||
final proxiesLayout = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.layout),
|
||||
);
|
||||
return utils.getProxiesColumns(contentWidth, proxiesLayout);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
SelectedProxyState realSelectedProxyState(Ref ref, String proxyName) {
|
||||
final groups = ref.watch(groupsProvider);
|
||||
@@ -570,11 +536,16 @@ Brightness currentBrightness(Ref ref) {
|
||||
@riverpod
|
||||
VM2<bool, bool> autoSetSystemDnsState(Ref ref) {
|
||||
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
|
||||
final realTunEnable = ref.watch(realTunEnableProvider);
|
||||
final tunEnable = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.tun.enable),
|
||||
);
|
||||
final authorizationState = ref.watch(authorizedTunEnableProvider);
|
||||
final autoSetSystemDns = ref.watch(
|
||||
networkSettingProvider.select((state) => state.autoSetSystemDns),
|
||||
);
|
||||
return VM2(isStart ? realTunEnable : false, autoSetSystemDns);
|
||||
final effectiveTunEnable =
|
||||
tunEnable && authorizationState == TunAuthorizationState.authorized;
|
||||
return VM2(isStart ? effectiveTunEnable : false, autoSetSystemDns);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:defer_pointer/defer_pointer.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'widgets/core_status_button.dart';
|
||||
import 'widgets/start_button.dart';
|
||||
|
||||
typedef _IsEditWidgetBuilder = Widget Function(bool isEdit);
|
||||
|
||||
const _maxCrossAxisCount = 12;
|
||||
const _maxGridWidth = 280.0 * _maxCrossAxisCount / 4;
|
||||
|
||||
class DashboardView extends ConsumerStatefulWidget {
|
||||
const DashboardView({super.key});
|
||||
|
||||
@@ -42,114 +45,9 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleConnection() async {
|
||||
final coreStatus = ref.read(coreStatusProvider);
|
||||
if (coreStatus == CoreStatus.connecting) {
|
||||
return;
|
||||
}
|
||||
final tip = coreStatus == CoreStatus.connected
|
||||
? context.appLocalizations.forceRestartCoreTip
|
||||
: context.appLocalizations.restartCoreTip;
|
||||
final res = await globalState.showMessage(message: TextSpan(text: tip));
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
globalState.container.read(coreActionProvider.notifier).restartCore();
|
||||
}
|
||||
|
||||
List<Widget> _buildActions(bool isEdit) {
|
||||
final appLocalizations = context.appLocalizations;
|
||||
return [
|
||||
if (!isEdit)
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final coreStatus = ref.watch(coreStatusProvider);
|
||||
return Tooltip(
|
||||
message: appLocalizations.coreStatus,
|
||||
child: FadeScaleBox(
|
||||
alignment: Alignment.centerRight,
|
||||
child: coreStatus == CoreStatus.connected
|
||||
? IconButton.filled(
|
||||
visualDensity: VisualDensity.compact,
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.zero,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.green.harmonizeWith(
|
||||
context.colorScheme.primary,
|
||||
),
|
||||
foregroundColor: switch (Theme.brightnessOf(
|
||||
context,
|
||||
)) {
|
||||
Brightness.light =>
|
||||
context.colorScheme.onSurfaceVariant,
|
||||
Brightness.dark =>
|
||||
context.colorScheme.onPrimaryFixedVariant,
|
||||
},
|
||||
),
|
||||
onPressed: _handleConnection,
|
||||
icon: const Icon(
|
||||
Icons.check,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
)
|
||||
: FilledButton.icon(
|
||||
key: ValueKey(coreStatus),
|
||||
onPressed: _handleConnection,
|
||||
style: FilledButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
backgroundColor: switch (coreStatus) {
|
||||
CoreStatus.connecting => null,
|
||||
CoreStatus.connected => Colors.greenAccent,
|
||||
CoreStatus.disconnected =>
|
||||
context.colorScheme.error,
|
||||
},
|
||||
foregroundColor: switch (coreStatus) {
|
||||
CoreStatus.connecting => null,
|
||||
CoreStatus.connected => switch (Theme.brightnessOf(
|
||||
context,
|
||||
)) {
|
||||
Brightness.light =>
|
||||
context.colorScheme.onSurfaceVariant,
|
||||
Brightness.dark => null,
|
||||
},
|
||||
CoreStatus.disconnected =>
|
||||
context.colorScheme.onError,
|
||||
},
|
||||
),
|
||||
icon: SizedBox(
|
||||
height: globalState.measure.bodyMediumHeight,
|
||||
width: globalState.measure.bodyMediumHeight,
|
||||
child: switch (coreStatus) {
|
||||
CoreStatus.connecting => Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
color: context.colorScheme.onPrimary,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
),
|
||||
CoreStatus.connected => const Icon(
|
||||
Icons.check_sharp,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
CoreStatus.disconnected => const Icon(
|
||||
Icons.restart_alt_sharp,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
},
|
||||
),
|
||||
label: Text(switch (coreStatus) {
|
||||
CoreStatus.connecting => appLocalizations.connecting,
|
||||
CoreStatus.connected => appLocalizations.connected,
|
||||
CoreStatus.disconnected =>
|
||||
appLocalizations.disconnected,
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (!isEdit && coreLib == null) const CoreStatusButton(),
|
||||
if (isEdit)
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _addedWidgetsNotifier,
|
||||
@@ -216,23 +114,27 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
if (currentState == null) {
|
||||
return;
|
||||
}
|
||||
if (mounted && currentState.children.isNotEmpty) {
|
||||
await currentState.isTransformCompleter;
|
||||
final dashboardWidgets = currentState.children
|
||||
.map((item) => DashboardWidget.getDashboardWidget(item))
|
||||
.toList();
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.update(
|
||||
(state) => state.copyWith(dashboardWidgets: dashboardWidgets),
|
||||
);
|
||||
if (!mounted || currentState.children.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final transformCompleted = await currentState.isTransformCompleter;
|
||||
if (!transformCompleted ||
|
||||
!mounted ||
|
||||
!currentState.mounted ||
|
||||
!identical(key.currentState, currentState)) {
|
||||
return;
|
||||
}
|
||||
final dashboardWidgets = currentState.children
|
||||
.map((item) => DashboardWidget.getDashboardWidget(item))
|
||||
.toList();
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.update((state) => state.copyWith(dashboardWidgets: dashboardWidgets));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dashboardState = ref.watch(dashboardStateProvider);
|
||||
final columns = max(4 * ((dashboardState.contentWidth / 280).ceil()), 8);
|
||||
final spacing = 14.mAp;
|
||||
final children = [
|
||||
...dashboardState.dashboardWidgets
|
||||
@@ -260,31 +162,45 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16).copyWith(bottom: 88),
|
||||
child: isEdit
|
||||
? SystemBackBlock(
|
||||
child: CommonPopScope(
|
||||
child: SuperGrid(
|
||||
key: key,
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: children,
|
||||
onUpdate: () {
|
||||
_handleSave();
|
||||
},
|
||||
),
|
||||
onPop: (context) {
|
||||
_handleUpdateIsEdit();
|
||||
return false;
|
||||
},
|
||||
),
|
||||
)
|
||||
: Grid(
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: children,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: _maxGridWidth),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
final columns = min(
|
||||
max(4 * ((constraints.maxWidth / 280).ceil()), 8),
|
||||
_maxCrossAxisCount,
|
||||
);
|
||||
return isEdit
|
||||
? SystemBackBlock(
|
||||
child: CommonPopScope(
|
||||
child: SuperGrid(
|
||||
key: key,
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: children,
|
||||
onUpdate: () {
|
||||
_handleSave();
|
||||
},
|
||||
),
|
||||
onPop: (context) {
|
||||
_handleUpdateIsEdit();
|
||||
return false;
|
||||
},
|
||||
),
|
||||
)
|
||||
: Grid(
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -70,12 +70,10 @@ class OutboundMode extends StatelessWidget {
|
||||
left: 12.ap,
|
||||
right: 16.ap,
|
||||
),
|
||||
delegate: RadioDelegate(
|
||||
onTab: () {
|
||||
_handleChangeMode(item);
|
||||
},
|
||||
value: item,
|
||||
),
|
||||
onTap: () {
|
||||
_handleChangeMode(item);
|
||||
},
|
||||
value: item,
|
||||
title: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(
|
||||
|
||||
@@ -47,7 +47,7 @@ class ProxyCard extends StatelessWidget {
|
||||
height: measure.labelSmallHeight,
|
||||
width: measure.labelSmallHeight,
|
||||
child: delay == 0
|
||||
? const CircularProgressIndicator(strokeWidth: 2)
|
||||
? const CommonCircleLoading()
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.bolt),
|
||||
iconSize: globalState.measure.labelSmallHeight,
|
||||
|
||||
@@ -30,12 +30,6 @@ List<Group> getGroups() {
|
||||
return globalState.container.read(groupsProvider);
|
||||
}
|
||||
|
||||
String? getCurrentGroupName() {
|
||||
return globalState.container.read(
|
||||
currentProfileProvider.select((state) => state?.currentGroupName),
|
||||
);
|
||||
}
|
||||
|
||||
void updateCurrentGroupName(String groupName) {
|
||||
globalState.container
|
||||
.read(proxiesActionProvider.notifier)
|
||||
@@ -68,19 +62,31 @@ Future<void> proxyDelayTest(Proxy proxy, [String? testUrl]) async {
|
||||
ref
|
||||
.read(proxiesActionProvider.notifier)
|
||||
.setDelay(Delay(url: currentTestUrl, name: state.proxyName, value: 0));
|
||||
ref
|
||||
.read(proxiesActionProvider.notifier)
|
||||
.setDelay(await coreController.getDelay(currentTestUrl, state.proxyName));
|
||||
try {
|
||||
final delay = await coreController.getDelay(
|
||||
currentTestUrl,
|
||||
state.proxyName,
|
||||
);
|
||||
ref.read(proxiesActionProvider.notifier).setDelay(delay);
|
||||
} catch (error) {
|
||||
commonPrint.log(
|
||||
'Delay test failed for ${state.proxyName}: $error',
|
||||
logLevel: LogLevel.error,
|
||||
);
|
||||
ref
|
||||
.read(proxiesActionProvider.notifier)
|
||||
.setDelay(Delay(url: currentTestUrl, name: state.proxyName, value: -1));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> delayTest(List<Proxy> proxies, [String? testUrl]) async {
|
||||
final delayProxies = proxies.map<Future>((proxy) async {
|
||||
await proxyDelayTest(proxy, testUrl);
|
||||
}).toList();
|
||||
|
||||
final batchesDelayProxies = delayProxies.batch(100);
|
||||
for (final batchDelayProxies in batchesDelayProxies) {
|
||||
await Future.wait(batchDelayProxies);
|
||||
final batches = proxies.batch(100);
|
||||
for (final batch in batches) {
|
||||
await Future.wait(
|
||||
batch.map((proxy) async {
|
||||
await proxyDelayTest(proxy, testUrl);
|
||||
}),
|
||||
);
|
||||
}
|
||||
globalState.container.read(sortNumProvider.notifier).add();
|
||||
}
|
||||
@@ -88,9 +94,9 @@ Future<void> delayTest(List<Proxy> proxies, [String? testUrl]) async {
|
||||
double getScrollToSelectedOffset({
|
||||
required String groupName,
|
||||
required List<Proxy> proxies,
|
||||
required int columns,
|
||||
}) {
|
||||
final ref = globalState.container;
|
||||
final columns = ref.read(proxiesColumnsProvider);
|
||||
final proxyCardType = ref.read(
|
||||
proxiesStyleSettingProvider.select((state) => state.cardType),
|
||||
);
|
||||
|
||||
@@ -133,6 +133,7 @@ class ProviderItem extends StatelessWidget {
|
||||
runSpacing: 6,
|
||||
spacing: 12,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
CommonChip(
|
||||
avatar: const Icon(Icons.upload),
|
||||
@@ -151,7 +152,7 @@ class ProviderItem extends StatelessWidget {
|
||||
width: 30,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(2),
|
||||
child: CircularProgressIndicator(),
|
||||
child: CommonCircleLoading(),
|
||||
),
|
||||
)
|
||||
: CommonChip(
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const _transitionDuration = Duration(milliseconds: 300);
|
||||
|
||||
enum _VisibilityMotion {
|
||||
sidebar(
|
||||
axis: Axis.horizontal,
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
hiddenOffset: Offset(-1, 0),
|
||||
),
|
||||
bottomNavigation(
|
||||
axis: Axis.vertical,
|
||||
alignment: AlignmentDirectional.bottomStart,
|
||||
hiddenOffset: Offset(0, 1),
|
||||
);
|
||||
|
||||
final Axis axis;
|
||||
final AlignmentGeometry alignment;
|
||||
final Offset hiddenOffset;
|
||||
|
||||
const _VisibilityMotion({
|
||||
required this.axis,
|
||||
required this.alignment,
|
||||
required this.hiddenOffset,
|
||||
});
|
||||
}
|
||||
|
||||
/// Animates navigation visibility together with the surrounding layout.
|
||||
class AnimatedVisibility extends StatefulWidget {
|
||||
final bool visible;
|
||||
final _VisibilityMotion _motion;
|
||||
final Widget child;
|
||||
|
||||
/// Creates a sidebar transition that moves to the left when hidden.
|
||||
const AnimatedVisibility.sidebar({
|
||||
super.key,
|
||||
required this.visible,
|
||||
required this.child,
|
||||
}) : _motion = _VisibilityMotion.sidebar;
|
||||
|
||||
/// Creates a bottom-navigation transition that moves through the bottom.
|
||||
const AnimatedVisibility.bottomNavigation({
|
||||
super.key,
|
||||
required this.visible,
|
||||
required this.child,
|
||||
}) : _motion = _VisibilityMotion.bottomNavigation;
|
||||
|
||||
@override
|
||||
State<AnimatedVisibility> createState() => _AnimatedVisibilityState();
|
||||
}
|
||||
|
||||
class _AnimatedVisibilityState extends State<AnimatedVisibility>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final CurvedAnimation _animation;
|
||||
late Widget _presentedChild;
|
||||
late bool _includeChild;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_presentedChild = widget.child;
|
||||
_includeChild = widget.visible;
|
||||
_controller = AnimationController(
|
||||
value: widget.visible ? 1 : 0,
|
||||
duration: _transitionDuration,
|
||||
reverseDuration: _transitionDuration,
|
||||
vsync: this,
|
||||
)..addStatusListener(_handleAnimationStatus);
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Easing.standardDecelerate,
|
||||
reverseCurve: const FlippedCurve(Easing.standardAccelerate),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant AnimatedVisibility oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.visible == oldWidget.visible) {
|
||||
if (widget.visible) {
|
||||
_presentedChild = widget.child;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (widget.visible) {
|
||||
_presentedChild = widget.child;
|
||||
_includeChild = true;
|
||||
_controller.forward();
|
||||
return;
|
||||
}
|
||||
if (_controller.isDismissed) {
|
||||
_includeChild = false;
|
||||
return;
|
||||
}
|
||||
_controller.reverse();
|
||||
}
|
||||
|
||||
void _handleAnimationStatus(AnimationStatus status) {
|
||||
if (status != AnimationStatus.dismissed ||
|
||||
widget.visible ||
|
||||
!_includeChild) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_includeChild = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.removeStatusListener(_handleAnimationStatus);
|
||||
_animation.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final motion = widget._motion;
|
||||
final content = _includeChild
|
||||
? SizeTransition(
|
||||
sizeFactor: _animation,
|
||||
axis: motion.axis,
|
||||
alignment: motion.alignment,
|
||||
child: FadeTransition(
|
||||
opacity: _animation,
|
||||
child: SlideTransition(
|
||||
position: _animation.drive(
|
||||
Tween(begin: motion.hiddenOffset, end: Offset.zero),
|
||||
),
|
||||
child: _presentedChild,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
return ExcludeSemantics(
|
||||
excluding: !widget.visible,
|
||||
child: ExcludeFocus(
|
||||
excluding: !widget.visible,
|
||||
child: IgnorePointer(
|
||||
ignoring: !widget.visible,
|
||||
child: ClipRect(child: content),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,29 @@
|
||||
import 'package:fl_clash/common/system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FloatLayout extends StatelessWidget {
|
||||
final Widget floatingWidget;
|
||||
|
||||
final Widget child;
|
||||
final bool? isTV;
|
||||
|
||||
const FloatLayout({
|
||||
super.key,
|
||||
required this.floatingWidget,
|
||||
this.isTV,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isTV ?? system.isTV) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
floatingWidget,
|
||||
Expanded(child: Center(child: child)),
|
||||
],
|
||||
);
|
||||
}
|
||||
return Stack(
|
||||
fit: StackFit.loose,
|
||||
children: [
|
||||
|
||||
+333
-46
@@ -1,11 +1,34 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
|
||||
enum LoadingIndicatorM3EVariant { defaultStyle, contained }
|
||||
|
||||
class CommonCircleLoading extends StatefulWidget {
|
||||
static const double defaultDimension = 32;
|
||||
static const double defaultDimension = 48;
|
||||
|
||||
final LoadingIndicatorM3EVariant variant;
|
||||
final Color? color;
|
||||
final Color? containerColor;
|
||||
final List<StarBorder>? polygons;
|
||||
final BoxConstraints? constraints;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final String? semanticLabel;
|
||||
final String? semanticValue;
|
||||
|
||||
const CommonCircleLoading({super.key, this.color});
|
||||
const CommonCircleLoading({
|
||||
super.key,
|
||||
this.variant = LoadingIndicatorM3EVariant.defaultStyle,
|
||||
this.color,
|
||||
this.containerColor,
|
||||
this.polygons,
|
||||
this.constraints,
|
||||
this.padding,
|
||||
this.semanticLabel,
|
||||
this.semanticValue,
|
||||
}) : assert(polygons == null || polygons.length > 1);
|
||||
|
||||
@override
|
||||
State<CommonCircleLoading> createState() => _CommonCircleLoadingState();
|
||||
@@ -13,53 +36,195 @@ class CommonCircleLoading extends StatefulWidget {
|
||||
|
||||
class _CommonCircleLoadingState extends State<CommonCircleLoading>
|
||||
with TickerProviderStateMixin {
|
||||
late AnimationController _rotateController;
|
||||
late AnimationController _pointsController;
|
||||
late Animation<double> _pointsAnimation;
|
||||
static const _globalRotationDuration = Duration(milliseconds: 4666);
|
||||
static const _morphInterval = Duration(milliseconds: 650);
|
||||
static const _fullRotation = 360.0;
|
||||
static const _quarterRotation = _fullRotation / 4;
|
||||
static const _activeIndicatorScale = 38 / 48;
|
||||
static const _defaultConstraints = BoxConstraints.tightFor(
|
||||
width: CommonCircleLoading.defaultDimension,
|
||||
height: CommonCircleLoading.defaultDimension,
|
||||
);
|
||||
|
||||
static const List<_ShapeSpec> _defaultShapeSequence = [
|
||||
// Soft burst.
|
||||
_ShapeSpec(
|
||||
points: 10,
|
||||
innerRadiusRatio: 0.78,
|
||||
pointRounding: 0.55,
|
||||
valleyRounding: 0.35,
|
||||
),
|
||||
// Nine-sided cookie.
|
||||
_ShapeSpec(
|
||||
points: 9,
|
||||
innerRadiusRatio: 0.86,
|
||||
pointRounding: 0.62,
|
||||
valleyRounding: 0.28,
|
||||
),
|
||||
// Pentagon.
|
||||
_ShapeSpec(points: 5, innerRadiusRatio: 0.81, pointRounding: 0.25),
|
||||
// Horizontal pill.
|
||||
_ShapeSpec(
|
||||
points: 2,
|
||||
innerRadiusRatio: 1,
|
||||
pointRounding: 0.5,
|
||||
valleyRounding: 0.5,
|
||||
widthScale: 0.95,
|
||||
heightScale: 0.52,
|
||||
squash: 1,
|
||||
),
|
||||
// Sunny.
|
||||
_ShapeSpec(
|
||||
points: 8,
|
||||
innerRadiusRatio: 0.58,
|
||||
pointRounding: 0.52,
|
||||
valleyRounding: 0.18,
|
||||
),
|
||||
// Four-sided cookie.
|
||||
_ShapeSpec(
|
||||
points: 4,
|
||||
innerRadiusRatio: 0.78,
|
||||
pointRounding: 0.62,
|
||||
valleyRounding: 0.28,
|
||||
),
|
||||
// Vertical oval.
|
||||
_ShapeSpec(
|
||||
points: 2,
|
||||
innerRadiusRatio: 1,
|
||||
pointRounding: 0.5,
|
||||
valleyRounding: 0.5,
|
||||
widthScale: 0.62,
|
||||
heightScale: 0.95,
|
||||
squash: 1,
|
||||
),
|
||||
];
|
||||
|
||||
final SpringSimulation _morphAnimation = SpringSimulation(
|
||||
SpringDescription.withDampingRatio(mass: 1, stiffness: 200, ratio: 0.6),
|
||||
0,
|
||||
1,
|
||||
5,
|
||||
snapToEnd: true,
|
||||
);
|
||||
|
||||
late final AnimationController _morphController;
|
||||
late final AnimationController _globalRotationController;
|
||||
late final Listenable _animation;
|
||||
|
||||
var _currentMorphIndex = 0;
|
||||
var _morphRotationTargetAngle = _quarterRotation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_rotateController = AnimationController(
|
||||
duration: const Duration(seconds: 3),
|
||||
_morphController = AnimationController.unbounded(vsync: this);
|
||||
_globalRotationController = AnimationController(
|
||||
duration: _globalRotationDuration,
|
||||
vsync: this,
|
||||
)..repeat();
|
||||
|
||||
_pointsController = AnimationController(
|
||||
duration: const Duration(seconds: 1),
|
||||
vsync: this,
|
||||
)..repeat(reverse: true);
|
||||
|
||||
_pointsAnimation = Tween<double>(begin: 3.0, end: 9.0).animate(
|
||||
CurvedAnimation(parent: _pointsController, curve: Curves.easeInOut),
|
||||
);
|
||||
_animation = Listenable.merge([
|
||||
_morphController,
|
||||
_globalRotationController,
|
||||
]);
|
||||
unawaited(_runMorphLoop());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_rotateController.dispose();
|
||||
_pointsController.dispose();
|
||||
_morphController.dispose();
|
||||
_globalRotationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant CommonCircleLoading oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.polygons != oldWidget.polygons &&
|
||||
_currentMorphIndex >= _shapeCount) {
|
||||
_currentMorphIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = widget.color ?? Theme.of(context).colorScheme.primary;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final activeColor = switch (widget.variant) {
|
||||
LoadingIndicatorM3EVariant.defaultStyle =>
|
||||
widget.color ?? colorScheme.primary,
|
||||
LoadingIndicatorM3EVariant.contained =>
|
||||
widget.color ?? colorScheme.onPrimaryContainer,
|
||||
};
|
||||
final backgroundColor = switch (widget.variant) {
|
||||
LoadingIndicatorM3EVariant.defaultStyle =>
|
||||
widget.containerColor ?? Colors.transparent,
|
||||
LoadingIndicatorM3EVariant.contained =>
|
||||
widget.containerColor ?? colorScheme.primaryContainer,
|
||||
};
|
||||
final shapeSequence =
|
||||
widget.polygons
|
||||
?.map(_ShapeSpec.fromStarBorder)
|
||||
.toList(growable: false) ??
|
||||
_defaultShapeSequence;
|
||||
final padding = (widget.padding ?? EdgeInsets.zero).resolve(
|
||||
Directionality.of(context),
|
||||
);
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final dimension = _resolveDimension(constraints);
|
||||
final indicatorConstraints = constraints.deflate(padding);
|
||||
final dimension = _resolveDimension(
|
||||
indicatorConstraints,
|
||||
widget.constraints ?? _defaultConstraints,
|
||||
);
|
||||
final currentMorphIndex = _currentMorphIndex % shapeSequence.length;
|
||||
return Align(
|
||||
widthFactor: 1,
|
||||
heightFactor: 1,
|
||||
child: RepaintBoundary(
|
||||
child: RotationTransition(
|
||||
turns: _rotateController,
|
||||
child: CustomPaint(
|
||||
size: Size.square(dimension),
|
||||
painter: _StarPainter(
|
||||
points: _pointsAnimation.value,
|
||||
color: color,
|
||||
child: Semantics(
|
||||
label: widget.semanticLabel,
|
||||
value: widget.semanticValue,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: RepaintBoundary(
|
||||
child: SizedBox.square(
|
||||
dimension: dimension,
|
||||
child: AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder: (context, child) {
|
||||
final morphProgress = _morphController.value.clamp(
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
final rotationDegrees =
|
||||
morphProgress * _quarterRotation +
|
||||
_morphRotationTargetAngle +
|
||||
_globalRotationController.value * _fullRotation;
|
||||
final shape = _ShapeSpec.lerp(
|
||||
shapeSequence[currentMorphIndex],
|
||||
shapeSequence[(currentMorphIndex + 1) %
|
||||
shapeSequence.length],
|
||||
morphProgress,
|
||||
);
|
||||
return Transform.rotate(
|
||||
angle: rotationDegrees * math.pi / 180,
|
||||
child: CustomPaint(
|
||||
painter: _ShapePainter(
|
||||
shape: shape,
|
||||
color: activeColor,
|
||||
scaleFactor: _activeIndicatorScale,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -69,9 +234,21 @@ class _CommonCircleLoadingState extends State<CommonCircleLoading>
|
||||
);
|
||||
}
|
||||
|
||||
double _resolveDimension(BoxConstraints constraints) {
|
||||
final maxWidth = constraints.maxWidth;
|
||||
final maxHeight = constraints.maxHeight;
|
||||
int get _shapeCount =>
|
||||
widget.polygons?.length ?? _defaultShapeSequence.length;
|
||||
|
||||
double _resolveDimension(
|
||||
BoxConstraints parentConstraints,
|
||||
BoxConstraints preferredConstraints,
|
||||
) {
|
||||
final effectiveConstraints = preferredConstraints.enforce(
|
||||
BoxConstraints(
|
||||
maxWidth: parentConstraints.maxWidth,
|
||||
maxHeight: parentConstraints.maxHeight,
|
||||
),
|
||||
);
|
||||
final maxWidth = effectiveConstraints.maxWidth;
|
||||
final maxHeight = effectiveConstraints.maxHeight;
|
||||
|
||||
if (maxWidth.isFinite && maxHeight.isFinite) {
|
||||
return maxWidth < maxHeight ? maxWidth : maxHeight;
|
||||
@@ -87,33 +264,143 @@ class _CommonCircleLoadingState extends State<CommonCircleLoading>
|
||||
|
||||
return CommonCircleLoading.defaultDimension;
|
||||
}
|
||||
|
||||
Future<void> _runMorphLoop() async {
|
||||
while (mounted) {
|
||||
final startedAt = DateTime.now();
|
||||
try {
|
||||
await _morphController.animateWith(_morphAnimation).orCancel;
|
||||
} on TickerCanceled {
|
||||
return;
|
||||
}
|
||||
|
||||
final elapsed = DateTime.now().difference(startedAt);
|
||||
if (elapsed < _morphInterval) {
|
||||
await Future<void>.delayed(_morphInterval - elapsed);
|
||||
}
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_currentMorphIndex = (_currentMorphIndex + 1) % _shapeCount;
|
||||
_morphRotationTargetAngle =
|
||||
(_morphRotationTargetAngle + _quarterRotation) % _fullRotation;
|
||||
_morphController.value = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _StarPainter extends CustomPainter {
|
||||
class _ShapeSpec {
|
||||
final double points;
|
||||
final double innerRadiusRatio;
|
||||
final double pointRounding;
|
||||
final double valleyRounding;
|
||||
final double widthScale;
|
||||
final double heightScale;
|
||||
final double squash;
|
||||
|
||||
const _ShapeSpec({
|
||||
required this.points,
|
||||
required this.innerRadiusRatio,
|
||||
required this.pointRounding,
|
||||
this.valleyRounding = 0,
|
||||
this.widthScale = 1,
|
||||
this.heightScale = 1,
|
||||
this.squash = 0,
|
||||
});
|
||||
|
||||
static _ShapeSpec lerp(_ShapeSpec begin, _ShapeSpec end, double progress) {
|
||||
return _ShapeSpec(
|
||||
points: _lerp(begin.points, end.points, progress),
|
||||
innerRadiusRatio: _lerp(
|
||||
begin.innerRadiusRatio,
|
||||
end.innerRadiusRatio,
|
||||
progress,
|
||||
),
|
||||
pointRounding: _lerp(begin.pointRounding, end.pointRounding, progress),
|
||||
valleyRounding: _lerp(begin.valleyRounding, end.valleyRounding, progress),
|
||||
widthScale: _lerp(begin.widthScale, end.widthScale, progress),
|
||||
heightScale: _lerp(begin.heightScale, end.heightScale, progress),
|
||||
squash: _lerp(begin.squash, end.squash, progress),
|
||||
);
|
||||
}
|
||||
|
||||
static _ShapeSpec fromStarBorder(StarBorder border) {
|
||||
return _ShapeSpec(
|
||||
points: border.points,
|
||||
innerRadiusRatio: border.innerRadiusRatio,
|
||||
pointRounding: border.pointRounding,
|
||||
valleyRounding: border.valleyRounding,
|
||||
squash: border.squash,
|
||||
);
|
||||
}
|
||||
|
||||
static double _lerp(double begin, double end, double progress) {
|
||||
return begin + (end - begin) * progress;
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _ShapeSpec &&
|
||||
other.points == points &&
|
||||
other.innerRadiusRatio == innerRadiusRatio &&
|
||||
other.pointRounding == pointRounding &&
|
||||
other.valleyRounding == valleyRounding &&
|
||||
other.widthScale == widthScale &&
|
||||
other.heightScale == heightScale &&
|
||||
other.squash == squash;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
points,
|
||||
innerRadiusRatio,
|
||||
pointRounding,
|
||||
valleyRounding,
|
||||
widthScale,
|
||||
heightScale,
|
||||
squash,
|
||||
);
|
||||
}
|
||||
|
||||
class _ShapePainter extends CustomPainter {
|
||||
final _ShapeSpec shape;
|
||||
final Color color;
|
||||
final double scaleFactor;
|
||||
final Paint _paint;
|
||||
|
||||
_StarPainter({required this.points, required this.color})
|
||||
: _paint = Paint()..color = color;
|
||||
_ShapePainter({
|
||||
required this.shape,
|
||||
required this.color,
|
||||
required this.scaleFactor,
|
||||
}) : _paint = Paint()
|
||||
..style = PaintingStyle.fill
|
||||
..isAntiAlias = true
|
||||
..color = color;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final rect = Offset.zero & size;
|
||||
final starBorder = StarBorder(
|
||||
points: points,
|
||||
innerRadiusRatio: 0.8,
|
||||
pointRounding: 0.5,
|
||||
valleyRounding: 0.1,
|
||||
squash: 0.5,
|
||||
final rect = Rect.fromCenter(
|
||||
center: size.center(Offset.zero),
|
||||
width: size.width * scaleFactor * shape.widthScale,
|
||||
height: size.height * scaleFactor * shape.heightScale,
|
||||
);
|
||||
|
||||
final path = starBorder.getOuterPath(rect);
|
||||
canvas.drawPath(path, _paint);
|
||||
final border = StarBorder(
|
||||
points: shape.points,
|
||||
innerRadiusRatio: shape.innerRadiusRatio,
|
||||
pointRounding: shape.pointRounding,
|
||||
valleyRounding: shape.valleyRounding,
|
||||
squash: shape.squash,
|
||||
);
|
||||
canvas.drawPath(border.getOuterPath(rect), _paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _StarPainter oldDelegate) {
|
||||
return oldDelegate.points != points || oldDelegate.color != color;
|
||||
bool shouldRepaint(covariant _ShapePainter oldDelegate) {
|
||||
return oldDelegate.shape != shape ||
|
||||
oldDelegate.color != color ||
|
||||
oldDelegate.scaleFactor != scaleFactor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +269,10 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
|
||||
final _FlippableTweenSequence<double> _closedOpacityTween;
|
||||
final _FlippableTweenSequence<double> _openOpacityTween;
|
||||
final Animatable<Color?> _scrimTween = ColorTween(
|
||||
begin: Colors.transparent,
|
||||
end: Colors.transparent,
|
||||
);
|
||||
late _FlippableTweenSequence<Color?> _colorTween;
|
||||
final GlobalKey _openBuilderKey = GlobalKey();
|
||||
final RectTween _rectTween = RectTween();
|
||||
@@ -475,60 +479,64 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
|
||||
final Rect rect = _rectTween.evaluate(curvedAnimation)!;
|
||||
return SizedBox.expand(
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Transform.translate(
|
||||
offset: Offset(rect.left, rect.top),
|
||||
child: SizedBox(
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
child: Material(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
animationDuration: Duration.zero,
|
||||
color: colorTween!.evaluate(animation),
|
||||
child: Stack(
|
||||
fit: StackFit.passthrough,
|
||||
children: <Widget>[
|
||||
// Closed child fading out.
|
||||
FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
alignment: Alignment.topLeft,
|
||||
child: SizedBox(
|
||||
width: _rectTween.begin!.width,
|
||||
height: _rectTween.begin!.height,
|
||||
child: (hideableKey.currentState?.isInTree ?? false)
|
||||
? null
|
||||
: FadeTransition(
|
||||
opacity: closedOpacityTween!.animate(
|
||||
animation,
|
||||
child: Container(
|
||||
color: _scrimTween.evaluate(curvedAnimation),
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Transform.translate(
|
||||
offset: Offset(rect.left, rect.top),
|
||||
child: SizedBox(
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
child: Material(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
animationDuration: Duration.zero,
|
||||
color: colorTween!.evaluate(animation),
|
||||
child: Stack(
|
||||
fit: StackFit.passthrough,
|
||||
children: <Widget>[
|
||||
// Closed child fading out.
|
||||
FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
alignment: Alignment.topLeft,
|
||||
child: SizedBox(
|
||||
width: _rectTween.begin!.width,
|
||||
height: _rectTween.begin!.height,
|
||||
child:
|
||||
(hideableKey.currentState?.isInTree ?? false)
|
||||
? null
|
||||
: FadeTransition(
|
||||
opacity: closedOpacityTween!.animate(
|
||||
animation,
|
||||
),
|
||||
child: Builder(
|
||||
key: closedBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
// Use dummy "open container" callback
|
||||
// since we are in the process of opening.
|
||||
return closedBuilder(context, () {});
|
||||
},
|
||||
),
|
||||
),
|
||||
child: Builder(
|
||||
key: closedBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
// Use dummy "open container" callback
|
||||
// since we are in the process of opening.
|
||||
return closedBuilder(context, () {});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Open child fading in.
|
||||
OverflowBox(
|
||||
maxWidth: _rectTween.end!.width,
|
||||
maxHeight: _rectTween.end!.height,
|
||||
alignment: Alignment.topLeft,
|
||||
child: FadeTransition(
|
||||
opacity: openOpacityTween!.animate(animation),
|
||||
child: Builder(
|
||||
key: _openBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
return openBuilder(context, closeContainer);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// Open child fading in.
|
||||
OverflowBox(
|
||||
maxWidth: _rectTween.end!.width,
|
||||
maxHeight: _rectTween.end!.height,
|
||||
alignment: Alignment.topLeft,
|
||||
child: FadeTransition(
|
||||
opacity: openOpacityTween!.animate(animation),
|
||||
child: Builder(
|
||||
key: _openBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
return openBuilder(context, closeContainer);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -22,6 +22,7 @@ class CommonScaffold extends StatefulWidget {
|
||||
final List<Widget>? actions;
|
||||
final bool? centerTitle;
|
||||
final Widget? floatingActionButton;
|
||||
final bool? isTV;
|
||||
final AppBarEditState? editState;
|
||||
final AppBarSearchState? searchState;
|
||||
final OnKeywordsUpdateCallback? onKeywordsUpdate;
|
||||
@@ -39,6 +40,7 @@ class CommonScaffold extends StatefulWidget {
|
||||
this.isLoading = false,
|
||||
this.searchState,
|
||||
this.floatingActionButton,
|
||||
this.isTV,
|
||||
this.onKeywordsUpdate,
|
||||
this.resizeToAvoidBottomInset,
|
||||
});
|
||||
@@ -305,10 +307,19 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
Widget build(BuildContext context) {
|
||||
assert(widget.appBar != null || widget.title != null);
|
||||
final backActionProvider = CommonScaffoldBackActionProvider.of(context);
|
||||
final isTV = widget.isTV ?? system.isTV;
|
||||
final body = SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isTV && widget.floatingActionButton != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: CommonScaffoldFabExtendedProvider(
|
||||
isExtended: true,
|
||||
child: widget.floatingActionButton!,
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _keywordsNotifier,
|
||||
builder: (_, keywords, _) {
|
||||
@@ -361,7 +372,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
),
|
||||
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
floatingActionButton: widget.floatingActionButton != null
|
||||
floatingActionButton: !isTV && widget.floatingActionButton != null
|
||||
? ValueListenableBuilder<bool>(
|
||||
valueListenable: _isFabExtendedNotifier,
|
||||
builder: (_, isExtended, child) {
|
||||
|
||||
+165
-120
@@ -10,8 +10,6 @@ import 'package:fl_clash/widgets/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
|
||||
typedef VoidCallback = void Function();
|
||||
|
||||
class SuperGrid extends StatefulWidget {
|
||||
final List<GridItem> children;
|
||||
final double mainAxisSpacing;
|
||||
@@ -33,7 +31,11 @@ class SuperGrid extends StatefulWidget {
|
||||
}
|
||||
|
||||
class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
final ValueNotifier<List<GridItem>> _childrenNotifier = ValueNotifier([]);
|
||||
static const _reorderDuration = Duration(milliseconds: 420);
|
||||
static const _shakeDuration = Duration(milliseconds: 480);
|
||||
static const _reorderCurve = Cubic(0.22, 0.72, 0.24, 1.08);
|
||||
|
||||
late final ValueNotifier<List<GridItem>> _childrenNotifier;
|
||||
List<GridItem> children = [];
|
||||
|
||||
int get length => _childrenNotifier.value.length;
|
||||
@@ -46,7 +48,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
List<Offset> _offsets = [];
|
||||
Offset _parentOffset = Offset.zero;
|
||||
EdgeDraggingAutoScroller? _edgeDraggingAutoScroller;
|
||||
Map<int, Tween<Offset>> _transformTweenMap = {};
|
||||
|
||||
final ValueNotifier<bool> _animating = ValueNotifier(false);
|
||||
|
||||
@@ -57,7 +58,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
late AnimationController _transformController;
|
||||
|
||||
Future<bool> get isTransformCompleter =>
|
||||
_transformCompleter?.future ?? Future(() => true);
|
||||
_transformCompleter?.future ?? Future<bool>.value(true);
|
||||
|
||||
Completer<bool>? _transformCompleter;
|
||||
|
||||
@@ -67,15 +68,21 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
Animation<Offset>? _fakeDragWidgetAnimation;
|
||||
|
||||
late AnimationController _shakeController;
|
||||
late Animation<double> _shakeAnimation;
|
||||
Rect _dragRect = Rect.zero;
|
||||
Scrollable? _scrollable;
|
||||
bool _isDragging = false;
|
||||
|
||||
int get crossCount => widget.crossAxisCount;
|
||||
|
||||
void _onChildrenChange() {
|
||||
void _stopAutoScroll() {
|
||||
_edgeDraggingAutoScroller?.stopAutoScroll();
|
||||
}
|
||||
|
||||
void _handleChildrenChanged() {
|
||||
children = List<GridItem>.of(_childrenNotifier.value);
|
||||
_tempIndexList = List.generate(length, (index) => index);
|
||||
_itemContexts = List.filled(length, null);
|
||||
widget.onUpdate?.call();
|
||||
}
|
||||
|
||||
void _preTransformState() {
|
||||
@@ -99,7 +106,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_transformController.value = 0;
|
||||
_sizes = List.generate(length, (index) => Size.zero);
|
||||
_offsets = [];
|
||||
_transformTweenMap.clear();
|
||||
_transformAnimationMap.clear();
|
||||
_containerSize = Size.zero;
|
||||
_dragIndexNotifier.value = -1;
|
||||
@@ -113,36 +119,28 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
children = widget.children;
|
||||
_childrenNotifier.addListener(() {
|
||||
children = _childrenNotifier.value;
|
||||
if (widget.onUpdate != null) {
|
||||
widget.onUpdate!();
|
||||
}
|
||||
});
|
||||
|
||||
_childrenNotifier.value = widget.children;
|
||||
children = List<GridItem>.of(widget.children);
|
||||
_childrenNotifier = ValueNotifier(children)
|
||||
..addListener(_handleChildrenChanged);
|
||||
_tempIndexList = List.generate(length, (index) => index);
|
||||
_itemContexts = List.filled(length, null);
|
||||
|
||||
_fakeDragWidgetController = AnimationController.unbounded(vsync: this);
|
||||
|
||||
_shakeController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 120),
|
||||
);
|
||||
|
||||
_shakeAnimation = Tween<double>(begin: -0.012, end: 0.012).animate(
|
||||
CurvedAnimation(parent: _shakeController, curve: Curves.easeInOut),
|
||||
);
|
||||
duration: _shakeDuration,
|
||||
)..repeat();
|
||||
|
||||
_transformController = AnimationController(
|
||||
vsync: this,
|
||||
duration: commonDuration,
|
||||
duration: _reorderDuration,
|
||||
);
|
||||
_initState();
|
||||
}
|
||||
|
||||
void handleAdd(GridItem gridItem) {
|
||||
_childrenNotifier.value = List.from(_childrenNotifier.value)..add(gridItem);
|
||||
_childrenNotifier.value = [..._childrenNotifier.value, gridItem];
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -151,20 +149,33 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
|
||||
final scrollable = context.findAncestorWidgetOfExactType<Scrollable>();
|
||||
if (scrollable == null) {
|
||||
_stopAutoScroll();
|
||||
_scrollable = null;
|
||||
_edgeDraggingAutoScroller = null;
|
||||
return;
|
||||
}
|
||||
if (_scrollable != scrollable) {
|
||||
_edgeDraggingAutoScroller = EdgeDraggingAutoScroller(
|
||||
Scrollable.of(context),
|
||||
onScrollViewScrolled: () {
|
||||
_edgeDraggingAutoScroller?.startAutoScrollIfNecessary(_dragRect);
|
||||
},
|
||||
velocityScalar: 40,
|
||||
);
|
||||
if (_scrollable == scrollable) {
|
||||
return;
|
||||
}
|
||||
_stopAutoScroll();
|
||||
_scrollable = scrollable;
|
||||
late final EdgeDraggingAutoScroller autoScroller;
|
||||
autoScroller = EdgeDraggingAutoScroller(
|
||||
Scrollable.of(context),
|
||||
onScrollViewScrolled: () {
|
||||
if (!mounted ||
|
||||
!_isDragging ||
|
||||
!identical(_edgeDraggingAutoScroller, autoScroller)) {
|
||||
return;
|
||||
}
|
||||
autoScroller.startAutoScrollIfNecessary(_dragRect);
|
||||
},
|
||||
velocityScalar: 40,
|
||||
);
|
||||
_edgeDraggingAutoScroller = autoScroller;
|
||||
}
|
||||
|
||||
Future _transform() async {
|
||||
Future<bool> _transform() async {
|
||||
final List<Offset> layoutOffsets = [Offset(_containerSize.width, 0)];
|
||||
final List<Offset> nextOffsets = [];
|
||||
|
||||
@@ -210,42 +221,35 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
layoutOffsets.removeRange(min(startIndex + 1, endIndex), endIndex);
|
||||
}
|
||||
|
||||
final Map<int, Tween<Offset>> transformTweenMap = {};
|
||||
|
||||
for (final index in _tempIndexList) {
|
||||
final nextIndex = _tempIndexList.indexWhere((i) => i == index);
|
||||
transformTweenMap[index] = Tween(
|
||||
begin: _transformTweenMap[index]?.begin ?? Offset.zero,
|
||||
final transformAnimationMap = <int, Animation<Offset>>{};
|
||||
final transformCurve = CurvedAnimation(
|
||||
parent: _transformController,
|
||||
curve: _reorderCurve,
|
||||
);
|
||||
for (var nextIndex = 0; nextIndex < _tempIndexList.length; nextIndex++) {
|
||||
final index = _tempIndexList[nextIndex];
|
||||
transformAnimationMap[index] = Tween<Offset>(
|
||||
begin: _transformAnimationMap[index]?.value ?? Offset.zero,
|
||||
end: nextOffsets[nextIndex] - _offsets[index],
|
||||
);
|
||||
).animate(transformCurve);
|
||||
}
|
||||
|
||||
_transformTweenMap = transformTweenMap;
|
||||
|
||||
_transformAnimationMap = transformTweenMap.map((key, value) {
|
||||
final preAnimationValue = _transformAnimationMap[key]?.value;
|
||||
return MapEntry(
|
||||
key,
|
||||
Tween(begin: preAnimationValue ?? Offset.zero, end: value.end).animate(
|
||||
_transformController.drive(
|
||||
Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1,
|
||||
).chain(CurveTween(curve: Curves.fastOutSlowIn)),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
_transformAnimationMap = transformAnimationMap;
|
||||
|
||||
if (_targetIndex != -1) {
|
||||
_targetOffset = nextOffsets[_targetIndex];
|
||||
}
|
||||
return _transformController.forward(from: 0);
|
||||
try {
|
||||
await _transformController.forward(from: 0).orCancel;
|
||||
return true;
|
||||
} on TickerCanceled {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleDragStarted(int index) {
|
||||
_initState();
|
||||
_preTransformState();
|
||||
_isDragging = true;
|
||||
_dragIndexNotifier.value = index;
|
||||
_dragWidgetSizeNotifier.value = _sizes[index];
|
||||
_targetIndex = index;
|
||||
@@ -259,41 +263,71 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Future<void> _handleDragEnd(DraggableDetails details) async {
|
||||
final children = List<GridItem>.from(_childrenNotifier.value);
|
||||
children.insert(_targetIndex, children.removeAt(_dragIndexNotifier.value));
|
||||
this.children = children;
|
||||
_isDragging = false;
|
||||
_stopAutoScroll();
|
||||
debouncer.cancel(FunctionTag.handleWill);
|
||||
if (_targetIndex == -1) {
|
||||
final dragIndex = _dragIndexNotifier.value;
|
||||
if (_targetIndex < 0 ||
|
||||
_targetIndex >= length ||
|
||||
dragIndex < 0 ||
|
||||
dragIndex >= length) {
|
||||
_initState();
|
||||
return;
|
||||
}
|
||||
const tolerance = Tolerance(distance: 0.5, velocity: 0.01);
|
||||
const spring = SpringDescription(mass: 1, stiffness: 100, damping: 10);
|
||||
|
||||
final nextChildren = List<GridItem>.of(_childrenNotifier.value);
|
||||
nextChildren.insert(_targetIndex, nextChildren.removeAt(dragIndex));
|
||||
children = nextChildren;
|
||||
|
||||
const tolerance = Tolerance(distance: 0.001, velocity: 0.01);
|
||||
const spring = SpringDescription(mass: 1, stiffness: 180, damping: 18);
|
||||
final simulation = SpringSimulation(spring, 0, 1, 0, tolerance: tolerance);
|
||||
_fakeDragWidgetAnimation = Tween(
|
||||
_fakeDragWidgetAnimation = Tween<Offset>(
|
||||
begin: details.offset - _parentOffset,
|
||||
end: _targetOffset,
|
||||
).animate(_fakeDragWidgetController);
|
||||
_animating.value = true;
|
||||
|
||||
_transformCompleter = Completer<bool>();
|
||||
await _fakeDragWidgetController.animateWith(simulation);
|
||||
_transformCompleter?.complete(true);
|
||||
_animating.value = false;
|
||||
_fakeDragWidgetAnimation = null;
|
||||
_transformTweenMap.clear();
|
||||
_transformAnimationMap.clear();
|
||||
_childrenNotifier.value = children;
|
||||
_initState();
|
||||
final completer = Completer<bool>();
|
||||
_transformCompleter = completer;
|
||||
try {
|
||||
await _fakeDragWidgetController.animateWith(simulation).orCancel;
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_animating.value = false;
|
||||
_fakeDragWidgetAnimation = null;
|
||||
_transformAnimationMap.clear();
|
||||
_childrenNotifier.value = nextChildren;
|
||||
_initState();
|
||||
completer.complete(true);
|
||||
} on TickerCanceled {
|
||||
if (mounted) {
|
||||
_animating.value = false;
|
||||
_fakeDragWidgetAnimation = null;
|
||||
_initState();
|
||||
}
|
||||
} finally {
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(false);
|
||||
}
|
||||
if (identical(_transformCompleter, completer)) {
|
||||
_transformCompleter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _handleDragUpdate(DragUpdateDetails details) {
|
||||
if (!_isDragging) {
|
||||
return;
|
||||
}
|
||||
_dragRect = _dragRect.translate(0, details.delta.dy);
|
||||
_edgeDraggingAutoScroller?.startAutoScrollIfNecessary(_dragRect);
|
||||
}
|
||||
|
||||
Future<void> _handleWill(int index) async {
|
||||
final dragIndex = _dragIndexNotifier.value;
|
||||
if (dragIndex < 0 || dragIndex > _offsets.length - 1) {
|
||||
if (dragIndex < 0 || dragIndex >= _offsets.length) {
|
||||
return;
|
||||
}
|
||||
final targetIndex = _tempIndexList.indexWhere((i) => i == index);
|
||||
@@ -321,7 +355,10 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_preTransformState();
|
||||
final indexWhere = _tempIndexList.indexWhere((i) => i == index);
|
||||
_tempIndexList.removeAt(indexWhere);
|
||||
await _transform();
|
||||
final completed = await _transform();
|
||||
if (!completed || !mounted) {
|
||||
return;
|
||||
}
|
||||
final children = List<GridItem>.from(_childrenNotifier.value);
|
||||
children.removeAt(index);
|
||||
_childrenNotifier.value = children;
|
||||
@@ -333,7 +370,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
valueListenable: _animating,
|
||||
builder: (_, animating, child) {
|
||||
if (animating && _dragIndexNotifier.value == index) {
|
||||
return _buildSizeBox(Container(), index);
|
||||
return _buildSizeBox(const SizedBox.shrink());
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
@@ -382,7 +419,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
return nextOffset;
|
||||
}
|
||||
|
||||
Widget _buildSizeBox(Widget child, int index) {
|
||||
Widget _buildSizeBox(Widget child) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _dragWidgetSizeNotifier,
|
||||
builder: (_, size, child) {
|
||||
@@ -396,27 +433,19 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _animating,
|
||||
builder: (_, animating, child) {
|
||||
if (animating) {
|
||||
return ActivateBox(child: child!);
|
||||
} else {
|
||||
return child!;
|
||||
}
|
||||
return animating ? ActivateBox(child: child!) : child!;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildShake(Widget child) {
|
||||
final random = 0.7 + Random().nextDouble() * 0.3;
|
||||
_shakeController.stop();
|
||||
_shakeController.repeat(reverse: true);
|
||||
Widget _buildShake(Widget child, int index) {
|
||||
return AnimatedBuilder(
|
||||
animation: _shakeAnimation,
|
||||
animation: _shakeController,
|
||||
builder: (_, child) {
|
||||
return Transform.rotate(
|
||||
angle: _shakeAnimation.value * random,
|
||||
child: child!,
|
||||
);
|
||||
final phase = index * pi / 2;
|
||||
final angle = sin(_shakeController.value * 2 * pi + phase) * 0.01;
|
||||
return Transform.rotate(angle: angle, child: child!);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
@@ -464,64 +493,64 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
},
|
||||
child: child!,
|
||||
),
|
||||
index,
|
||||
);
|
||||
},
|
||||
child: target,
|
||||
),
|
||||
);
|
||||
void onDragStarted() {
|
||||
_handleDragStarted(index);
|
||||
}
|
||||
|
||||
void onDragUpdate(DragUpdateDetails details) {
|
||||
_handleDragUpdate(details);
|
||||
}
|
||||
|
||||
void onDragEnd(DraggableDetails details) {
|
||||
_handleDragEnd(details);
|
||||
}
|
||||
|
||||
final draggableChild = system.isDesktop
|
||||
? Draggable(
|
||||
childWhenDragging: childWhenDragging,
|
||||
data: index,
|
||||
feedback: feedback,
|
||||
onDragStarted: () {
|
||||
_handleDragStarted(index);
|
||||
},
|
||||
onDragUpdate: (details) {
|
||||
_handleDragUpdate(details);
|
||||
},
|
||||
onDragEnd: (details) {
|
||||
_handleDragEnd(details);
|
||||
},
|
||||
onDragStarted: onDragStarted,
|
||||
onDragUpdate: onDragUpdate,
|
||||
onDragEnd: onDragEnd,
|
||||
child: shakeTarget,
|
||||
)
|
||||
: LongPressDraggable(
|
||||
childWhenDragging: childWhenDragging,
|
||||
data: index,
|
||||
feedback: feedback,
|
||||
onDragStarted: () {
|
||||
_handleDragStarted(index);
|
||||
},
|
||||
onDragUpdate: (details) {
|
||||
_handleDragUpdate(details);
|
||||
},
|
||||
onDragEnd: (details) {
|
||||
_handleDragEnd(details);
|
||||
},
|
||||
onDragStarted: onDragStarted,
|
||||
onDragUpdate: onDragUpdate,
|
||||
onDragEnd: onDragEnd,
|
||||
child: shakeTarget,
|
||||
);
|
||||
return draggableChild;
|
||||
}
|
||||
|
||||
Widget _builderItem(int index) {
|
||||
final girdItem = _childrenNotifier.value[index];
|
||||
final child = girdItem.child;
|
||||
final gridItem = _childrenNotifier.value[index];
|
||||
final child = gridItem.child;
|
||||
return GridItem(
|
||||
mainAxisCellCount: girdItem.mainAxisCellCount,
|
||||
crossAxisCellCount: girdItem.crossAxisCellCount,
|
||||
mainAxisCellCount: gridItem.mainAxisCellCount,
|
||||
crossAxisCellCount: gridItem.crossAxisCellCount,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
_itemContexts[index] = context;
|
||||
final childWhenDragging = ActivateBox(
|
||||
child: Opacity(
|
||||
opacity: 0.6,
|
||||
child: _buildSizeBox(CommonCard(child: child), index),
|
||||
child: _buildSizeBox(CommonCard(child: child)),
|
||||
),
|
||||
);
|
||||
final feedback = ActivateBox(
|
||||
child: _buildSizeBox(
|
||||
CommonCard(child: Material(elevation: 6, child: child)),
|
||||
index,
|
||||
),
|
||||
);
|
||||
return _buildTransform(
|
||||
@@ -544,7 +573,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
builder: (_, animating, _) {
|
||||
final index = _dragIndexNotifier.value;
|
||||
if (!animating || _fakeDragWidgetAnimation == null || index == -1) {
|
||||
return Container();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return _buildSizeBox(
|
||||
AnimatedBuilder(
|
||||
@@ -557,7 +586,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
},
|
||||
child: ActivateBox(child: _childrenNotifier.value[index].child),
|
||||
),
|
||||
index,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -565,10 +593,28 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isDragging = false;
|
||||
_stopAutoScroll();
|
||||
_edgeDraggingAutoScroller = null;
|
||||
_scrollable = null;
|
||||
debouncer.cancel(FunctionTag.handleWill);
|
||||
final completer = _transformCompleter;
|
||||
if (completer != null && !completer.isCompleted) {
|
||||
completer.complete(false);
|
||||
}
|
||||
_transformCompleter = null;
|
||||
_childrenNotifier.removeListener(_handleChildrenChanged);
|
||||
_childrenNotifier.value = const [];
|
||||
children = const [];
|
||||
_itemContexts = [];
|
||||
_sizes = [];
|
||||
_offsets = [];
|
||||
_transformAnimationMap.clear();
|
||||
_fakeDragWidgetAnimation = null;
|
||||
_fakeDragWidgetController.dispose();
|
||||
_shakeController.dispose();
|
||||
_transformController.dispose();
|
||||
_dragWidgetSizeNotifier.dispose();
|
||||
_dragIndexNotifier.dispose();
|
||||
_animating.dispose();
|
||||
_childrenNotifier.dispose();
|
||||
@@ -584,7 +630,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _childrenNotifier,
|
||||
builder: (_, children, _) {
|
||||
_onChildrenChange();
|
||||
return Grid(
|
||||
axisDirection: AxisDirection.down,
|
||||
crossAxisCount: crossCount,
|
||||
|
||||
Reference in New Issue
Block a user