Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
891977408e | ||
|
|
5292f34e8d | ||
|
|
1c54db6bf3 | ||
|
|
a4b5f4abdb | ||
|
|
aa4ffbe4fb |
@@ -44,7 +44,7 @@ object GlobalState {
|
||||
fun initServiceEngine(context: Context) {
|
||||
if (serviceEngine != null) return
|
||||
lock.withLock {
|
||||
if (serviceEngine != null) return
|
||||
destroyServiceEngine()
|
||||
serviceEngine = FlutterEngine(context)
|
||||
serviceEngine?.plugins?.add(ProxyPlugin())
|
||||
serviceEngine?.plugins?.add(AppPlugin())
|
||||
|
||||
@@ -71,7 +71,7 @@ class FlClashTileService : TileService() {
|
||||
if (titlePlugin != null) {
|
||||
titlePlugin.handleStart()
|
||||
} else {
|
||||
GlobalState.initServiceEngine(this)
|
||||
GlobalState.initServiceEngine(applicationContext)
|
||||
}
|
||||
} else if (GlobalState.runState.value == RunState.START) {
|
||||
GlobalState.runState.value = RunState.PENDING
|
||||
|
||||
@@ -11,12 +11,17 @@ import android.net.VpnService
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.Parcel
|
||||
import android.os.RemoteException
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.MainActivity
|
||||
import com.follow.clash.R
|
||||
import com.follow.clash.models.AccessControlMode
|
||||
import com.follow.clash.models.Props
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class FlClashVpnService : VpnService() {
|
||||
@@ -131,7 +136,7 @@ class FlClashVpnService : VpnService() {
|
||||
}
|
||||
|
||||
fun initServiceEngine() {
|
||||
GlobalState.initServiceEngine(this)
|
||||
GlobalState.initServiceEngine(applicationContext)
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
@@ -168,8 +173,23 @@ class FlClashVpnService : VpnService() {
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
fun getService(): FlClashVpnService = this@FlClashVpnService
|
||||
|
||||
// override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
||||
// try {
|
||||
// val isSuccess = super.onTransact(code, data, reply, flags)
|
||||
// if (!isSuccess) {
|
||||
// CoroutineScope(Dispatchers.Main).launch {
|
||||
// GlobalState.getCurrentTitlePlugin()?.handleStop()
|
||||
// }
|
||||
// }
|
||||
// return isSuccess
|
||||
// } catch (e: RemoteException) {
|
||||
// throw e
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
override fun onBind(intent: Intent): IBinder {
|
||||
return binder
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ class _AccessFragmentState extends State<AccessFragment> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
@@ -112,55 +111,48 @@ class _AccessFragmentState extends State<AccessFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
_buildSelectedAllButton({
|
||||
Widget _buildSelectedAllButton({
|
||||
required bool isAccessControl,
|
||||
required bool isSelectedAll,
|
||||
required List<String> allValueList,
|
||||
}) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final tooltip = isSelectedAll
|
||||
? appLocalizations.cancelSelectAll
|
||||
: appLocalizations.selectAll;
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
commonScaffoldState?.floatingActionButton = DisabledMask(
|
||||
status: !isAccessControl,
|
||||
child: AbsorbPointer(
|
||||
absorbing: !isAccessControl,
|
||||
child: FloatingActionButton (
|
||||
tooltip: tooltip,
|
||||
onPressed: () {
|
||||
final config = globalState.appController.config;
|
||||
final isAccept =
|
||||
config.accessControl.mode == AccessControlMode.acceptSelected;
|
||||
final tooltip = isSelectedAll
|
||||
? appLocalizations.cancelSelectAll
|
||||
: appLocalizations.selectAll;
|
||||
return AbsorbPointer(
|
||||
absorbing: !isAccessControl,
|
||||
child: FloatingActionButton(
|
||||
tooltip: tooltip,
|
||||
onPressed: () {
|
||||
final config = globalState.appController.config;
|
||||
final isAccept =
|
||||
config.accessControl.mode == AccessControlMode.acceptSelected;
|
||||
|
||||
if (isSelectedAll) {
|
||||
config.accessControl = switch (isAccept) {
|
||||
true => config.accessControl.copyWith(
|
||||
acceptList: [],
|
||||
),
|
||||
false => config.accessControl.copyWith(
|
||||
rejectList: [],
|
||||
),
|
||||
};
|
||||
} else {
|
||||
config.accessControl = switch (isAccept) {
|
||||
true => config.accessControl.copyWith(
|
||||
acceptList: allValueList,
|
||||
),
|
||||
false => config.accessControl.copyWith(
|
||||
rejectList: allValueList,
|
||||
),
|
||||
};
|
||||
}
|
||||
},
|
||||
child: isSelectedAll
|
||||
? const Icon(Icons.deselect)
|
||||
: const Icon(Icons.select_all),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
if (isSelectedAll) {
|
||||
config.accessControl = switch (isAccept) {
|
||||
true => config.accessControl.copyWith(
|
||||
acceptList: [],
|
||||
),
|
||||
false => config.accessControl.copyWith(
|
||||
rejectList: [],
|
||||
),
|
||||
};
|
||||
} else {
|
||||
config.accessControl = switch (isAccept) {
|
||||
true => config.accessControl.copyWith(
|
||||
acceptList: allValueList,
|
||||
),
|
||||
false => config.accessControl.copyWith(
|
||||
rejectList: allValueList,
|
||||
),
|
||||
};
|
||||
}
|
||||
},
|
||||
child: isSelectedAll
|
||||
? const Icon(Icons.deselect)
|
||||
: const Icon(Icons.select_all),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPackageList() {
|
||||
@@ -213,137 +205,141 @@ class _AccessFragmentState extends State<AccessFragment> {
|
||||
accessControlMode == AccessControlMode.acceptSelected
|
||||
? appLocalizations.accessControlAllowDesc
|
||||
: appLocalizations.accessControlNotAllowDesc;
|
||||
_buildSelectedAllButton(
|
||||
isAccessControl: isAccessControl,
|
||||
isSelectedAll: valueList.length == packageNameList.length,
|
||||
allValueList: packageNameList,
|
||||
);
|
||||
return DisabledMask(
|
||||
status: !isAccessControl,
|
||||
child: Column(
|
||||
children: [
|
||||
AbsorbPointer(
|
||||
absorbing: !isAccessControl,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 16,
|
||||
right: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
appLocalizations.selected,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
),
|
||||
child: FloatLayout(
|
||||
floatingWidget: FloatWrapper(
|
||||
child: _buildSelectedAllButton(
|
||||
isAccessControl: isAccessControl,
|
||||
isSelectedAll: valueList.length == packageNameList.length,
|
||||
allValueList: packageNameList,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
AbsorbPointer(
|
||||
absorbing: !isAccessControl,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 16,
|
||||
right: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
appLocalizations.selected,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Flexible(
|
||||
child: SizedBox(
|
||||
width: 8,
|
||||
const Flexible(
|
||||
child: SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
"${valueList.length}",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
"${valueList.length}",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(describe),
|
||||
)
|
||||
],
|
||||
Flexible(
|
||||
child: Text(describe),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _buildSearchButton(currentPackages)),
|
||||
Flexible(child: _buildFilterSystemAppButton()),
|
||||
Flexible(child: _buildAppProxyModePopup()),
|
||||
],
|
||||
),
|
||||
],
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _buildSearchButton(currentPackages)),
|
||||
Flexible(child: _buildFilterSystemAppButton()),
|
||||
Flexible(child: _buildAppProxyModePopup()),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: FadeBox(
|
||||
key: const Key("fade_box"),
|
||||
child: currentPackages.isEmpty
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: currentPackages.length,
|
||||
itemBuilder: (_, index) {
|
||||
final package = currentPackages[index];
|
||||
return PackageListItem(
|
||||
key: Key(package.packageName),
|
||||
package: package,
|
||||
value:
|
||||
valueList.contains(package.packageName),
|
||||
isActive: isAccessControl,
|
||||
onChanged: (value) {
|
||||
if (value == true) {
|
||||
valueList.add(package.packageName);
|
||||
} else {
|
||||
valueList.remove(package.packageName);
|
||||
}
|
||||
final config =
|
||||
globalState.appController.config;
|
||||
if (accessControlMode ==
|
||||
AccessControlMode.acceptSelected) {
|
||||
config.accessControl =
|
||||
config.accessControl.copyWith(
|
||||
acceptList: valueList,
|
||||
);
|
||||
} else {
|
||||
config.accessControl =
|
||||
config.accessControl.copyWith(
|
||||
rejectList: valueList,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: FadeBox(
|
||||
key: const Key("fade_box"),
|
||||
child: currentPackages.isEmpty
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: currentPackages.length,
|
||||
itemBuilder: (_, index) {
|
||||
final package = currentPackages[index];
|
||||
return PackageListItem(
|
||||
key: Key(package.packageName),
|
||||
package: package,
|
||||
value:
|
||||
valueList.contains(package.packageName),
|
||||
isActive: isAccessControl,
|
||||
onChanged: (value) {
|
||||
if (value == true) {
|
||||
valueList.add(package.packageName);
|
||||
} else {
|
||||
valueList.remove(package.packageName);
|
||||
}
|
||||
final config =
|
||||
globalState.appController.config;
|
||||
if (accessControlMode ==
|
||||
AccessControlMode.acceptSelected) {
|
||||
config.accessControl =
|
||||
config.accessControl.copyWith(
|
||||
acceptList: valueList,
|
||||
);
|
||||
} else {
|
||||
config.accessControl =
|
||||
config.accessControl.copyWith(
|
||||
rejectList: valueList,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -76,13 +76,6 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
||||
width: 8,
|
||||
)
|
||||
];
|
||||
commonScaffoldState?.floatingActionButton = FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _handleShowAddExtendPage,
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -113,74 +106,86 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.currentLabel == 'profiles',
|
||||
builder: (_, isCurrent, child) {
|
||||
if (isCurrent) {
|
||||
_initScaffoldState();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: Selector2<AppState, Config, ProfilesSelectorState>(
|
||||
selector: (_, appState, config) => ProfilesSelectorState(
|
||||
profiles: config.profiles,
|
||||
currentProfileId: config.currentProfileId,
|
||||
viewMode: appState.viewMode,
|
||||
return FloatLayout(
|
||||
floatingWidget: FloatWrapper(
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _handleShowAddExtendPage,
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
),
|
||||
),
|
||||
builder: (context, state, child) {
|
||||
if (state.profiles.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullProfileDesc,
|
||||
);
|
||||
),
|
||||
child: Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.currentLabel == 'profiles',
|
||||
builder: (_, isCurrent, child) {
|
||||
if (isCurrent) {
|
||||
_initScaffoldState();
|
||||
}
|
||||
profileItemKeys = state.profiles
|
||||
.map((profile) => GlobalObjectKey<_ProfileItemState>(profile.id))
|
||||
.toList();
|
||||
final columns = _getColumns(state.viewMode);
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: (scrollNotification) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
hasPadding.value =
|
||||
scrollNotification.metrics.maxScrollExtent > 0;
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: hasPadding,
|
||||
builder: (_, hasPadding, __) {
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: 16 + (hasPadding ? 56 : 0),
|
||||
),
|
||||
child: Grid(
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16,
|
||||
crossAxisCount: columns,
|
||||
children: [
|
||||
for (int i = 0; i < state.profiles.length; i++)
|
||||
GridItem(
|
||||
child: ProfileItem(
|
||||
key: profileItemKeys[i],
|
||||
profile: state.profiles[i],
|
||||
groupValue: state.currentProfileId,
|
||||
onChanged: _changeProfile,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
return child!;
|
||||
},
|
||||
child: Selector2<AppState, Config, ProfilesSelectorState>(
|
||||
selector: (_, appState, config) => ProfilesSelectorState(
|
||||
profiles: config.profiles,
|
||||
currentProfileId: config.currentProfileId,
|
||||
viewMode: appState.viewMode,
|
||||
),
|
||||
builder: (context, state, child) {
|
||||
if (state.profiles.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullProfileDesc,
|
||||
);
|
||||
}
|
||||
profileItemKeys = state.profiles
|
||||
.map(
|
||||
(profile) => GlobalObjectKey<_ProfileItemState>(profile.id))
|
||||
.toList();
|
||||
final columns = _getColumns(state.viewMode);
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: (scrollNotification) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
hasPadding.value =
|
||||
scrollNotification.metrics.maxScrollExtent > 0;
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: hasPadding,
|
||||
builder: (_, hasPadding, __) {
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: 16 + (hasPadding ? 56 : 0),
|
||||
),
|
||||
child: Grid(
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16,
|
||||
crossAxisCount: columns,
|
||||
children: [
|
||||
for (int i = 0; i < state.profiles.length; i++)
|
||||
GridItem(
|
||||
child: ProfileItem(
|
||||
key: profileItemKeys[i],
|
||||
profile: state.profiles[i],
|
||||
groupValue: state.currentProfileId,
|
||||
onChanged: _changeProfile,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
@@ -67,7 +66,6 @@ Future<void> vpnService() async {
|
||||
clashCore.setFdMap(fd.id);
|
||||
},
|
||||
onProcess: (Process process) async {
|
||||
print(process);
|
||||
var packageName = await app?.resolverProcess(process);
|
||||
clashCore.setProcessMap(
|
||||
ProcessMapItem(
|
||||
@@ -112,14 +110,13 @@ Future<void> vpnService() async {
|
||||
onStop: () async {
|
||||
await app?.tip(appLocalizations.stopVpn);
|
||||
await globalState.stopSystemProxy();
|
||||
exit(0);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
globalState.updateTraffic();
|
||||
globalState.updateFunctionLists = [
|
||||
() {
|
||||
() {
|
||||
globalState.updateTraffic();
|
||||
}
|
||||
];
|
||||
@@ -137,7 +134,8 @@ class ServiceMessageHandler with ServiceMessageListener {
|
||||
required Function(Process process) onProcess,
|
||||
required Function(String runTime) onStarted,
|
||||
required Function(String groupName) onLoaded,
|
||||
}) : _onProtect = onProtect,
|
||||
})
|
||||
: _onProtect = onProtect,
|
||||
_onProcess = onProcess,
|
||||
_onStarted = onStarted,
|
||||
_onLoaded = onLoaded;
|
||||
@@ -163,10 +161,11 @@ class ServiceMessageHandler with ServiceMessageListener {
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class TileListenerWithVpn with TileListener {
|
||||
final Function() _onStop;
|
||||
|
||||
TileListenerWithVpn({
|
||||
const TileListenerWithVpn({
|
||||
required Function() onStop,
|
||||
}) : _onStop = onStop;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ 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:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:proxy/proxy_platform_interface.dart';
|
||||
|
||||
@@ -80,6 +81,7 @@ class Proxy extends ProxyPlatform {
|
||||
bool get isStart => startTime != null && startTime!.isBeforeNow;
|
||||
|
||||
onStarted(int? fd) {
|
||||
debugPrint("onStarted ==> $fd");
|
||||
if (fd == null) return;
|
||||
if (receiver != null) {
|
||||
receiver!.close();
|
||||
|
||||
@@ -49,8 +49,6 @@ class CommonScaffold extends StatefulWidget {
|
||||
|
||||
class CommonScaffoldState extends State<CommonScaffold> {
|
||||
final ValueNotifier<List<Widget>> _actions = ValueNotifier([]);
|
||||
final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);
|
||||
|
||||
final ValueNotifier<bool> _loading = ValueNotifier(false);
|
||||
|
||||
set actions(List<Widget> actions) {
|
||||
@@ -59,12 +57,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
}
|
||||
}
|
||||
|
||||
set floatingActionButton(Widget? actions) {
|
||||
if (_floatingActionButton.value != actions) {
|
||||
_floatingActionButton.value = actions;
|
||||
}
|
||||
}
|
||||
|
||||
Future<T?> loadingRun<T>(
|
||||
Future<T> Function() futureFunction, {
|
||||
String? title,
|
||||
@@ -89,7 +81,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
@override
|
||||
void dispose() {
|
||||
_actions.dispose();
|
||||
_floatingActionButton.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -98,7 +89,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.title != widget.title) {
|
||||
_actions.value = [];
|
||||
_floatingActionButton.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.37+202407141
|
||||
version: 0.8.40+202407152
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user