Compare commits
2 Commits
v0.8.87-pr
...
v0.8.87
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1154e7b245 | ||
|
|
adb890d763 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,3 +1,13 @@
|
||||
## v0.8.86
|
||||
|
||||
- Fix windows tun issues
|
||||
|
||||
- Optimize android get system dns
|
||||
|
||||
- Optimize more details
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.85
|
||||
|
||||
- Support override script
|
||||
|
||||
@@ -117,7 +117,7 @@ class Request {
|
||||
}
|
||||
}).catchError((e) {
|
||||
failureCount++;
|
||||
if (e == DioExceptionType.cancel) {
|
||||
if (e is DioException && e.type == DioExceptionType.cancel) {
|
||||
completer.complete(Result.error('cancelled'));
|
||||
}
|
||||
handleFailRes();
|
||||
|
||||
@@ -97,6 +97,7 @@ class System {
|
||||
final shell = Platform.environment['SHELL'] ?? 'bash';
|
||||
final password = await globalState.showCommonDialog<String>(
|
||||
child: InputDialog(
|
||||
obscureText: true,
|
||||
title: appLocalizations.pleaseInputAdminPassword,
|
||||
value: '',
|
||||
),
|
||||
|
||||
@@ -93,6 +93,11 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
} else {
|
||||
render?.resume();
|
||||
}
|
||||
if (state == AppLifecycleState.inactive) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
detectionState.tryStartCheck();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -204,7 +209,7 @@ class AppSidebarContainer extends ConsumerWidget {
|
||||
SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
if (system.isWindows) ...[
|
||||
if (!system.isMacOS) ...[
|
||||
AppIcon(),
|
||||
SizedBox(
|
||||
height: 12,
|
||||
|
||||
@@ -39,7 +39,7 @@ class _TrayContainerState extends ConsumerState<TrayManager> with TrayListener {
|
||||
|
||||
@override
|
||||
void onTrayIconRightMouseDown() {
|
||||
trayManager.popUpContextMenu();
|
||||
trayManager.popUpContextMenu(bringAppToFront: true);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1061,7 +1061,6 @@ abstract class _ProfilesSelectorState implements ProfilesSelectorState {
|
||||
/// @nodoc
|
||||
mixin _$NetworkDetectionState {
|
||||
bool get isLoading => throw _privateConstructorUsedError;
|
||||
bool get isTesting => throw _privateConstructorUsedError;
|
||||
IpInfo? get ipInfo => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of NetworkDetectionState
|
||||
@@ -1077,7 +1076,7 @@ abstract class $NetworkDetectionStateCopyWith<$Res> {
|
||||
$Res Function(NetworkDetectionState) then) =
|
||||
_$NetworkDetectionStateCopyWithImpl<$Res, NetworkDetectionState>;
|
||||
@useResult
|
||||
$Res call({bool isLoading, bool isTesting, IpInfo? ipInfo});
|
||||
$Res call({bool isLoading, IpInfo? ipInfo});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1097,7 +1096,6 @@ class _$NetworkDetectionStateCopyWithImpl<$Res,
|
||||
@override
|
||||
$Res call({
|
||||
Object? isLoading = null,
|
||||
Object? isTesting = null,
|
||||
Object? ipInfo = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@@ -1105,10 +1103,6 @@ class _$NetworkDetectionStateCopyWithImpl<$Res,
|
||||
? _value.isLoading
|
||||
: isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isTesting: null == isTesting
|
||||
? _value.isTesting
|
||||
: isTesting // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
ipInfo: freezed == ipInfo
|
||||
? _value.ipInfo
|
||||
: ipInfo // ignore: cast_nullable_to_non_nullable
|
||||
@@ -1126,7 +1120,7 @@ abstract class _$$NetworkDetectionStateImplCopyWith<$Res>
|
||||
__$$NetworkDetectionStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool isLoading, bool isTesting, IpInfo? ipInfo});
|
||||
$Res call({bool isLoading, IpInfo? ipInfo});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1144,7 +1138,6 @@ class __$$NetworkDetectionStateImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? isLoading = null,
|
||||
Object? isTesting = null,
|
||||
Object? ipInfo = freezed,
|
||||
}) {
|
||||
return _then(_$NetworkDetectionStateImpl(
|
||||
@@ -1152,10 +1145,6 @@ class __$$NetworkDetectionStateImplCopyWithImpl<$Res>
|
||||
? _value.isLoading
|
||||
: isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isTesting: null == isTesting
|
||||
? _value.isTesting
|
||||
: isTesting // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
ipInfo: freezed == ipInfo
|
||||
? _value.ipInfo
|
||||
: ipInfo // ignore: cast_nullable_to_non_nullable
|
||||
@@ -1168,18 +1157,16 @@ class __$$NetworkDetectionStateImplCopyWithImpl<$Res>
|
||||
|
||||
class _$NetworkDetectionStateImpl implements _NetworkDetectionState {
|
||||
const _$NetworkDetectionStateImpl(
|
||||
{required this.isLoading, required this.isTesting, required this.ipInfo});
|
||||
{required this.isLoading, required this.ipInfo});
|
||||
|
||||
@override
|
||||
final bool isLoading;
|
||||
@override
|
||||
final bool isTesting;
|
||||
@override
|
||||
final IpInfo? ipInfo;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NetworkDetectionState(isLoading: $isLoading, isTesting: $isTesting, ipInfo: $ipInfo)';
|
||||
return 'NetworkDetectionState(isLoading: $isLoading, ipInfo: $ipInfo)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1189,13 +1176,11 @@ class _$NetworkDetectionStateImpl implements _NetworkDetectionState {
|
||||
other is _$NetworkDetectionStateImpl &&
|
||||
(identical(other.isLoading, isLoading) ||
|
||||
other.isLoading == isLoading) &&
|
||||
(identical(other.isTesting, isTesting) ||
|
||||
other.isTesting == isTesting) &&
|
||||
(identical(other.ipInfo, ipInfo) || other.ipInfo == ipInfo));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, isLoading, isTesting, ipInfo);
|
||||
int get hashCode => Object.hash(runtimeType, isLoading, ipInfo);
|
||||
|
||||
/// Create a copy of NetworkDetectionState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1210,14 +1195,11 @@ class _$NetworkDetectionStateImpl implements _NetworkDetectionState {
|
||||
abstract class _NetworkDetectionState implements NetworkDetectionState {
|
||||
const factory _NetworkDetectionState(
|
||||
{required final bool isLoading,
|
||||
required final bool isTesting,
|
||||
required final IpInfo? ipInfo}) = _$NetworkDetectionStateImpl;
|
||||
|
||||
@override
|
||||
bool get isLoading;
|
||||
@override
|
||||
bool get isTesting;
|
||||
@override
|
||||
IpInfo? get ipInfo;
|
||||
|
||||
/// Create a copy of NetworkDetectionState
|
||||
|
||||
@@ -66,7 +66,6 @@ class ProfilesSelectorState with _$ProfilesSelectorState {
|
||||
class NetworkDetectionState with _$NetworkDetectionState {
|
||||
const factory NetworkDetectionState({
|
||||
required bool isLoading,
|
||||
required bool isTesting,
|
||||
required IpInfo? ipInfo,
|
||||
}) = _NetworkDetectionState;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ class HomePage extends StatelessWidget {
|
||||
final navigationItem = state.navigationItems[index];
|
||||
final navigationView = navigationItem.builder(context);
|
||||
final view = isMobile
|
||||
? navigationView
|
||||
? KeepScope(
|
||||
keep: navigationItem.keep,
|
||||
child: navigationView,
|
||||
)
|
||||
: KeepScope(
|
||||
keep: navigationItem.keep,
|
||||
child: Navigator(
|
||||
@@ -68,7 +71,7 @@ class HomePage extends StatelessWidget {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: MediaQuery.removePadding(
|
||||
removeTop: false,
|
||||
|
||||
@@ -41,7 +41,7 @@ final currentGroupsStateProvider = AutoDisposeProvider<GroupsState>.internal(
|
||||
// ignore: unused_element
|
||||
typedef CurrentGroupsStateRef = AutoDisposeProviderRef<GroupsState>;
|
||||
String _$navigationItemsStateHash() =>
|
||||
r'53133c67446f91a2bb499d24dae3e9a7dc00f60f';
|
||||
r'1fc37c14d129f9725b0e62fd53f6b25382f51102';
|
||||
|
||||
/// See also [navigationItemsState].
|
||||
@ProviderFor(navigationItemsState)
|
||||
|
||||
@@ -64,6 +64,8 @@ GroupsState currentGroupsState(Ref ref) {
|
||||
@riverpod
|
||||
NavigationItemsState navigationItemsState(Ref ref) {
|
||||
final openLogs = ref.watch(appSettingProvider).openLogs;
|
||||
final hasProfiles =
|
||||
ref.watch(profilesProvider.select((state) => state.isNotEmpty));
|
||||
final hasProxies = ref.watch(currentGroupsStateProvider.select(
|
||||
(state) => state.value.isNotEmpty,
|
||||
));
|
||||
@@ -71,7 +73,7 @@ NavigationItemsState navigationItemsState(Ref ref) {
|
||||
return NavigationItemsState(
|
||||
value: navigation.getItems(
|
||||
openLogs: openLogs,
|
||||
hasProxies: !isInit ? true : hasProxies,
|
||||
hasProxies: !isInit ? hasProfiles : hasProxies,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -465,7 +465,6 @@ class DetectionState {
|
||||
|
||||
final state = ValueNotifier<NetworkDetectionState>(
|
||||
const NetworkDetectionState(
|
||||
isTesting: false,
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
),
|
||||
@@ -488,6 +487,12 @@ class DetectionState {
|
||||
);
|
||||
}
|
||||
|
||||
void tryStartCheck() {
|
||||
if (state.value.isLoading == false && state.value.ipInfo == null) {
|
||||
startCheck();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _checkIp() async {
|
||||
final appState = globalState.appState;
|
||||
final isInit = appState.isInit;
|
||||
@@ -509,9 +514,6 @@ class DetectionState {
|
||||
cancelToken = null;
|
||||
}
|
||||
cancelToken = CancelToken();
|
||||
state.value = state.value.copyWith(
|
||||
isTesting: true,
|
||||
);
|
||||
final res = await request.checkIp(cancelToken: cancelToken);
|
||||
if (res.isError) {
|
||||
state.value = state.value.copyWith(
|
||||
@@ -521,9 +523,6 @@ class DetectionState {
|
||||
return;
|
||||
}
|
||||
final ipInfo = res.data;
|
||||
state.value = state.value.copyWith(
|
||||
isTesting: false,
|
||||
);
|
||||
if (ipInfo != null) {
|
||||
state.value = state.value.copyWith(
|
||||
isLoading: false,
|
||||
|
||||
@@ -109,7 +109,11 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
trailing: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
style: ButtonStyle(
|
||||
minimumSize: WidgetStatePropertyAll(Size.zero),
|
||||
),
|
||||
icon: const Icon(Icons.block),
|
||||
onPressed: () {
|
||||
_handleBlockConnection(trackerInfo.id);
|
||||
|
||||
@@ -68,7 +68,6 @@ class TrackerInfoItem extends ConsumerWidget {
|
||||
spacing: 8,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
trackerInfo.desc,
|
||||
maxLines: 1,
|
||||
|
||||
@@ -51,7 +51,7 @@ class _ProxiesListViewState extends State<ProxiesListView> {
|
||||
|
||||
void _adjustHeader() {
|
||||
_headerStateNotifier.value = _getProxiesListHeaderSelectorState(
|
||||
_controller.offset,
|
||||
!_controller.hasClients ? 0 : _controller.offset,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -197,12 +197,15 @@ class ProxiesTabViewState extends ConsumerState<ProxiesTabView>
|
||||
final children = groups.map((group) {
|
||||
final key = GlobalObjectKey<_ProxyGroupViewState>(group.name);
|
||||
keyMap[group.name] = key;
|
||||
return ProxyGroupView(
|
||||
key: key,
|
||||
group: group,
|
||||
columns: state.columns,
|
||||
cardType: state.proxyCardType,
|
||||
sortType: state.proxiesSortType,
|
||||
return KeepScope(
|
||||
keep: true,
|
||||
child: ProxyGroupView(
|
||||
key: key,
|
||||
group: group,
|
||||
columns: state.columns,
|
||||
cardType: state.proxyCardType,
|
||||
sortType: state.proxiesSortType,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
_keyMap = keyMap;
|
||||
|
||||
@@ -92,6 +92,7 @@ class InputDialog extends StatefulWidget {
|
||||
final String? hintText;
|
||||
final FormFieldValidator<String>? validator;
|
||||
final AutovalidateMode? autovalidateMode;
|
||||
final bool? obscureText;
|
||||
|
||||
const InputDialog({
|
||||
super.key,
|
||||
@@ -101,6 +102,7 @@ class InputDialog extends StatefulWidget {
|
||||
this.resetValue,
|
||||
this.hintText,
|
||||
this.validator,
|
||||
this.obscureText,
|
||||
this.labelText,
|
||||
this.autovalidateMode = AutovalidateMode.onUserInteraction,
|
||||
});
|
||||
@@ -168,8 +170,9 @@ class _InputDialogState extends State<InputDialog> {
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextFormField(
|
||||
obscureText: widget.obscureText ?? false,
|
||||
keyboardType: TextInputType.url,
|
||||
maxLines: 5,
|
||||
maxLines: widget.obscureText == true ? 1 : 5,
|
||||
minLines: 1,
|
||||
controller: textController,
|
||||
onFieldSubmitted: (_) {
|
||||
|
||||
@@ -1387,14 +1387,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
two_dimensional_scrollables:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: two_dimensional_scrollables
|
||||
sha256: f02e99bae8b457f710c2a717dfbd0a32a5bbe332c0814bc559d9549a5b117db3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.6"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.87+2025072401
|
||||
version: 0.8.87+2025072902
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -64,7 +64,6 @@ dependencies:
|
||||
flutter_cache_manager: ^3.4.1
|
||||
crypto: ^3.0.3
|
||||
flutter_acrylic: ^1.1.4
|
||||
two_dimensional_scrollables: ^0.3.6
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
Reference in New Issue
Block a user