Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba91fab2b5 | ||
|
|
18add7fba3 | ||
|
|
0d3034f216 | ||
|
|
313faa8cf3 | ||
|
|
4ba2f72b1a | ||
|
|
d6c22d4cca | ||
|
|
614dfc841a | ||
|
|
5afa552b6c | ||
|
|
ee0e2a15a7 | ||
|
|
7b7fd084bf | ||
|
|
03c39bf4bf | ||
|
|
fd7c3be02f | ||
|
|
b67d221063 | ||
|
|
1a74c0a12f | ||
|
|
9fc9c53427 | ||
|
|
8954b88d7f | ||
|
|
3990f8d7fa | ||
|
|
98688bcd2a | ||
|
|
2957311682 | ||
|
|
87a903af60 |
@@ -64,7 +64,6 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
toast!!.show()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"moveTaskToBack" -> {
|
||||
@@ -151,7 +150,6 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
val message = call.argument<String>("message")
|
||||
tip(message)
|
||||
result.success(true)
|
||||
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
@@ -2,20 +2,13 @@ import 'package:flutter/material.dart';
|
||||
import 'color.dart';
|
||||
|
||||
extension TextStyleExtension on TextStyle {
|
||||
toLight() {
|
||||
return copyWith(color: color?.toLight());
|
||||
}
|
||||
TextStyle get toLight => copyWith(color: color?.toLight());
|
||||
|
||||
toLighter() {
|
||||
return copyWith(color: color?.toLighter());
|
||||
}
|
||||
TextStyle get toLighter => copyWith(color: color?.toLighter());
|
||||
|
||||
TextStyle get toSoftBold => copyWith(fontWeight: FontWeight.w500);
|
||||
|
||||
toSoftBold() {
|
||||
return copyWith(fontWeight: FontWeight.w500);
|
||||
}
|
||||
TextStyle get toBold => copyWith(fontWeight: FontWeight.bold);
|
||||
|
||||
toBold() {
|
||||
return copyWith(fontWeight: FontWeight.bold);
|
||||
}
|
||||
}
|
||||
TextStyle get toMinus => copyWith(fontSize: fontSize! - 1);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -267,6 +266,7 @@ class AppController {
|
||||
}
|
||||
|
||||
init() async {
|
||||
updateLogStatus();
|
||||
if (!config.silentLaunch) {
|
||||
window?.show();
|
||||
}
|
||||
@@ -290,14 +290,13 @@ class AppController {
|
||||
}
|
||||
|
||||
afterInit() async {
|
||||
if (config.autoRun) {
|
||||
await proxyManager.updateStartTime();
|
||||
if (proxyManager.isStart) {
|
||||
await updateSystemProxy(true);
|
||||
} else {
|
||||
await proxyManager.updateStartTime();
|
||||
await updateSystemProxy(proxyManager.isStart);
|
||||
await updateSystemProxy(config.autoRun);
|
||||
}
|
||||
autoUpdateProfiles();
|
||||
updateLogStatus();
|
||||
autoCheckUpdate();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class CoreInfo extends StatelessWidget {
|
||||
style: context
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.toSoftBold(),
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
@@ -44,7 +44,7 @@ class CoreInfo extends StatelessWidget {
|
||||
style: context
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.toSoftBold(),
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -19,7 +19,7 @@ class _IntranetIpState extends State<IntranetIp> {
|
||||
List<NetworkInterface> interfaces = await NetworkInterface.list();
|
||||
for (final interface in interfaces) {
|
||||
for (final address in interface.addresses) {
|
||||
if (address.type == InternetAddressType.IPv4 && !address.isLoopback) {
|
||||
if (!address.isLoopback) {
|
||||
return address.address;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ class _IntranetIpState extends State<IntranetIp> {
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16).copyWith(top: 0),
|
||||
height: globalState.appController.measure.titleLargeHeight + 16,
|
||||
height: globalState.appController.measure.titleLargeHeight + 24 - 1,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: ipNotifier,
|
||||
builder: (_, value, __) {
|
||||
@@ -65,7 +65,7 @@ class _IntranetIpState extends State<IntranetIp> {
|
||||
child: TooltipText(
|
||||
text: Text(
|
||||
value,
|
||||
style: context.textTheme.titleLarge?.toSoftBold(),
|
||||
style: context.textTheme.titleLarge?.toSoftBold.toMinus,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -74,8 +74,11 @@ class _IntranetIpState extends State<IntranetIp> {
|
||||
],
|
||||
)
|
||||
: const Padding(
|
||||
padding: EdgeInsets.all(4),
|
||||
child: CircularProgressIndicator(),
|
||||
padding: EdgeInsets.all(2),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -135,7 +135,7 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
||||
),
|
||||
Container(
|
||||
height:
|
||||
globalState.appController.measure.titleLargeHeight + 24,
|
||||
globalState.appController.measure.titleLargeHeight + 24 - 1,
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.all(16).copyWith(top: 0),
|
||||
child: FadeBox(
|
||||
@@ -150,7 +150,7 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
||||
text: Text(
|
||||
ipInfo.ip,
|
||||
style: context.textTheme.titleLarge
|
||||
?.toSoftBold(),
|
||||
?.toSoftBold.toMinus,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -164,9 +164,9 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
||||
if (timeout) {
|
||||
return Text(
|
||||
"timeout",
|
||||
style: context.textTheme.titleMedium
|
||||
style: context.textTheme.titleLarge
|
||||
?.copyWith(color: Colors.red)
|
||||
.toSoftBold(),
|
||||
.toSoftBold.toMinus,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
|
||||
@@ -47,8 +47,8 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
||||
final showValue = value.showValue;
|
||||
final showUnit = "${value.showUnit}/s";
|
||||
final titleLargeSoftBold =
|
||||
Theme.of(context).textTheme.titleLarge?.toSoftBold();
|
||||
final bodyMedium = Theme.of(context).textTheme.bodySmall?.toLight();
|
||||
Theme.of(context).textTheme.titleLarge?.toSoftBold;
|
||||
final bodyMedium = Theme.of(context).textTheme.bodySmall?.toLight;
|
||||
final valueText = Text(
|
||||
showValue,
|
||||
style: titleLargeSoftBold,
|
||||
@@ -75,7 +75,7 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.titleSmall?.toSoftBold(),
|
||||
style: Theme.of(context).textTheme.titleSmall?.toSoftBold,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -67,7 +67,7 @@ class OutboundMode extends StatelessWidget {
|
||||
.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.toSoftBold(),
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -13,19 +13,17 @@ class StartButton extends StatefulWidget {
|
||||
|
||||
class _StartButtonState extends State<StartButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
bool isStart = false;
|
||||
bool isInit = false;
|
||||
late AnimationController _controller;
|
||||
bool isStart = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isStart = globalState.appController.appState.isStart;
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
value: isStart ? 1 : 0,
|
||||
value: 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -35,9 +33,12 @@ class _StartButtonState extends State<StartButton>
|
||||
}
|
||||
|
||||
handleSwitchStart() {
|
||||
isStart = !isStart;
|
||||
updateController();
|
||||
updateSystemProxy();
|
||||
final appController = globalState.appController;
|
||||
if (isStart == appController.appState.isStart) {
|
||||
isStart = !isStart;
|
||||
updateController();
|
||||
appController.updateSystemProxy(isStart);
|
||||
}
|
||||
}
|
||||
|
||||
updateController() {
|
||||
@@ -48,11 +49,18 @@ class _StartButtonState extends State<StartButton>
|
||||
}
|
||||
}
|
||||
|
||||
updateSystemProxy() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final appController = globalState.appController;
|
||||
await appController.updateSystemProxy(isStart);
|
||||
});
|
||||
Widget _updateControllerContainer(Widget child) {
|
||||
return Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.isStart,
|
||||
builder: (_, isStart, child) {
|
||||
if(isStart != this.isStart){
|
||||
this.isStart = isStart;
|
||||
updateController();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -72,8 +80,7 @@ class _StartButtonState extends State<StartButton>
|
||||
other.getTimeDifference(
|
||||
DateTime.now(),
|
||||
),
|
||||
style:
|
||||
Theme.of(context).textTheme.titleMedium?.toSoftBold(),
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold,
|
||||
),
|
||||
)
|
||||
.width +
|
||||
@@ -119,24 +126,14 @@ class _StartButtonState extends State<StartButton>
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.runTime != null,
|
||||
builder: (_, isRun, child) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isStart != isRun) {
|
||||
isStart = isRun;
|
||||
updateController();
|
||||
}
|
||||
});
|
||||
return child!;
|
||||
},
|
||||
child: Selector<AppState, int?>(
|
||||
child: _updateControllerContainer(
|
||||
Selector<AppState, int?>(
|
||||
selector: (_, appState) => appState.runTime,
|
||||
builder: (_, int? value, __) {
|
||||
final text = other.getTimeText(value);
|
||||
return Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold(),
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class TrafficUsage extends StatelessWidget {
|
||||
),
|
||||
Text(
|
||||
trafficValue.showUnit,
|
||||
style: context.textTheme.labelMedium?.toLight(),
|
||||
style: context.textTheme.labelMedium?.toLight,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export 'proxies.dart';
|
||||
export 'proxies/proxies.dart';
|
||||
export 'dashboard/dashboard.dart';
|
||||
export 'tools.dart';
|
||||
export 'profiles/profiles.dart';
|
||||
|
||||
@@ -253,7 +253,7 @@ class _ProfileItemState extends State<ProfileItem> {
|
||||
),
|
||||
Text(
|
||||
profile.lastUpdateDate?.lastUpdateTimeDesc ?? '',
|
||||
style: textTheme.labelMedium?.toLight(),
|
||||
style: textTheme.labelMedium?.toLight,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -284,7 +284,7 @@ class _ProfileItemState extends State<ProfileItem> {
|
||||
),
|
||||
Text(
|
||||
"$useShow / $totalShow",
|
||||
style: textTheme.labelMedium?.toLight(),
|
||||
style: textTheme.labelMedium?.toLight,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
@@ -293,14 +293,14 @@ class _ProfileItemState extends State<ProfileItem> {
|
||||
children: [
|
||||
Text(
|
||||
appLocalizations.expirationTime,
|
||||
style: textTheme.labelMedium?.toLighter(),
|
||||
style: textTheme.labelMedium?.toLighter,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
Text(
|
||||
expireShow,
|
||||
style: textTheme.labelMedium?.toLighter(),
|
||||
style: textTheme.labelMedium?.toLighter,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
59
lib/fragments/proxies/expansion_panel.dart
Normal file
59
lib/fragments/proxies/expansion_panel.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/widgets/card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ProxiesExpansionPanelFragment extends StatefulWidget {
|
||||
const ProxiesExpansionPanelFragment({super.key});
|
||||
|
||||
@override
|
||||
State<ProxiesExpansionPanelFragment> createState() =>
|
||||
_ProxiesExpansionPanelFragmentState();
|
||||
}
|
||||
|
||||
class _ProxiesExpansionPanelFragmentState
|
||||
extends State<ProxiesExpansionPanelFragment> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector2<AppState, Config, ProxiesSelectorState>(
|
||||
selector: (_, appState, config) {
|
||||
final currentGroups = appState.currentGroups;
|
||||
final groupNames = currentGroups.map((e) => e.name).toList();
|
||||
return ProxiesSelectorState(
|
||||
groupNames: groupNames,
|
||||
currentGroupName: config.currentGroupName,
|
||||
);
|
||||
},
|
||||
builder: (_, state, __) {
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: state.groupNames.length,
|
||||
itemBuilder: (_, index) {
|
||||
final groupName = state.groupNames[index];
|
||||
return CommonCard(
|
||||
child: ExpansionTile(
|
||||
title: Text(groupName),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0.0),
|
||||
side: const BorderSide(color: Colors.transparent),
|
||||
),
|
||||
collapsedShape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0.0),
|
||||
side: const BorderSide(color: Colors.transparent),
|
||||
),
|
||||
children: [
|
||||
Text("1212313"),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const SizedBox(
|
||||
height: 16,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
72
lib/fragments/proxies/proxies.dart
Normal file
72
lib/fragments/proxies/proxies.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/fragments/proxies/tabview.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ProxiesFragment extends StatefulWidget {
|
||||
const ProxiesFragment({super.key});
|
||||
|
||||
@override
|
||||
State<ProxiesFragment> createState() => _ProxiesFragmentState();
|
||||
}
|
||||
|
||||
class _ProxiesFragmentState extends State<ProxiesFragment> {
|
||||
|
||||
_initActions() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
final items = [
|
||||
CommonPopupMenuItem(
|
||||
action: ProxiesSortType.none,
|
||||
label: appLocalizations.defaultSort,
|
||||
iconData: Icons.sort,
|
||||
),
|
||||
CommonPopupMenuItem(
|
||||
action: ProxiesSortType.delay,
|
||||
label: appLocalizations.delaySort,
|
||||
iconData: Icons.network_ping),
|
||||
CommonPopupMenuItem(
|
||||
action: ProxiesSortType.name,
|
||||
label: appLocalizations.nameSort,
|
||||
iconData: Icons.sort_by_alpha),
|
||||
];
|
||||
commonScaffoldState?.actions = [
|
||||
Selector<Config, ProxiesSortType>(
|
||||
selector: (_, config) => config.proxiesSortType,
|
||||
builder: (_, proxiesSortType, __) {
|
||||
return CommonPopupMenu<ProxiesSortType>.radio(
|
||||
items: items,
|
||||
onSelected: (value) {
|
||||
final config = context.read<Config>();
|
||||
config.proxiesSortType = value;
|
||||
},
|
||||
selectedValue: proxiesSortType,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
)
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.currentLabel == 'proxies',
|
||||
builder: (_, isCurrent, child) {
|
||||
if (isCurrent) {
|
||||
_initActions();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: const ProxiesTabFragment(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,23 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../enum/enum.dart';
|
||||
import '../models/models.dart';
|
||||
import '../common/common.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
|
||||
class ProxiesFragment extends StatefulWidget {
|
||||
const ProxiesFragment({super.key});
|
||||
class ProxiesTabFragment extends StatefulWidget {
|
||||
const ProxiesTabFragment({super.key});
|
||||
|
||||
@override
|
||||
State<ProxiesFragment> createState() => _ProxiesFragmentState();
|
||||
State<ProxiesTabFragment> createState() => _ProxiesTabFragmentState();
|
||||
}
|
||||
|
||||
class _ProxiesFragmentState extends State<ProxiesFragment>
|
||||
with TickerProviderStateMixin {
|
||||
class _ProxiesTabFragmentState extends State<ProxiesTabFragment> with TickerProviderStateMixin {
|
||||
TabController? _tabController;
|
||||
|
||||
_initActions() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
final items = [
|
||||
CommonPopupMenuItem(
|
||||
action: ProxiesSortType.none,
|
||||
label: appLocalizations.defaultSort,
|
||||
iconData: Icons.sort,
|
||||
),
|
||||
CommonPopupMenuItem(
|
||||
action: ProxiesSortType.delay,
|
||||
label: appLocalizations.delaySort,
|
||||
iconData: Icons.network_ping),
|
||||
CommonPopupMenuItem(
|
||||
action: ProxiesSortType.name,
|
||||
label: appLocalizations.nameSort,
|
||||
iconData: Icons.sort_by_alpha),
|
||||
];
|
||||
commonScaffoldState?.actions = [
|
||||
Selector<Config, ProxiesSortType>(
|
||||
selector: (_, config) => config.proxiesSortType,
|
||||
builder: (_, proxiesSortType, __) {
|
||||
return CommonPopupMenu<ProxiesSortType>.radio(
|
||||
items: items,
|
||||
onSelected: (value) {
|
||||
final config = context.read<Config>();
|
||||
config.proxiesSortType = value;
|
||||
},
|
||||
selectedValue: proxiesSortType,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
)
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
_handleTabControllerChange() {
|
||||
final indexIsChanging = _tabController?.indexIsChanging ?? false;
|
||||
if (indexIsChanging) return;
|
||||
@@ -71,6 +29,7 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
|
||||
appController.config.updateCurrentGroupName(currentGroups[index].name);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
@@ -79,79 +38,70 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.currentLabel == 'proxies',
|
||||
builder: (_, isCurrent, child) {
|
||||
if (isCurrent) {
|
||||
_initActions();
|
||||
}
|
||||
return child!;
|
||||
return Selector2<AppState, Config, ProxiesSelectorState>(
|
||||
selector: (_, appState, config) {
|
||||
final currentGroups = appState.currentGroups;
|
||||
final groupNames = currentGroups.map((e) => e.name).toList();
|
||||
return ProxiesSelectorState(
|
||||
groupNames: groupNames,
|
||||
currentGroupName: config.currentGroupName,
|
||||
);
|
||||
},
|
||||
child: Selector2<AppState, Config, ProxiesSelectorState>(
|
||||
selector: (_, appState, config) {
|
||||
final currentGroups = appState.currentGroups;
|
||||
final groupNames = currentGroups.map((e) => e.name).toList();
|
||||
return ProxiesSelectorState(
|
||||
groupNames: groupNames,
|
||||
currentGroupName: config.currentGroupName,
|
||||
);
|
||||
},
|
||||
shouldRebuild: (prev, next) {
|
||||
if (!const ListEquality<String>()
|
||||
.equals(prev.groupNames, next.groupNames)) {
|
||||
_tabController?.removeListener(_handleTabControllerChange);
|
||||
_tabController?.dispose();
|
||||
_tabController = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
builder: (_, state, __) {
|
||||
final index = state.groupNames.indexWhere(
|
||||
(item) => item == state.currentGroupName,
|
||||
);
|
||||
_tabController ??= TabController(
|
||||
length: state.groupNames.length,
|
||||
initialIndex: index == -1 ? 0 : index,
|
||||
vsync: this,
|
||||
)..addListener(_handleTabControllerChange);
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TabBar(
|
||||
shouldRebuild: (prev, next) {
|
||||
if (!const ListEquality<String>()
|
||||
.equals(prev.groupNames, next.groupNames)) {
|
||||
_tabController?.removeListener(_handleTabControllerChange);
|
||||
_tabController?.dispose();
|
||||
_tabController = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
builder: (_, state, __) {
|
||||
final index = state.groupNames.indexWhere(
|
||||
(item) => item == state.currentGroupName,
|
||||
);
|
||||
_tabController ??= TabController(
|
||||
length: state.groupNames.length,
|
||||
initialIndex: index == -1 ? 0 : index,
|
||||
vsync: this,
|
||||
)..addListener(_handleTabControllerChange);
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
dividerColor: Colors.transparent,
|
||||
isScrollable: true,
|
||||
tabAlignment: TabAlignment.start,
|
||||
overlayColor:
|
||||
const WidgetStatePropertyAll(Colors.transparent),
|
||||
tabs: [
|
||||
for (final groupName in state.groupNames)
|
||||
Tab(
|
||||
text: groupName,
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
dividerColor: Colors.transparent,
|
||||
isScrollable: true,
|
||||
tabAlignment: TabAlignment.start,
|
||||
overlayColor:
|
||||
const WidgetStatePropertyAll(Colors.transparent),
|
||||
tabs: [
|
||||
children: [
|
||||
for (final groupName in state.groupNames)
|
||||
Tab(
|
||||
text: groupName,
|
||||
KeepContainer(
|
||||
key: ObjectKey(groupName),
|
||||
child: ProxiesTabView(
|
||||
groupName: groupName,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
for (final groupName in state.groupNames)
|
||||
KeepContainer(
|
||||
key: ObjectKey(groupName),
|
||||
child: ProxiesTabView(
|
||||
groupName: groupName,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -167,7 +117,7 @@ class ProxiesTabView extends StatelessWidget {
|
||||
List<Proxy> _sortOfName(List<Proxy> proxies) {
|
||||
return List.of(proxies)
|
||||
..sort(
|
||||
(a, b) => other.sortByChar(a.name, b.name),
|
||||
(a, b) => other.sortByChar(a.name, b.name),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,7 +125,7 @@ class ProxiesTabView extends StatelessWidget {
|
||||
final appState = context.read<AppState>();
|
||||
return proxies = List.of(proxies)
|
||||
..sort(
|
||||
(a, b) {
|
||||
(a, b) {
|
||||
final aDelay = appState.getDelay(a.name);
|
||||
final bDelay = appState.getDelay(b.name);
|
||||
if (aDelay == null && bDelay == null) {
|
||||
@@ -193,10 +143,10 @@ class ProxiesTabView extends StatelessWidget {
|
||||
}
|
||||
|
||||
_getProxies(
|
||||
BuildContext context,
|
||||
List<Proxy> proxies,
|
||||
ProxiesSortType proxiesSortType,
|
||||
) {
|
||||
BuildContext context,
|
||||
List<Proxy> proxies,
|
||||
ProxiesSortType proxiesSortType,
|
||||
) {
|
||||
if (proxiesSortType == ProxiesSortType.delay) {
|
||||
return _sortOfDelay(context, proxies);
|
||||
}
|
||||
@@ -379,7 +329,7 @@ class ProxyCard extends StatelessWidget {
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color:
|
||||
context.textTheme.bodySmall?.color?.toLight(),
|
||||
context.textTheme.bodySmall?.color?.toLight(),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -187,7 +187,7 @@
|
||||
"connections": "Connections",
|
||||
"connectionsDesc": "View current connection",
|
||||
"nullRequestsDesc": "No requests",
|
||||
"nullConnectionsDesc": "No Connections",
|
||||
"nullConnectionsDesc": "No connections",
|
||||
"intranetIp": "Intranet IP",
|
||||
"view": "View",
|
||||
"cut": "Cut",
|
||||
|
||||
@@ -186,7 +186,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
|
||||
"The current proxy group cannot be selected."),
|
||||
"nullConnectionsDesc":
|
||||
MessageLookupByLibrary.simpleMessage("No Connections"),
|
||||
MessageLookupByLibrary.simpleMessage("No connections"),
|
||||
"nullCoreInfoDesc":
|
||||
MessageLookupByLibrary.simpleMessage("Unable to obtain core info"),
|
||||
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("No logs"),
|
||||
|
||||
@@ -1930,10 +1930,10 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `No Connections`
|
||||
/// `No connections`
|
||||
String get nullConnectionsDesc {
|
||||
return Intl.message(
|
||||
'No Connections',
|
||||
'No connections',
|
||||
name: 'nullConnectionsDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
|
||||
@@ -11,7 +11,7 @@ class NullStatus extends StatelessWidget {
|
||||
return Center(
|
||||
child: Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toBold(),
|
||||
style: Theme.of(context).textTheme.titleMedium?.toBold,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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.26
|
||||
version: 0.8.27
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user