Support profiles sort
Support windows country flags display Optimize proxies page and profiles page columns
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -87,7 +87,7 @@ jobs:
|
|||||||
- name: Setup Flutter
|
- name: Setup Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.x'
|
flutter-version-file: pubspec.yaml
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
|
|||||||
BIN
assets/fonts/Twemoji.Mozilla.ttf
Normal file
BIN
assets/fonts/Twemoji.Mozilla.ttf
Normal file
Binary file not shown.
@@ -135,7 +135,6 @@ class ApplicationState extends State<Application> {
|
|||||||
builder: (lightDynamic, darkDynamic) {
|
builder: (lightDynamic, darkDynamic) {
|
||||||
_updateSystemColorSchemes(lightDynamic, darkDynamic);
|
_updateSystemColorSchemes(lightDynamic, darkDynamic);
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
navigatorKey: globalState.navigatorKey,
|
navigatorKey: globalState.navigatorKey,
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
AppLocalizations.delegate,
|
AppLocalizations.delegate,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ extension ColorSchemeExtension on ColorScheme {
|
|||||||
ColorScheme toPrueBlack(bool isPrueBlack) => isPrueBlack
|
ColorScheme toPrueBlack(bool isPrueBlack) => isPrueBlack
|
||||||
? copyWith(
|
? copyWith(
|
||||||
surface: Colors.black,
|
surface: Colors.black,
|
||||||
|
background: Colors.black,
|
||||||
surfaceContainer: surfaceContainer.darken(0.05),
|
surfaceContainer: surfaceContainer.darken(0.05),
|
||||||
)
|
)
|
||||||
: this;
|
: this;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class DAVClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>> recovery() async {
|
Future<List<int>> recovery() async {
|
||||||
|
await client.mkdir("$root");
|
||||||
final data = await client.read(backupFile);
|
final data = await client.read(backupFile);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:fl_clash/common/common.dart';
|
import 'package:fl_clash/common/common.dart';
|
||||||
@@ -191,22 +192,18 @@ class Other {
|
|||||||
return ViewMode.desktop;
|
return ViewMode.desktop;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getColumns(ViewMode viewMode, int currentColumns) {
|
|
||||||
final targetColumnsArray = viewModeColumnsMap[viewMode]!;
|
int getProxiesColumns(double viewWidth, ProxiesLayout proxiesLayout) {
|
||||||
if (targetColumnsArray.contains(currentColumns)) {
|
final columns = max((viewWidth / 300).ceil(), 2);
|
||||||
return currentColumns;
|
return switch (proxiesLayout) {
|
||||||
}
|
ProxiesLayout.tight => columns - 1,
|
||||||
return targetColumnsArray.first;
|
ProxiesLayout.standard => columns,
|
||||||
|
ProxiesLayout.loose => columns + 1,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
String getColumnsTextForInt(int number) {
|
int getProfilesColumns(double viewWidth) {
|
||||||
return switch (number) {
|
return max((viewWidth / 400).floor(), 1);
|
||||||
1 => appLocalizations.oneColumn,
|
|
||||||
2 => appLocalizations.twoColumns,
|
|
||||||
3 => appLocalizations.threeColumns,
|
|
||||||
4 => appLocalizations.fourColumns,
|
|
||||||
int() => throw UnimplementedError(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getBackupFileName() {
|
String getBackupFileName() {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
@@ -49,7 +50,9 @@ class Request {
|
|||||||
.get(
|
.get(
|
||||||
url,
|
url,
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {"User-Agent": globalState.appController.clashConfig.globalUa},
|
headers: {
|
||||||
|
"User-Agent": globalState.appController.clashConfig.globalUa
|
||||||
|
},
|
||||||
responseType: ResponseType.bytes,
|
responseType: ResponseType.bytes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -84,10 +87,13 @@ class Request {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Future<IpInfo?> checkIp({CancelToken? cancelToken}) async {
|
Future<IpInfo?> checkIp({CancelToken? cancelToken}) async {
|
||||||
for (final source in _ipInfoSources.entries) {
|
for (final source in _ipInfoSources.entries.toList()..shuffle(Random())) {
|
||||||
try {
|
try {
|
||||||
final response = await _dio
|
final response = await _dio
|
||||||
.get<Map<String, dynamic>>(source.key, cancelToken: cancelToken)
|
.get<Map<String, dynamic>>(
|
||||||
|
source.key,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
)
|
||||||
.timeout(
|
.timeout(
|
||||||
httpTimeoutDuration,
|
httpTimeoutDuration,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:archive/archive.dart';
|
import 'package:archive/archive.dart';
|
||||||
import 'package:fl_clash/common/archive.dart';
|
import 'package:fl_clash/common/archive.dart';
|
||||||
@@ -414,7 +415,6 @@ class AppController {
|
|||||||
addProfileFormURL(url);
|
addProfileFormURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get columns => other.getColumns(appState.viewMode, config.proxiesColumns);
|
|
||||||
|
|
||||||
updateViewWidth(double width) {
|
updateViewWidth(double width) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
|||||||
@@ -86,4 +86,6 @@ enum CommonCardType { plain, filled }
|
|||||||
|
|
||||||
enum ProxiesType { tab, list }
|
enum ProxiesType { tab, list }
|
||||||
|
|
||||||
|
enum ProxiesLayout{ loose, standard, tight }
|
||||||
|
|
||||||
enum ProxyCardType { expand, shrink, min }
|
enum ProxyCardType { expand, shrink, min }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:country_flags/country_flags.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:fl_clash/common/common.dart';
|
import 'package:fl_clash/common/common.dart';
|
||||||
import 'package:fl_clash/models/models.dart';
|
import 'package:fl_clash/models/models.dart';
|
||||||
import 'package:fl_clash/state.dart';
|
import 'package:fl_clash/state.dart';
|
||||||
@@ -18,6 +18,7 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
|||||||
final timeoutNotifier = ValueNotifier<bool>(false);
|
final timeoutNotifier = ValueNotifier<bool>(false);
|
||||||
bool? _preIsStart;
|
bool? _preIsStart;
|
||||||
Function? _checkIpDebounce;
|
Function? _checkIpDebounce;
|
||||||
|
CancelToken? cancelToken;
|
||||||
|
|
||||||
_checkIp() async {
|
_checkIp() async {
|
||||||
final appState = globalState.appController.appState;
|
final appState = globalState.appController.appState;
|
||||||
@@ -28,13 +29,19 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
|||||||
if (_preIsStart == false && _preIsStart == isStart) return;
|
if (_preIsStart == false && _preIsStart == isStart) return;
|
||||||
_preIsStart = isStart;
|
_preIsStart = isStart;
|
||||||
ipInfoNotifier.value = null;
|
ipInfoNotifier.value = null;
|
||||||
final ipInfo = await request.checkIp();
|
if (cancelToken != null) {
|
||||||
|
cancelToken!.cancel();
|
||||||
|
_preIsStart = null;
|
||||||
|
timeoutNotifier.value == false;
|
||||||
|
cancelToken = null;
|
||||||
|
}
|
||||||
|
cancelToken = CancelToken();
|
||||||
|
final ipInfo = await request.checkIp(cancelToken: cancelToken);
|
||||||
if (ipInfo == null) {
|
if (ipInfo == null) {
|
||||||
timeoutNotifier.value = true;
|
timeoutNotifier.value = true;
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
timeoutNotifier.value = false;
|
|
||||||
}
|
}
|
||||||
|
timeoutNotifier.value = false;
|
||||||
ipInfoNotifier.value = ipInfo;
|
ipInfoNotifier.value = ipInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,9 +67,19 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
|||||||
timeoutNotifier.dispose();
|
timeoutNotifier.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String countryCodeToEmoji(String countryCode) {
|
||||||
|
final String code = countryCode.toUpperCase();
|
||||||
|
if (code.length != 2) {
|
||||||
|
return countryCode;
|
||||||
|
}
|
||||||
|
final int firstLetter = code.codeUnitAt(0) - 0x41 + 0x1F1E6;
|
||||||
|
final int secondLetter = code.codeUnitAt(1) - 0x41 + 0x1F1E6;
|
||||||
|
return String.fromCharCode(firstLetter) + String.fromCharCode(secondLetter);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_checkIpDebounce = debounce(_checkIp);
|
_checkIpDebounce ??= debounce(_checkIp);
|
||||||
return _checkIpContainer(
|
return _checkIpContainer(
|
||||||
ValueListenableBuilder<IpInfo?>(
|
ValueListenableBuilder<IpInfo?>(
|
||||||
valueListenable: ipInfoNotifier,
|
valueListenable: ipInfoNotifier,
|
||||||
@@ -88,10 +105,19 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
child: FadeBox(
|
child: FadeBox(
|
||||||
child: ipInfo != null
|
child: ipInfo != null
|
||||||
? CountryFlag.fromCountryCode(
|
? Container(
|
||||||
ipInfo.countryCode,
|
alignment: Alignment.centerLeft,
|
||||||
width: 24,
|
height: globalState.appController.measure
|
||||||
height: 24,
|
.titleMediumHeight,
|
||||||
|
child: Text(
|
||||||
|
countryCodeToEmoji(ipInfo.countryCode),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleLarge
|
||||||
|
?.copyWith(
|
||||||
|
fontFamily: "Twemoji",
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: ValueListenableBuilder(
|
: ValueListenableBuilder(
|
||||||
valueListenable: timeoutNotifier,
|
valueListenable: timeoutNotifier,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:fl_clash/enum/enum.dart';
|
import 'package:fl_clash/enum/enum.dart';
|
||||||
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
|
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
|
||||||
import 'package:fl_clash/models/models.dart';
|
import 'package:fl_clash/models/models.dart';
|
||||||
@@ -37,21 +39,11 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getColumns(ViewMode viewMode) {
|
|
||||||
switch (viewMode) {
|
|
||||||
case ViewMode.mobile:
|
|
||||||
return 1;
|
|
||||||
case ViewMode.laptop:
|
|
||||||
return 1;
|
|
||||||
case ViewMode.desktop:
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateProfiles() async {
|
_updateProfiles() async {
|
||||||
final appController = globalState.appController;
|
final appController = globalState.appController;
|
||||||
final config = appController.config;
|
final config = appController.config;
|
||||||
final profiles = appController.config.profiles;
|
final profiles = appController.config.profiles;
|
||||||
|
final messages = [];
|
||||||
final updateProfiles = profiles.map<Future>(
|
final updateProfiles = profiles.map<Future>(
|
||||||
(profile) async {
|
(profile) async {
|
||||||
config.setProfile(
|
config.setProfile(
|
||||||
@@ -62,7 +54,8 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
if (profile.id == appController.config.currentProfile?.id) {
|
if (profile.id == appController.config.currentProfile?.id) {
|
||||||
appController.applyProfile(isPrue: true);
|
appController.applyProfile(isPrue: true);
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (e) {
|
||||||
|
messages.add("${profile.label ?? profile.id}: $e \n");
|
||||||
config.setProfile(
|
config.setProfile(
|
||||||
profile.copyWith(
|
profile.copyWith(
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
@@ -71,13 +64,25 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
final titleMedium = context.textTheme.titleMedium;
|
||||||
await Future.wait(updateProfiles);
|
await Future.wait(updateProfiles);
|
||||||
|
if (messages.isNotEmpty) {
|
||||||
|
globalState.showMessage(
|
||||||
|
title: appLocalizations.tip,
|
||||||
|
message: TextSpan(
|
||||||
|
children: [
|
||||||
|
for (final message in messages)
|
||||||
|
TextSpan(text: message, style: titleMedium)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_initScaffoldState() {
|
_initScaffoldState() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
(_) {
|
(_) {
|
||||||
if (!context.mounted) return;
|
if (!mounted) return;
|
||||||
final commonScaffoldState =
|
final commonScaffoldState =
|
||||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||||
commonScaffoldState?.actions = [
|
commonScaffoldState?.actions = [
|
||||||
@@ -87,6 +92,24 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
},
|
},
|
||||||
icon: const Icon(Icons.sync),
|
icon: const Icon(Icons.sync),
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
final profiles = globalState.appController.config.profiles;
|
||||||
|
showSheet(
|
||||||
|
title: appLocalizations.profilesSort,
|
||||||
|
context: context,
|
||||||
|
builder: (_) => SizedBox(
|
||||||
|
height: 400,
|
||||||
|
child: ReorderableProfiles(profiles: profiles),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.sort),
|
||||||
|
iconSize: 26,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -116,7 +139,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
selector: (_, appState, config) => ProfilesSelectorState(
|
selector: (_, appState, config) => ProfilesSelectorState(
|
||||||
profiles: config.profiles,
|
profiles: config.profiles,
|
||||||
currentProfileId: config.currentProfileId,
|
currentProfileId: config.currentProfileId,
|
||||||
viewMode: appState.viewMode,
|
columns: other.getProfilesColumns(appState.viewWidth),
|
||||||
),
|
),
|
||||||
builder: (context, state, child) {
|
builder: (context, state, child) {
|
||||||
if (state.profiles.isEmpty) {
|
if (state.profiles.isEmpty) {
|
||||||
@@ -124,7 +147,6 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
label: appLocalizations.nullProfileDesc,
|
label: appLocalizations.nullProfileDesc,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final columns = _getColumns(state.viewMode);
|
|
||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -137,7 +159,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
child: Grid(
|
child: Grid(
|
||||||
mainAxisSpacing: 16,
|
mainAxisSpacing: 16,
|
||||||
crossAxisSpacing: 16,
|
crossAxisSpacing: 16,
|
||||||
crossAxisCount: columns,
|
crossAxisCount: state.columns,
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < state.profiles.length; i++)
|
for (int i = 0; i < state.profiles.length; i++)
|
||||||
GridItem(
|
GridItem(
|
||||||
@@ -145,8 +167,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
|||||||
key: Key(state.profiles[i].id),
|
key: Key(state.profiles[i].id),
|
||||||
profile: state.profiles[i],
|
profile: state.profiles[i],
|
||||||
groupValue: state.currentProfileId,
|
groupValue: state.currentProfileId,
|
||||||
onChanged:
|
onChanged: globalState.appController.changeProfile,
|
||||||
globalState.appController.changeProfile,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -244,6 +265,7 @@ class ProfileItem extends StatelessWidget {
|
|||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
minHeight: 6,
|
minHeight: 6,
|
||||||
value: progress,
|
value: progress,
|
||||||
|
backgroundColor: context.colorScheme.primary.toSoft(),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
@@ -372,3 +394,129 @@ class ProfileItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReorderableProfiles extends StatefulWidget {
|
||||||
|
final List<Profile> profiles;
|
||||||
|
|
||||||
|
const ReorderableProfiles({
|
||||||
|
super.key,
|
||||||
|
required this.profiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReorderableProfiles> createState() => _ReorderableProfilesState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReorderableProfilesState extends State<ReorderableProfiles> {
|
||||||
|
late List<Profile> profiles;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
profiles = List.from(widget.profiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget proxyDecorator(
|
||||||
|
Widget child,
|
||||||
|
int index,
|
||||||
|
Animation<double> animation,
|
||||||
|
) {
|
||||||
|
final profile = profiles[index];
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: animation,
|
||||||
|
builder: (_, Widget? child) {
|
||||||
|
final double animValue = Curves.easeInOut.transform(animation.value);
|
||||||
|
final double scale = lerpDouble(1, 1.02, animValue)!;
|
||||||
|
return Transform.scale(
|
||||||
|
scale: scale,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
key: Key(profile.id),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: CommonCard(
|
||||||
|
type: CommonCardType.filled,
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.only(
|
||||||
|
right: 44,
|
||||||
|
left: 16,
|
||||||
|
),
|
||||||
|
title: Text(profile.label ?? profile.id),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: ReorderableListView.builder(
|
||||||
|
buildDefaultDragHandles: false,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
proxyDecorator: proxyDecorator,
|
||||||
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
|
if (oldIndex == newIndex) return;
|
||||||
|
setState(() {
|
||||||
|
if (oldIndex < newIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
final profile = profiles.removeAt(oldIndex);
|
||||||
|
profiles.insert(newIndex, profile);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final profile = profiles[index];
|
||||||
|
return Container(
|
||||||
|
key: Key(profile.id),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: CommonCard(
|
||||||
|
type: CommonCardType.filled,
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.only(
|
||||||
|
right: 16,
|
||||||
|
left: 16,
|
||||||
|
),
|
||||||
|
title: Text(profile.label ?? profile.id),
|
||||||
|
trailing: ReorderableDragStartListener(
|
||||||
|
index: index,
|
||||||
|
child: const Icon(Icons.drag_handle),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: profiles.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 12,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
globalState.appController.config.profiles = profiles;
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.check,
|
||||||
|
),
|
||||||
|
iconSize: 32,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,23 +69,21 @@ class ProxyCard extends StatelessWidget {
|
|||||||
if (type == ProxyCardType.min) {
|
if (type == ProxyCardType.min) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: measure.bodyMediumHeight * 1,
|
height: measure.bodyMediumHeight * 1,
|
||||||
child: Text(
|
child: EmojiText(
|
||||||
proxy.name,
|
proxy.name,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: measure.bodyMediumHeight * 2,
|
height: measure.bodyMediumHeight * 2,
|
||||||
child: Text(
|
child: EmojiText(
|
||||||
proxy.name,
|
proxy.name,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -155,14 +153,12 @@ class ProxyCard extends StatelessWidget {
|
|||||||
proxy.name,
|
proxy.name,
|
||||||
),
|
),
|
||||||
builder: (_, desc, __) {
|
builder: (_, desc, __) {
|
||||||
return TooltipText(
|
return EmojiText(
|
||||||
text: Text(
|
desc,
|
||||||
desc,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
overflow: TextOverflow.ellipsis,
|
color: context.textTheme.bodySmall?.color
|
||||||
color: context.textTheme.bodySmall?.color
|
?.toLight(),
|
||||||
?.toLight(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:fl_clash/clash/clash.dart';
|
import 'package:fl_clash/clash/clash.dart';
|
||||||
|
import 'package:fl_clash/common/other.dart';
|
||||||
import 'package:fl_clash/enum/enum.dart';
|
import 'package:fl_clash/enum/enum.dart';
|
||||||
import 'package:fl_clash/models/models.dart';
|
import 'package:fl_clash/models/models.dart';
|
||||||
import 'package:fl_clash/state.dart';
|
import 'package:fl_clash/state.dart';
|
||||||
@@ -60,7 +61,10 @@ double getScrollToSelectedOffset({
|
|||||||
required List<Proxy> proxies,
|
required List<Proxy> proxies,
|
||||||
}) {
|
}) {
|
||||||
final appController = globalState.appController;
|
final appController = globalState.appController;
|
||||||
final columns = appController.columns;
|
final columns = other.getProxiesColumns(
|
||||||
|
appController.appState.viewWidth,
|
||||||
|
appController.config.proxiesLayout,
|
||||||
|
);
|
||||||
final proxyCardType = appController.config.proxyCardType;
|
final proxyCardType = appController.config.proxyCardType;
|
||||||
final selectedName = appController.getCurrentSelectedName(groupName);
|
final selectedName = appController.getCurrentSelectedName(groupName);
|
||||||
final findSelectedIndex = proxies.indexWhere(
|
final findSelectedIndex = proxies.indexWhere(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:fl_clash/enum/enum.dart';
|
|||||||
import 'package:fl_clash/models/models.dart';
|
import 'package:fl_clash/models/models.dart';
|
||||||
import 'package:fl_clash/state.dart';
|
import 'package:fl_clash/state.dart';
|
||||||
import 'package:fl_clash/widgets/card.dart';
|
import 'package:fl_clash/widgets/card.dart';
|
||||||
|
import 'package:fl_clash/widgets/text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -237,7 +238,10 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
|
|||||||
currentUnfoldSet: config.currentUnfoldSet,
|
currentUnfoldSet: config.currentUnfoldSet,
|
||||||
proxyCardType: config.proxyCardType,
|
proxyCardType: config.proxyCardType,
|
||||||
proxiesSortType: config.proxiesSortType,
|
proxiesSortType: config.proxiesSortType,
|
||||||
columns: globalState.appController.columns,
|
columns: other.getProxiesColumns(
|
||||||
|
appState.viewWidth,
|
||||||
|
config.proxiesLayout,
|
||||||
|
),
|
||||||
sortNum: appState.sortNum,
|
sortNum: appState.sortNum,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -450,7 +454,7 @@ class _ListHeaderState extends State<ListHeader>
|
|||||||
if (currentGroupName.isNotEmpty) ...[
|
if (currentGroupName.isNotEmpty) ...[
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Text(
|
child: EmojiText(
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
" · $currentGroupName",
|
" · $currentGroupName",
|
||||||
style: context
|
style: context
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ class ProxiesSettingWidget extends StatelessWidget {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getTextForProxiesLayout(ProxiesLayout proxiesLayout) {
|
||||||
|
return switch (proxiesLayout) {
|
||||||
|
ProxiesLayout.tight => appLocalizations.tight,
|
||||||
|
ProxiesLayout.standard => appLocalizations.standard,
|
||||||
|
ProxiesLayout.loose => appLocalizations.loose,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> _buildStyleSetting() {
|
List<Widget> _buildStyleSetting() {
|
||||||
return generateSection(
|
return generateSection(
|
||||||
title: appLocalizations.style,
|
title: appLocalizations.style,
|
||||||
@@ -132,36 +140,28 @@ class ProxiesSettingWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildColumnsSetting() {
|
List<Widget> _buildLayoutSetting() {
|
||||||
return generateSection(
|
return generateSection(
|
||||||
title: appLocalizations.columns,
|
title: appLocalizations.layout,
|
||||||
items: [
|
items: [
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Selector2<AppState, Config, ColumnsSelectorState>(
|
child: Selector< Config, ProxiesLayout>(
|
||||||
selector: (_, appState, config) => ColumnsSelectorState(
|
selector: (_, config) => config.proxiesLayout,
|
||||||
columns: config.proxiesColumns,
|
builder: (_, proxiesLayout, __) {
|
||||||
viewMode: appState.viewMode,
|
|
||||||
),
|
|
||||||
builder: (_, state, __) {
|
|
||||||
final config = globalState.appController.config;
|
final config = globalState.appController.config;
|
||||||
final targetColumnsArray = viewModeColumnsMap[state.viewMode]!;
|
|
||||||
final currentColumns = other.getColumns(
|
|
||||||
state.viewMode,
|
|
||||||
state.columns,
|
|
||||||
);
|
|
||||||
return Wrap(
|
return Wrap(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
for (final item in targetColumnsArray)
|
for (final item in ProxiesLayout.values)
|
||||||
SettingTextCard(
|
SettingTextCard(
|
||||||
other.getColumnsTextForInt(item),
|
getTextForProxiesLayout(item),
|
||||||
isSelected: item == currentColumns,
|
isSelected: item == proxiesLayout,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
config.proxiesColumns = item;
|
config.proxiesLayout = item;
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -183,10 +183,10 @@ class ProxiesSettingWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
..._buildStyleSetting(),
|
..._buildStyleSetting(),
|
||||||
..._buildSortSetting(),
|
..._buildSortSetting(),
|
||||||
..._buildColumnsSetting(),
|
..._buildLayoutSetting(),
|
||||||
..._buildSizeSetting(),
|
..._buildSizeSetting(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,7 +284,10 @@ class ProxyGroupViewState extends State<ProxyGroupView> {
|
|||||||
return ProxyGroupSelectorState(
|
return ProxyGroupSelectorState(
|
||||||
proxyCardType: config.proxyCardType,
|
proxyCardType: config.proxyCardType,
|
||||||
proxiesSortType: config.proxiesSortType,
|
proxiesSortType: config.proxiesSortType,
|
||||||
columns: globalState.appController.columns,
|
columns: other.getProxiesColumns(
|
||||||
|
appState.viewWidth,
|
||||||
|
config.proxiesLayout,
|
||||||
|
),
|
||||||
sortNum: appState.sortNum,
|
sortNum: appState.sortNum,
|
||||||
proxies: group.all,
|
proxies: group.all,
|
||||||
groupType: group.type,
|
groupType: group.type,
|
||||||
|
|||||||
@@ -236,5 +236,10 @@
|
|||||||
"action": "Action",
|
"action": "Action",
|
||||||
"intelligentSelected": "Intelligent selection",
|
"intelligentSelected": "Intelligent selection",
|
||||||
"clipboardImport": "Clipboard import",
|
"clipboardImport": "Clipboard import",
|
||||||
"clipboardExport": "Export clipboard"
|
"clipboardExport": "Export clipboard",
|
||||||
|
"layout": "Layout",
|
||||||
|
"tight": "Tight",
|
||||||
|
"standard": "Standard",
|
||||||
|
"loose": "Loose",
|
||||||
|
"profilesSort": "Profiles sort"
|
||||||
}
|
}
|
||||||
@@ -236,5 +236,10 @@
|
|||||||
"action": "操作",
|
"action": "操作",
|
||||||
"intelligentSelected": "智能选择",
|
"intelligentSelected": "智能选择",
|
||||||
"clipboardImport": "剪贴板导入",
|
"clipboardImport": "剪贴板导入",
|
||||||
"clipboardExport": "导出剪贴板"
|
"clipboardExport": "导出剪贴板",
|
||||||
|
"layout": "布局",
|
||||||
|
"tight": "宽松",
|
||||||
|
"standard": "标准",
|
||||||
|
"loose": "紧凑",
|
||||||
|
"profilesSort": "配置排序"
|
||||||
}
|
}
|
||||||
@@ -182,6 +182,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"keepAliveIntervalDesc":
|
"keepAliveIntervalDesc":
|
||||||
MessageLookupByLibrary.simpleMessage("Tcp keep alive interval"),
|
MessageLookupByLibrary.simpleMessage("Tcp keep alive interval"),
|
||||||
"language": MessageLookupByLibrary.simpleMessage("Language"),
|
"language": MessageLookupByLibrary.simpleMessage("Language"),
|
||||||
|
"layout": MessageLookupByLibrary.simpleMessage("Layout"),
|
||||||
"light": MessageLookupByLibrary.simpleMessage("Light"),
|
"light": MessageLookupByLibrary.simpleMessage("Light"),
|
||||||
"list": MessageLookupByLibrary.simpleMessage("List"),
|
"list": MessageLookupByLibrary.simpleMessage("List"),
|
||||||
"local": MessageLookupByLibrary.simpleMessage("Local"),
|
"local": MessageLookupByLibrary.simpleMessage("Local"),
|
||||||
@@ -195,6 +196,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"Disabling will hide the log entry"),
|
"Disabling will hide the log entry"),
|
||||||
"logs": MessageLookupByLibrary.simpleMessage("Logs"),
|
"logs": MessageLookupByLibrary.simpleMessage("Logs"),
|
||||||
"logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"),
|
"logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"),
|
||||||
|
"loose": MessageLookupByLibrary.simpleMessage("Loose"),
|
||||||
"min": MessageLookupByLibrary.simpleMessage("Min"),
|
"min": MessageLookupByLibrary.simpleMessage("Min"),
|
||||||
"minimizeOnExit":
|
"minimizeOnExit":
|
||||||
MessageLookupByLibrary.simpleMessage("Minimize on exit"),
|
MessageLookupByLibrary.simpleMessage("Minimize on exit"),
|
||||||
@@ -266,6 +268,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"profileUrlNullValidationDesc": MessageLookupByLibrary.simpleMessage(
|
"profileUrlNullValidationDesc": MessageLookupByLibrary.simpleMessage(
|
||||||
"Please input the profile URL"),
|
"Please input the profile URL"),
|
||||||
"profiles": MessageLookupByLibrary.simpleMessage("Profiles"),
|
"profiles": MessageLookupByLibrary.simpleMessage("Profiles"),
|
||||||
|
"profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"),
|
||||||
"project": MessageLookupByLibrary.simpleMessage("Project"),
|
"project": MessageLookupByLibrary.simpleMessage("Project"),
|
||||||
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
|
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
|
||||||
"proxiesSetting":
|
"proxiesSetting":
|
||||||
@@ -312,6 +315,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"size": MessageLookupByLibrary.simpleMessage("Size"),
|
"size": MessageLookupByLibrary.simpleMessage("Size"),
|
||||||
"sort": MessageLookupByLibrary.simpleMessage("Sort"),
|
"sort": MessageLookupByLibrary.simpleMessage("Sort"),
|
||||||
"source": MessageLookupByLibrary.simpleMessage("Source"),
|
"source": MessageLookupByLibrary.simpleMessage("Source"),
|
||||||
|
"standard": MessageLookupByLibrary.simpleMessage("Standard"),
|
||||||
"startVpn": MessageLookupByLibrary.simpleMessage("Staring VPN..."),
|
"startVpn": MessageLookupByLibrary.simpleMessage("Staring VPN..."),
|
||||||
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
|
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
|
||||||
"style": MessageLookupByLibrary.simpleMessage("Style"),
|
"style": MessageLookupByLibrary.simpleMessage("Style"),
|
||||||
@@ -334,6 +338,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"Set dark mode,adjust the color"),
|
"Set dark mode,adjust the color"),
|
||||||
"themeMode": MessageLookupByLibrary.simpleMessage("Theme mode"),
|
"themeMode": MessageLookupByLibrary.simpleMessage("Theme mode"),
|
||||||
"threeColumns": MessageLookupByLibrary.simpleMessage("Three columns"),
|
"threeColumns": MessageLookupByLibrary.simpleMessage("Three columns"),
|
||||||
|
"tight": MessageLookupByLibrary.simpleMessage("Tight"),
|
||||||
"time": MessageLookupByLibrary.simpleMessage("Time"),
|
"time": MessageLookupByLibrary.simpleMessage("Time"),
|
||||||
"tip": MessageLookupByLibrary.simpleMessage("tip"),
|
"tip": MessageLookupByLibrary.simpleMessage("tip"),
|
||||||
"tools": MessageLookupByLibrary.simpleMessage("Tools"),
|
"tools": MessageLookupByLibrary.simpleMessage("Tools"),
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"keepAliveIntervalDesc":
|
"keepAliveIntervalDesc":
|
||||||
MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
|
MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
|
||||||
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
||||||
|
"layout": MessageLookupByLibrary.simpleMessage("布局"),
|
||||||
"light": MessageLookupByLibrary.simpleMessage("浅色"),
|
"light": MessageLookupByLibrary.simpleMessage("浅色"),
|
||||||
"list": MessageLookupByLibrary.simpleMessage("列表"),
|
"list": MessageLookupByLibrary.simpleMessage("列表"),
|
||||||
"local": MessageLookupByLibrary.simpleMessage("本地"),
|
"local": MessageLookupByLibrary.simpleMessage("本地"),
|
||||||
@@ -157,6 +158,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"),
|
"logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"),
|
||||||
"logs": MessageLookupByLibrary.simpleMessage("日志"),
|
"logs": MessageLookupByLibrary.simpleMessage("日志"),
|
||||||
"logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"),
|
"logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"),
|
||||||
|
"loose": MessageLookupByLibrary.simpleMessage("紧凑"),
|
||||||
"min": MessageLookupByLibrary.simpleMessage("最小"),
|
"min": MessageLookupByLibrary.simpleMessage("最小"),
|
||||||
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
|
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
|
||||||
"minimizeOnExitDesc":
|
"minimizeOnExitDesc":
|
||||||
@@ -214,6 +216,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"profileUrlNullValidationDesc":
|
"profileUrlNullValidationDesc":
|
||||||
MessageLookupByLibrary.simpleMessage("请输入配置URL"),
|
MessageLookupByLibrary.simpleMessage("请输入配置URL"),
|
||||||
"profiles": MessageLookupByLibrary.simpleMessage("配置"),
|
"profiles": MessageLookupByLibrary.simpleMessage("配置"),
|
||||||
|
"profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"),
|
||||||
"project": MessageLookupByLibrary.simpleMessage("项目"),
|
"project": MessageLookupByLibrary.simpleMessage("项目"),
|
||||||
"proxies": MessageLookupByLibrary.simpleMessage("代理"),
|
"proxies": MessageLookupByLibrary.simpleMessage("代理"),
|
||||||
"proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"),
|
"proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"),
|
||||||
@@ -249,6 +252,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"size": MessageLookupByLibrary.simpleMessage("尺寸"),
|
"size": MessageLookupByLibrary.simpleMessage("尺寸"),
|
||||||
"sort": MessageLookupByLibrary.simpleMessage("排序"),
|
"sort": MessageLookupByLibrary.simpleMessage("排序"),
|
||||||
"source": MessageLookupByLibrary.simpleMessage("来源"),
|
"source": MessageLookupByLibrary.simpleMessage("来源"),
|
||||||
|
"standard": MessageLookupByLibrary.simpleMessage("标准"),
|
||||||
"startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."),
|
"startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."),
|
||||||
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
|
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
|
||||||
"style": MessageLookupByLibrary.simpleMessage("风格"),
|
"style": MessageLookupByLibrary.simpleMessage("风格"),
|
||||||
@@ -269,6 +273,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
|
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
|
||||||
"themeMode": MessageLookupByLibrary.simpleMessage("主题模式"),
|
"themeMode": MessageLookupByLibrary.simpleMessage("主题模式"),
|
||||||
"threeColumns": MessageLookupByLibrary.simpleMessage("三列"),
|
"threeColumns": MessageLookupByLibrary.simpleMessage("三列"),
|
||||||
|
"tight": MessageLookupByLibrary.simpleMessage("宽松"),
|
||||||
"time": MessageLookupByLibrary.simpleMessage("时间"),
|
"time": MessageLookupByLibrary.simpleMessage("时间"),
|
||||||
"tip": MessageLookupByLibrary.simpleMessage("提示"),
|
"tip": MessageLookupByLibrary.simpleMessage("提示"),
|
||||||
"tools": MessageLookupByLibrary.simpleMessage("工具"),
|
"tools": MessageLookupByLibrary.simpleMessage("工具"),
|
||||||
|
|||||||
@@ -2429,6 +2429,56 @@ class AppLocalizations {
|
|||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Layout`
|
||||||
|
String get layout {
|
||||||
|
return Intl.message(
|
||||||
|
'Layout',
|
||||||
|
name: 'layout',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Tight`
|
||||||
|
String get tight {
|
||||||
|
return Intl.message(
|
||||||
|
'Tight',
|
||||||
|
name: 'tight',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Standard`
|
||||||
|
String get standard {
|
||||||
|
return Intl.message(
|
||||||
|
'Standard',
|
||||||
|
name: 'standard',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Loose`
|
||||||
|
String get loose {
|
||||||
|
return Intl.message(
|
||||||
|
'Loose',
|
||||||
|
name: 'loose',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Profiles sort`
|
||||||
|
String get profilesSort {
|
||||||
|
return Intl.message(
|
||||||
|
'Profiles sort',
|
||||||
|
name: 'profilesSort',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ class AccessControl with _$AccessControl {
|
|||||||
|
|
||||||
extension AccessControlExt on AccessControl {
|
extension AccessControlExt on AccessControl {
|
||||||
List<String> get currentList => switch (mode) {
|
List<String> get currentList => switch (mode) {
|
||||||
AccessControlMode.acceptSelected => acceptList,
|
AccessControlMode.acceptSelected => acceptList,
|
||||||
AccessControlMode.rejectSelected => rejectList,
|
AccessControlMode.rejectSelected => rejectList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -44,7 +44,8 @@ class CoreState with _$CoreState {
|
|||||||
required bool onlyProxy,
|
required bool onlyProxy,
|
||||||
}) = _CoreState;
|
}) = _CoreState;
|
||||||
|
|
||||||
factory CoreState.fromJson(Map<String, Object?> json) => _$CoreStateFromJson(json);
|
factory CoreState.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$CoreStateFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -87,7 +88,7 @@ class Config extends ChangeNotifier {
|
|||||||
bool _isCloseConnections;
|
bool _isCloseConnections;
|
||||||
ProxiesType _proxiesType;
|
ProxiesType _proxiesType;
|
||||||
ProxyCardType _proxyCardType;
|
ProxyCardType _proxyCardType;
|
||||||
int _proxiesColumns;
|
ProxiesLayout _proxiesLayout;
|
||||||
String _testUrl;
|
String _testUrl;
|
||||||
WindowProps _windowProps;
|
WindowProps _windowProps;
|
||||||
bool _onlyProxy;
|
bool _onlyProxy;
|
||||||
@@ -116,9 +117,9 @@ class Config extends ChangeNotifier {
|
|||||||
_proxyCardType = ProxyCardType.expand,
|
_proxyCardType = ProxyCardType.expand,
|
||||||
_windowProps = defaultWindowProps,
|
_windowProps = defaultWindowProps,
|
||||||
_proxiesType = ProxiesType.tab,
|
_proxiesType = ProxiesType.tab,
|
||||||
_proxiesColumns = 2,
|
|
||||||
_prueBlack = false,
|
_prueBlack = false,
|
||||||
_onlyProxy = false;
|
_onlyProxy = false,
|
||||||
|
_proxiesLayout = ProxiesLayout.standard;
|
||||||
|
|
||||||
deleteProfileById(String id) {
|
deleteProfileById(String id) {
|
||||||
_profiles = profiles.where((element) => element.id != id).toList();
|
_profiles = profiles.where((element) => element.id != id).toList();
|
||||||
@@ -320,6 +321,16 @@ class Config extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonKey(defaultValue: ProxiesLayout.standard)
|
||||||
|
ProxiesLayout get proxiesLayout => _proxiesLayout;
|
||||||
|
|
||||||
|
set proxiesLayout(ProxiesLayout value) {
|
||||||
|
if (_proxiesLayout != value) {
|
||||||
|
_proxiesLayout = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JsonKey(defaultValue: true)
|
@JsonKey(defaultValue: true)
|
||||||
bool get isMinimizeOnExit => _isMinimizeOnExit;
|
bool get isMinimizeOnExit => _isMinimizeOnExit;
|
||||||
|
|
||||||
@@ -481,16 +492,6 @@ class Config extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(defaultValue: 2)
|
|
||||||
int get proxiesColumns => _proxiesColumns;
|
|
||||||
|
|
||||||
set proxiesColumns(int value) {
|
|
||||||
if (_proxiesColumns != value) {
|
|
||||||
_proxiesColumns = value;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(name: "test-url", defaultValue: defaultTestUrl)
|
@JsonKey(name: "test-url", defaultValue: defaultTestUrl)
|
||||||
String get testUrl => _testUrl;
|
String get testUrl => _testUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
|
|||||||
..proxiesSortType =
|
..proxiesSortType =
|
||||||
$enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['proxiesSortType']) ??
|
$enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['proxiesSortType']) ??
|
||||||
ProxiesSortType.none
|
ProxiesSortType.none
|
||||||
|
..proxiesLayout =
|
||||||
|
$enumDecodeNullable(_$ProxiesLayoutEnumMap, json['proxiesLayout']) ??
|
||||||
|
ProxiesLayout.standard
|
||||||
..isMinimizeOnExit = json['isMinimizeOnExit'] as bool? ?? true
|
..isMinimizeOnExit = json['isMinimizeOnExit'] as bool? ?? true
|
||||||
..isAccessControl = json['isAccessControl'] as bool? ?? false
|
..isAccessControl = json['isAccessControl'] as bool? ?? false
|
||||||
..accessControl =
|
..accessControl =
|
||||||
@@ -44,7 +47,6 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
|
|||||||
..proxyCardType =
|
..proxyCardType =
|
||||||
$enumDecodeNullable(_$ProxyCardTypeEnumMap, json['proxyCardType']) ??
|
$enumDecodeNullable(_$ProxyCardTypeEnumMap, json['proxyCardType']) ??
|
||||||
ProxyCardType.expand
|
ProxyCardType.expand
|
||||||
..proxiesColumns = (json['proxiesColumns'] as num?)?.toInt() ?? 2
|
|
||||||
..testUrl =
|
..testUrl =
|
||||||
json['test-url'] as String? ?? 'https://www.gstatic.com/generate_204'
|
json['test-url'] as String? ?? 'https://www.gstatic.com/generate_204'
|
||||||
..isExclude = json['isExclude'] as bool? ?? false
|
..isExclude = json['isExclude'] as bool? ?? false
|
||||||
@@ -62,6 +64,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
|||||||
'locale': instance.locale,
|
'locale': instance.locale,
|
||||||
'primaryColor': instance.primaryColor,
|
'primaryColor': instance.primaryColor,
|
||||||
'proxiesSortType': _$ProxiesSortTypeEnumMap[instance.proxiesSortType]!,
|
'proxiesSortType': _$ProxiesSortTypeEnumMap[instance.proxiesSortType]!,
|
||||||
|
'proxiesLayout': _$ProxiesLayoutEnumMap[instance.proxiesLayout]!,
|
||||||
'isMinimizeOnExit': instance.isMinimizeOnExit,
|
'isMinimizeOnExit': instance.isMinimizeOnExit,
|
||||||
'isAccessControl': instance.isAccessControl,
|
'isAccessControl': instance.isAccessControl,
|
||||||
'accessControl': instance.accessControl,
|
'accessControl': instance.accessControl,
|
||||||
@@ -76,7 +79,6 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
|||||||
'isCloseConnections': instance.isCloseConnections,
|
'isCloseConnections': instance.isCloseConnections,
|
||||||
'proxiesType': _$ProxiesTypeEnumMap[instance.proxiesType]!,
|
'proxiesType': _$ProxiesTypeEnumMap[instance.proxiesType]!,
|
||||||
'proxyCardType': _$ProxyCardTypeEnumMap[instance.proxyCardType]!,
|
'proxyCardType': _$ProxyCardTypeEnumMap[instance.proxyCardType]!,
|
||||||
'proxiesColumns': instance.proxiesColumns,
|
|
||||||
'test-url': instance.testUrl,
|
'test-url': instance.testUrl,
|
||||||
'isExclude': instance.isExclude,
|
'isExclude': instance.isExclude,
|
||||||
'windowProps': instance.windowProps,
|
'windowProps': instance.windowProps,
|
||||||
@@ -94,6 +96,12 @@ const _$ProxiesSortTypeEnumMap = {
|
|||||||
ProxiesSortType.name: 'name',
|
ProxiesSortType.name: 'name',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _$ProxiesLayoutEnumMap = {
|
||||||
|
ProxiesLayout.loose: 'loose',
|
||||||
|
ProxiesLayout.standard: 'standard',
|
||||||
|
ProxiesLayout.tight: 'tight',
|
||||||
|
};
|
||||||
|
|
||||||
const _$ProxiesTypeEnumMap = {
|
const _$ProxiesTypeEnumMap = {
|
||||||
ProxiesType.tab: 'tab',
|
ProxiesType.tab: 'tab',
|
||||||
ProxiesType.list: 'list',
|
ProxiesType.list: 'list',
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ abstract class _NetworkDetectionSelectorState
|
|||||||
mixin _$ProfilesSelectorState {
|
mixin _$ProfilesSelectorState {
|
||||||
List<Profile> get profiles => throw _privateConstructorUsedError;
|
List<Profile> get profiles => throw _privateConstructorUsedError;
|
||||||
String? get currentProfileId => throw _privateConstructorUsedError;
|
String? get currentProfileId => throw _privateConstructorUsedError;
|
||||||
ViewMode get viewMode => throw _privateConstructorUsedError;
|
int get columns => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
$ProfilesSelectorStateCopyWith<ProfilesSelectorState> get copyWith =>
|
$ProfilesSelectorStateCopyWith<ProfilesSelectorState> get copyWith =>
|
||||||
@@ -471,8 +471,7 @@ abstract class $ProfilesSelectorStateCopyWith<$Res> {
|
|||||||
$Res Function(ProfilesSelectorState) then) =
|
$Res Function(ProfilesSelectorState) then) =
|
||||||
_$ProfilesSelectorStateCopyWithImpl<$Res, ProfilesSelectorState>;
|
_$ProfilesSelectorStateCopyWithImpl<$Res, ProfilesSelectorState>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call({List<Profile> profiles, String? currentProfileId, int columns});
|
||||||
{List<Profile> profiles, String? currentProfileId, ViewMode viewMode});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -491,7 +490,7 @@ class _$ProfilesSelectorStateCopyWithImpl<$Res,
|
|||||||
$Res call({
|
$Res call({
|
||||||
Object? profiles = null,
|
Object? profiles = null,
|
||||||
Object? currentProfileId = freezed,
|
Object? currentProfileId = freezed,
|
||||||
Object? viewMode = null,
|
Object? columns = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
profiles: null == profiles
|
profiles: null == profiles
|
||||||
@@ -502,10 +501,10 @@ class _$ProfilesSelectorStateCopyWithImpl<$Res,
|
|||||||
? _value.currentProfileId
|
? _value.currentProfileId
|
||||||
: currentProfileId // ignore: cast_nullable_to_non_nullable
|
: currentProfileId // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,
|
as String?,
|
||||||
viewMode: null == viewMode
|
columns: null == columns
|
||||||
? _value.viewMode
|
? _value.columns
|
||||||
: viewMode // ignore: cast_nullable_to_non_nullable
|
: columns // ignore: cast_nullable_to_non_nullable
|
||||||
as ViewMode,
|
as int,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,8 +518,7 @@ abstract class _$$ProfilesSelectorStateImplCopyWith<$Res>
|
|||||||
__$$ProfilesSelectorStateImplCopyWithImpl<$Res>;
|
__$$ProfilesSelectorStateImplCopyWithImpl<$Res>;
|
||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call({List<Profile> profiles, String? currentProfileId, int columns});
|
||||||
{List<Profile> profiles, String? currentProfileId, ViewMode viewMode});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -537,7 +535,7 @@ class __$$ProfilesSelectorStateImplCopyWithImpl<$Res>
|
|||||||
$Res call({
|
$Res call({
|
||||||
Object? profiles = null,
|
Object? profiles = null,
|
||||||
Object? currentProfileId = freezed,
|
Object? currentProfileId = freezed,
|
||||||
Object? viewMode = null,
|
Object? columns = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$ProfilesSelectorStateImpl(
|
return _then(_$ProfilesSelectorStateImpl(
|
||||||
profiles: null == profiles
|
profiles: null == profiles
|
||||||
@@ -548,10 +546,10 @@ class __$$ProfilesSelectorStateImplCopyWithImpl<$Res>
|
|||||||
? _value.currentProfileId
|
? _value.currentProfileId
|
||||||
: currentProfileId // ignore: cast_nullable_to_non_nullable
|
: currentProfileId // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,
|
as String?,
|
||||||
viewMode: null == viewMode
|
columns: null == columns
|
||||||
? _value.viewMode
|
? _value.columns
|
||||||
: viewMode // ignore: cast_nullable_to_non_nullable
|
: columns // ignore: cast_nullable_to_non_nullable
|
||||||
as ViewMode,
|
as int,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -562,7 +560,7 @@ class _$ProfilesSelectorStateImpl implements _ProfilesSelectorState {
|
|||||||
const _$ProfilesSelectorStateImpl(
|
const _$ProfilesSelectorStateImpl(
|
||||||
{required final List<Profile> profiles,
|
{required final List<Profile> profiles,
|
||||||
required this.currentProfileId,
|
required this.currentProfileId,
|
||||||
required this.viewMode})
|
required this.columns})
|
||||||
: _profiles = profiles;
|
: _profiles = profiles;
|
||||||
|
|
||||||
final List<Profile> _profiles;
|
final List<Profile> _profiles;
|
||||||
@@ -576,11 +574,11 @@ class _$ProfilesSelectorStateImpl implements _ProfilesSelectorState {
|
|||||||
@override
|
@override
|
||||||
final String? currentProfileId;
|
final String? currentProfileId;
|
||||||
@override
|
@override
|
||||||
final ViewMode viewMode;
|
final int columns;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ProfilesSelectorState(profiles: $profiles, currentProfileId: $currentProfileId, viewMode: $viewMode)';
|
return 'ProfilesSelectorState(profiles: $profiles, currentProfileId: $currentProfileId, columns: $columns)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -591,8 +589,7 @@ class _$ProfilesSelectorStateImpl implements _ProfilesSelectorState {
|
|||||||
const DeepCollectionEquality().equals(other._profiles, _profiles) &&
|
const DeepCollectionEquality().equals(other._profiles, _profiles) &&
|
||||||
(identical(other.currentProfileId, currentProfileId) ||
|
(identical(other.currentProfileId, currentProfileId) ||
|
||||||
other.currentProfileId == currentProfileId) &&
|
other.currentProfileId == currentProfileId) &&
|
||||||
(identical(other.viewMode, viewMode) ||
|
(identical(other.columns, columns) || other.columns == columns));
|
||||||
other.viewMode == viewMode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -600,7 +597,7 @@ class _$ProfilesSelectorStateImpl implements _ProfilesSelectorState {
|
|||||||
runtimeType,
|
runtimeType,
|
||||||
const DeepCollectionEquality().hash(_profiles),
|
const DeepCollectionEquality().hash(_profiles),
|
||||||
currentProfileId,
|
currentProfileId,
|
||||||
viewMode);
|
columns);
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
@@ -614,14 +611,14 @@ abstract class _ProfilesSelectorState implements ProfilesSelectorState {
|
|||||||
const factory _ProfilesSelectorState(
|
const factory _ProfilesSelectorState(
|
||||||
{required final List<Profile> profiles,
|
{required final List<Profile> profiles,
|
||||||
required final String? currentProfileId,
|
required final String? currentProfileId,
|
||||||
required final ViewMode viewMode}) = _$ProfilesSelectorStateImpl;
|
required final int columns}) = _$ProfilesSelectorStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Profile> get profiles;
|
List<Profile> get profiles;
|
||||||
@override
|
@override
|
||||||
String? get currentProfileId;
|
String? get currentProfileId;
|
||||||
@override
|
@override
|
||||||
ViewMode get viewMode;
|
int get columns;
|
||||||
@override
|
@override
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
_$$ProfilesSelectorStateImplCopyWith<_$ProfilesSelectorStateImpl>
|
_$$ProfilesSelectorStateImplCopyWith<_$ProfilesSelectorStateImpl>
|
||||||
@@ -2562,146 +2559,6 @@ abstract class _PackageListSelectorState implements PackageListSelectorState {
|
|||||||
get copyWith => throw _privateConstructorUsedError;
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$ColumnsSelectorState {
|
|
||||||
int get columns => throw _privateConstructorUsedError;
|
|
||||||
ViewMode get viewMode => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$ColumnsSelectorStateCopyWith<ColumnsSelectorState> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $ColumnsSelectorStateCopyWith<$Res> {
|
|
||||||
factory $ColumnsSelectorStateCopyWith(ColumnsSelectorState value,
|
|
||||||
$Res Function(ColumnsSelectorState) then) =
|
|
||||||
_$ColumnsSelectorStateCopyWithImpl<$Res, ColumnsSelectorState>;
|
|
||||||
@useResult
|
|
||||||
$Res call({int columns, ViewMode viewMode});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$ColumnsSelectorStateCopyWithImpl<$Res,
|
|
||||||
$Val extends ColumnsSelectorState>
|
|
||||||
implements $ColumnsSelectorStateCopyWith<$Res> {
|
|
||||||
_$ColumnsSelectorStateCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? columns = null,
|
|
||||||
Object? viewMode = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
columns: null == columns
|
|
||||||
? _value.columns
|
|
||||||
: columns // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
viewMode: null == viewMode
|
|
||||||
? _value.viewMode
|
|
||||||
: viewMode // ignore: cast_nullable_to_non_nullable
|
|
||||||
as ViewMode,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$ColumnsSelectorStateImplCopyWith<$Res>
|
|
||||||
implements $ColumnsSelectorStateCopyWith<$Res> {
|
|
||||||
factory _$$ColumnsSelectorStateImplCopyWith(_$ColumnsSelectorStateImpl value,
|
|
||||||
$Res Function(_$ColumnsSelectorStateImpl) then) =
|
|
||||||
__$$ColumnsSelectorStateImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({int columns, ViewMode viewMode});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$ColumnsSelectorStateImplCopyWithImpl<$Res>
|
|
||||||
extends _$ColumnsSelectorStateCopyWithImpl<$Res, _$ColumnsSelectorStateImpl>
|
|
||||||
implements _$$ColumnsSelectorStateImplCopyWith<$Res> {
|
|
||||||
__$$ColumnsSelectorStateImplCopyWithImpl(_$ColumnsSelectorStateImpl _value,
|
|
||||||
$Res Function(_$ColumnsSelectorStateImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? columns = null,
|
|
||||||
Object? viewMode = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$ColumnsSelectorStateImpl(
|
|
||||||
columns: null == columns
|
|
||||||
? _value.columns
|
|
||||||
: columns // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
viewMode: null == viewMode
|
|
||||||
? _value.viewMode
|
|
||||||
: viewMode // ignore: cast_nullable_to_non_nullable
|
|
||||||
as ViewMode,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$ColumnsSelectorStateImpl implements _ColumnsSelectorState {
|
|
||||||
const _$ColumnsSelectorStateImpl(
|
|
||||||
{required this.columns, required this.viewMode});
|
|
||||||
|
|
||||||
@override
|
|
||||||
final int columns;
|
|
||||||
@override
|
|
||||||
final ViewMode viewMode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ColumnsSelectorState(columns: $columns, viewMode: $viewMode)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$ColumnsSelectorStateImpl &&
|
|
||||||
(identical(other.columns, columns) || other.columns == columns) &&
|
|
||||||
(identical(other.viewMode, viewMode) ||
|
|
||||||
other.viewMode == viewMode));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, columns, viewMode);
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$ColumnsSelectorStateImplCopyWith<_$ColumnsSelectorStateImpl>
|
|
||||||
get copyWith =>
|
|
||||||
__$$ColumnsSelectorStateImplCopyWithImpl<_$ColumnsSelectorStateImpl>(
|
|
||||||
this, _$identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _ColumnsSelectorState implements ColumnsSelectorState {
|
|
||||||
const factory _ColumnsSelectorState(
|
|
||||||
{required final int columns,
|
|
||||||
required final ViewMode viewMode}) = _$ColumnsSelectorStateImpl;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get columns;
|
|
||||||
@override
|
|
||||||
ViewMode get viewMode;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$ColumnsSelectorStateImplCopyWith<_$ColumnsSelectorStateImpl>
|
|
||||||
get copyWith => throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$ProxiesListHeaderSelectorState {
|
mixin _$ProxiesListHeaderSelectorState {
|
||||||
double get offset => throw _privateConstructorUsedError;
|
double get offset => throw _privateConstructorUsedError;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class ProfilesSelectorState with _$ProfilesSelectorState {
|
|||||||
const factory ProfilesSelectorState({
|
const factory ProfilesSelectorState({
|
||||||
required List<Profile> profiles,
|
required List<Profile> profiles,
|
||||||
required String? currentProfileId,
|
required String? currentProfileId,
|
||||||
required ViewMode viewMode,
|
required int columns,
|
||||||
}) = _ProfilesSelectorState;
|
}) = _ProfilesSelectorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,14 +172,6 @@ extension PackageListSelectorStateExt on PackageListSelectorState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
|
||||||
class ColumnsSelectorState with _$ColumnsSelectorState {
|
|
||||||
const factory ColumnsSelectorState({
|
|
||||||
required int columns,
|
|
||||||
required ViewMode viewMode,
|
|
||||||
}) = _ColumnsSelectorState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {
|
class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {
|
||||||
const factory ProxiesListHeaderSelectorState({
|
const factory ProxiesListHeaderSelectorState({
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ class HomePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
selectedLabelTextStyle: context.textTheme.labelLarge!.copyWith(
|
selectedLabelTextStyle: context.textTheme.labelLarge!.copyWith(
|
||||||
color: context.colorScheme.onSurface,
|
color: context.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
),
|
||||||
unselectedLabelTextStyle: context.textTheme.labelLarge!.copyWith(
|
unselectedLabelTextStyle: context.textTheme.labelLarge!.copyWith(
|
||||||
color: context.colorScheme.onSurface,
|
color: context.colorScheme.onSurface,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:emoji_regex/emoji_regex.dart';
|
||||||
|
|
||||||
import '../state.dart';
|
import '../state.dart';
|
||||||
|
|
||||||
@@ -30,3 +31,63 @@ class TooltipText extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EmojiText extends StatelessWidget {
|
||||||
|
final String text;
|
||||||
|
final TextStyle? style;
|
||||||
|
final int? maxLines;
|
||||||
|
final TextOverflow? overflow;
|
||||||
|
|
||||||
|
const EmojiText(
|
||||||
|
this.text, {
|
||||||
|
super.key,
|
||||||
|
this.maxLines,
|
||||||
|
this.overflow,
|
||||||
|
this.style,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<TextSpan> _buildTextSpans(String emojis) {
|
||||||
|
final List<TextSpan> spans = [];
|
||||||
|
final matches = emojiRegex().allMatches(text);
|
||||||
|
|
||||||
|
int lastMatchEnd = 0;
|
||||||
|
for (final match in matches) {
|
||||||
|
if (match.start > lastMatchEnd) {
|
||||||
|
spans.add(
|
||||||
|
TextSpan(
|
||||||
|
text: text.substring(lastMatchEnd, match.start), style: style),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
spans.add(
|
||||||
|
TextSpan(
|
||||||
|
text:match.group(0),
|
||||||
|
style: style?.copyWith(
|
||||||
|
fontFamily: "Twemoji",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
lastMatchEnd = match.end;
|
||||||
|
}
|
||||||
|
if (lastMatchEnd < text.length) {
|
||||||
|
spans.add(
|
||||||
|
TextSpan(
|
||||||
|
text: text.substring(lastMatchEnd),
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spans;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return RichText(
|
||||||
|
maxLines: maxLines,
|
||||||
|
overflow: overflow ?? TextOverflow.clip,
|
||||||
|
text: TextSpan(
|
||||||
|
children: _buildTextSpans(text),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
66
pubspec.lock
66
pubspec.lock
@@ -193,14 +193,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
country_flags:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: country_flags
|
|
||||||
sha256: dbc4f76e7c801619b2d841023e0327191ba00663f1f1b4770394e9bc6632c444
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
cross_file:
|
cross_file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -249,6 +241,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.7.0"
|
||||||
|
emoji_regex:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: emoji_regex
|
||||||
|
sha256: "3a25dd4d16f98b6f76dc37cc9ae49b8511891ac4b87beac9443a1e9f4634b6c7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -541,22 +541,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.1"
|
version: "4.3.1"
|
||||||
jovial_misc:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: jovial_misc
|
|
||||||
sha256: f6e64f789ee311025bb367be9c9afe9759f76dd8209070b7f38e735b5f529eb1
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.5"
|
|
||||||
jovial_svg:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: jovial_svg
|
|
||||||
sha256: "000cafe183bdeba28f76d65bf93fc9b636d6f7eaa7e2a33e80c4345a28866ea8"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.21"
|
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -593,18 +577,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.5"
|
version: "10.0.4"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.3"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -649,10 +633,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.8.0"
|
||||||
menu_base:
|
menu_base:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -665,10 +649,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.12.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -797,14 +781,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
pointycastle:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pointycastle
|
|
||||||
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.9.1"
|
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1029,10 +1005,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.0"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1133,10 +1109,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.4"
|
version: "14.2.1"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1251,4 +1227,4 @@ packages:
|
|||||||
version: "0.2.3"
|
version: "0.2.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.4.0 <4.0.0"
|
dart: ">=3.4.0 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.22.3"
|
||||||
|
|||||||
10
pubspec.yaml
10
pubspec.yaml
@@ -1,9 +1,10 @@
|
|||||||
name: fl_clash
|
name: fl_clash
|
||||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.8.52+202408111
|
version: 0.8.53+202408151
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.1.0 <4.0.0'
|
sdk: '>=3.1.0 <4.0.0'
|
||||||
|
flutter: 3.22.3
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
@@ -37,13 +38,13 @@ dependencies:
|
|||||||
image: ^4.1.7
|
image: ^4.1.7
|
||||||
webdav_client: ^1.2.2
|
webdav_client: ^1.2.2
|
||||||
dio: ^5.4.3+1
|
dio: ^5.4.3+1
|
||||||
country_flags: ^2.2.0
|
|
||||||
win32: ^5.5.1
|
win32: ^5.5.1
|
||||||
ffi: ^2.1.2
|
ffi: ^2.1.2
|
||||||
re_editor: ^0.3.1
|
re_editor: ^0.3.1
|
||||||
re_highlight: ^0.0.3
|
re_highlight: ^0.0.3
|
||||||
archive: ^3.6.1
|
archive: ^3.6.1
|
||||||
lpinyin: ^2.0.3
|
lpinyin: ^2.0.3
|
||||||
|
emoji_regex: ^0.0.5
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
@@ -58,8 +59,13 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
- assets/data/
|
- assets/data/
|
||||||
|
- assets/fonts/
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/images/avatars/
|
- assets/images/avatars/
|
||||||
|
fonts:
|
||||||
|
- family: Twemoji
|
||||||
|
fonts:
|
||||||
|
- asset: assets/fonts/Twemoji.Mozilla.ttf
|
||||||
ffigen:
|
ffigen:
|
||||||
name: "ClashFFI"
|
name: "ClashFFI"
|
||||||
output: 'lib/clash/generated/clash_ffi.dart'
|
output: 'lib/clash/generated/clash_ffi.dart'
|
||||||
|
|||||||
Reference in New Issue
Block a user