Merge branch 'be1c1bb580ec039044e8057ae1469153008cdb4d'
This commit is contained in:
17
.github/release_message.md
vendored
17
.github/release_message.md
vendored
@@ -1,8 +1,6 @@
|
||||
[](https://img.shields.io/github/downloads/hiddify/hiddify-next/RELEASE_TAG/)
|
||||
|
||||
<a href="https://apps.microsoft.com/detail/Hiddify/9pdfnl3qv2s5?mode=mini" target="_blank">
|
||||
<img src="https://github.com/hiddify/hiddify-next/assets/114227601/88210921-4d33-46e5-ac6a-89b4ffe13ae8" width="200">
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
**Release Highlights:**
|
||||
@@ -27,6 +25,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div align=left>
|
||||
<table>
|
||||
<thead align=left>
|
||||
@@ -45,14 +45,16 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows</td>
|
||||
<td><a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-Windows-Setup-x64.exe"><img src="https://img.shields.io/badge/Setup-x64-0078d7.svg?logo=github"></a><br>
|
||||
<a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-Windows-Portable-x64.zip"><img src="https://img.shields.io/badge/Portable-x64-2d7d9a.svg?logo=github"></a>
|
||||
<td>Windows</td><td>
|
||||
<a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-Windows-Setup-x64.Msix"><img src="https://img.shields.io/badge/OfficialSetup-x64-0078d7.svg?logo=github"></a><br>
|
||||
<a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-Windows-Setup-x64.exe"><img src="https://img.shields.io/badge/Setup-x64-2d7d9a.svg?logo=github"></a><br>
|
||||
<a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-Windows-Portable-x64.zip"><img src="https://img.shields.io/badge/Portable-x64-67b7d1.svg?logo=github"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>macOS (v10.15+)</td>
|
||||
<td><a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-MacOS.dmg"><img src="https://img.shields.io/badge/DMG-Universal-ea005e.svg?logo=github"></a></td>
|
||||
<td><a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-MacOS.dmg"><img src="https://img.shields.io/badge/DMG-Universal-ea005e.svg?logo=github"></a><br>
|
||||
<a href="https://github.com/hiddify/hiddify-next/releases/download/RELEASE_TAG/Hiddify-MacOS-Installer.pkg"><img src="https://img.shields.io/badge/PKG-Universal-bc544b.svg?logo=github" /></a></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Linux</td>
|
||||
@@ -63,6 +65,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div dir="ltr">
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -15,7 +15,7 @@ on:
|
||||
env:
|
||||
IS_GITHUB_ACTIONS: 1
|
||||
CHANNEL: "${{ inputs.channel }}"
|
||||
FLUTTER_VERSION: '3.16.x'
|
||||
FLUTTER_VERSION: '3.19.x'
|
||||
NDK_VERSION: r26b
|
||||
UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}"
|
||||
TAG_NAME: "${{ inputs.tag-name }}"
|
||||
|
||||
1
Makefile
1
Makefile
@@ -182,7 +182,6 @@ build-linux-libs:
|
||||
|
||||
build-macos-libs:
|
||||
make -C libcore -f Makefile macos-universal
|
||||
mv $(BINDIR)/$(SRV_NAME) $(DESKTOP_OUT)/
|
||||
|
||||
build-ios-libs:
|
||||
rf -rf $(IOS_OUT)/Libcore.xcframework
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
boolean hasKeyStore = false
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
@@ -39,10 +40,6 @@ if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
namespace "com.hiddify.hiddify"
|
||||
compileSdkVersion 34
|
||||
@@ -127,7 +124,6 @@ flutter {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.8.21'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
include ':app'
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.4.2" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.21" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"connecting": "Connecting",
|
||||
"disconnecting": "Disconnecting",
|
||||
"connected": "Connected",
|
||||
"reconnect": "Reconnect",
|
||||
"experimentalNotice": "Experimental Features In Use",
|
||||
"experimentalNoticeMsg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from Config options page.",
|
||||
"disableExperimentalNotice": "Don't show again"
|
||||
@@ -228,12 +229,21 @@
|
||||
"reconnectMsg": "Reconnect for changes to take effect",
|
||||
"reconnectBtn": "Reconnect",
|
||||
"serviceMode": "Service Mode",
|
||||
"quickSettings": "Quick Settings",
|
||||
"setupWarp": "Setup WARP",
|
||||
"allOptions": "All Config Options",
|
||||
"serviceModes": {
|
||||
"proxy": "Proxy Service Only",
|
||||
"systemProxy": "Set System Proxy",
|
||||
"tun": "VPN",
|
||||
"tunService": "VPN Service"
|
||||
},
|
||||
"shortServiceModes": {
|
||||
"proxy": "Proxy",
|
||||
"systemProxy": "System Proxy",
|
||||
"tun": "VPN",
|
||||
"tunService": "VPN Service"
|
||||
},
|
||||
"section": {
|
||||
"route": "Route Options",
|
||||
"dns": "DNS Options",
|
||||
|
||||
@@ -93,7 +93,6 @@ Future<void> lazyBootstrap(
|
||||
final debug = container.read(debugModeNotifierProvider) || kDebugMode;
|
||||
|
||||
if (PlatformUtils.isDesktop) {
|
||||
Logger.bootstrap.info("Starting Window Contoller");
|
||||
await _init(
|
||||
"window controller",
|
||||
() => container.read(windowNotifierProvider.future),
|
||||
@@ -107,61 +106,50 @@ Future<void> lazyBootstrap(
|
||||
} else {
|
||||
Logger.bootstrap.debug("silent start, remain hidden accessible via tray");
|
||||
}
|
||||
Logger.bootstrap.info("Starting Auto Start Service");
|
||||
await _init(
|
||||
"auto start service",
|
||||
() => container.read(autoStartNotifierProvider.future),
|
||||
);
|
||||
}
|
||||
Logger.bootstrap.info("Starting Log Repository");
|
||||
await _init(
|
||||
"logs repository",
|
||||
() => container.read(logRepositoryProvider.future),
|
||||
);
|
||||
Logger.bootstrap.info("Starting Logger Contoller");
|
||||
await _init("logger controller", () => LoggerController.postInit(debug));
|
||||
|
||||
Logger.bootstrap.info(appInfo.format());
|
||||
Logger.bootstrap.info("Starting GeoAssets");
|
||||
await _init(
|
||||
"geo assets repository",
|
||||
() => container.read(geoAssetRepositoryProvider.future),
|
||||
);
|
||||
Logger.bootstrap.info("Starting Profile Repository");
|
||||
await _init(
|
||||
"profile repository",
|
||||
() => container.read(profileRepositoryProvider.future),
|
||||
);
|
||||
|
||||
Logger.bootstrap.info("Starting Active Profile");
|
||||
await _safeInit(
|
||||
"active profile",
|
||||
() => container.read(activeProfileProvider.future),
|
||||
timeout: 1000,
|
||||
);
|
||||
Logger.bootstrap.info("Starting Deep Link Service");
|
||||
await _safeInit(
|
||||
"deep link service",
|
||||
() => container.read(deepLinkNotifierProvider.future),
|
||||
timeout: 1000,
|
||||
);
|
||||
Logger.bootstrap.info("Starting Singbox Service Provider");
|
||||
await _init(
|
||||
"sing-box",
|
||||
() => container.read(singboxServiceProvider).init(),
|
||||
);
|
||||
if (PlatformUtils.isDesktop) {
|
||||
Logger.bootstrap.info("Starting System Tray");
|
||||
await _safeInit(
|
||||
"system tray",
|
||||
() => container.read(systemTrayNotifierProvider.future),
|
||||
timeout: 1000,
|
||||
);
|
||||
Logger.bootstrap.info("System Tray initialized");
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
Logger.bootstrap.info("Starting FlutterDisplayMode.setHighRefreshRate");
|
||||
await _safeInit(
|
||||
"android display mode",
|
||||
() async {
|
||||
@@ -191,13 +179,14 @@ Future<T> _init<T>(
|
||||
int? timeout,
|
||||
}) async {
|
||||
final stopWatch = Stopwatch()..start();
|
||||
Logger.bootstrap.info("initializing [$name]");
|
||||
Future<T> func() => timeout != null
|
||||
? initializer().timeout(Duration(milliseconds: timeout))
|
||||
: initializer();
|
||||
try {
|
||||
final result = await func();
|
||||
Logger.bootstrap.debug(
|
||||
"[$name] initialized in ${stopWatch.elapsedMilliseconds}ms $result");
|
||||
Logger.bootstrap
|
||||
.debug("[$name] initialized in ${stopWatch.elapsedMilliseconds}ms");
|
||||
return result;
|
||||
} catch (e, stackTrace) {
|
||||
Logger.bootstrap.error("[$name] error initializing", e, stackTrace);
|
||||
|
||||
@@ -9,6 +9,6 @@ LazyDatabase openConnection() {
|
||||
return LazyDatabase(() async {
|
||||
final dbDir = await AppDirectories.getDatabaseDirectory();
|
||||
final file = File(p.join(dbDir.path, 'db.sqlite'));
|
||||
return NativeDatabase.createInBackground(file);
|
||||
return NativeDatabase(file);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hiddify/core/router/app_router.dart';
|
||||
import 'package:hiddify/features/common/adaptive_root_scaffold.dart';
|
||||
import 'package:hiddify/features/config_option/overview/config_options_page.dart';
|
||||
import 'package:hiddify/features/config_option/widget/quick_settings_modal.dart';
|
||||
import 'package:hiddify/features/geo_asset/overview/geo_assets_overview_page.dart';
|
||||
import 'package:hiddify/features/home/widget/home_page.dart';
|
||||
import 'package:hiddify/features/intro/widget/intro_page.dart';
|
||||
@@ -47,6 +48,10 @@ GlobalKey<NavigatorState>? _dynamicRootKey =
|
||||
path: "config-options",
|
||||
name: ConfigOptionsRoute.name,
|
||||
),
|
||||
TypedGoRoute<QuickSettingsRoute>(
|
||||
path: "quick-settings",
|
||||
name: QuickSettingsRoute.name,
|
||||
),
|
||||
TypedGoRoute<SettingsRoute>(
|
||||
path: "settings",
|
||||
name: SettingsRoute.name,
|
||||
@@ -108,6 +113,10 @@ class MobileWrapperRoute extends ShellRouteData {
|
||||
path: "profiles/:id",
|
||||
name: ProfileDetailsRoute.name,
|
||||
),
|
||||
TypedGoRoute<QuickSettingsRoute>(
|
||||
path: "quick-settings",
|
||||
name: QuickSettingsRoute.name,
|
||||
),
|
||||
],
|
||||
),
|
||||
TypedGoRoute<ProxiesRoute>(
|
||||
@@ -277,6 +286,22 @@ class LogsOverviewRoute extends GoRouteData {
|
||||
}
|
||||
}
|
||||
|
||||
class QuickSettingsRoute extends GoRouteData {
|
||||
const QuickSettingsRoute();
|
||||
static const name = "Quick Settings";
|
||||
|
||||
static final GlobalKey<NavigatorState> $parentNavigatorKey = rootNavigatorKey;
|
||||
|
||||
@override
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
return BottomSheetPage(
|
||||
fixed: true,
|
||||
name: name,
|
||||
builder: (controller) => const QuickSettingsModal(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsRoute extends GoRouteData {
|
||||
const SettingsRoute();
|
||||
static const name = "Settings";
|
||||
@@ -296,7 +321,8 @@ class SettingsRoute extends GoRouteData {
|
||||
}
|
||||
|
||||
class ConfigOptionsRoute extends GoRouteData {
|
||||
const ConfigOptionsRoute();
|
||||
const ConfigOptionsRoute({this.section});
|
||||
final String? section;
|
||||
static const name = "Config Options";
|
||||
|
||||
static final GlobalKey<NavigatorState>? $parentNavigatorKey = _dynamicRootKey;
|
||||
@@ -304,12 +330,15 @@ class ConfigOptionsRoute extends GoRouteData {
|
||||
@override
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
if (useMobileRouter) {
|
||||
return const MaterialPage(
|
||||
return MaterialPage(
|
||||
name: name,
|
||||
child: ConfigOptionsPage(),
|
||||
child: ConfigOptionsPage(section: section),
|
||||
);
|
||||
}
|
||||
return const NoTransitionPage(name: name, child: ConfigOptionsPage());
|
||||
return NoTransitionPage(
|
||||
name: name,
|
||||
child: ConfigOptionsPage(section: section),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class ConfigOptionNotifier extends _$ConfigOptionNotifier with AppLogger {
|
||||
if (next case AsyncData(:final value) when next != previous) {
|
||||
if (_lastUpdate == null ||
|
||||
DateTime.now().difference(_lastUpdate!) >
|
||||
const Duration(seconds: 3)) {
|
||||
const Duration(milliseconds: 100)) {
|
||||
_lastUpdate = DateTime.now();
|
||||
state = AsyncData(value != serviceSingboxOptions);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/model/optional_range.dart';
|
||||
@@ -21,12 +22,49 @@ import 'package:hiddify/utils/utils.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:humanizer/humanizer.dart';
|
||||
|
||||
enum ConfigOptionSection {
|
||||
warp;
|
||||
|
||||
static final _warpKey = GlobalKey(debugLabel: "warp-section-key");
|
||||
|
||||
GlobalKey get key => switch (this) { _ => _warpKey };
|
||||
}
|
||||
|
||||
class ConfigOptionsPage extends HookConsumerWidget {
|
||||
const ConfigOptionsPage({super.key});
|
||||
ConfigOptionsPage({super.key, String? section})
|
||||
: section =
|
||||
section != null ? ConfigOptionSection.values.byName(section) : null;
|
||||
|
||||
final ConfigOptionSection? section;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final t = ref.watch(translationsProvider);
|
||||
final scrollController = useScrollController();
|
||||
|
||||
useMemoized(
|
||||
() {
|
||||
if (section != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
final box =
|
||||
section!.key.currentContext?.findRenderObject() as RenderBox?;
|
||||
final offset = box?.localToGlobal(Offset.zero);
|
||||
if (offset == null) return;
|
||||
final height = scrollController.offset +
|
||||
offset.dy -
|
||||
MediaQueryData.fromView(View.of(context)).padding.top -
|
||||
kToolbarHeight;
|
||||
scrollController.animateTo(
|
||||
height,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.decelerate,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
String experimental(String txt) {
|
||||
return "$txt (${t.settings.experimental})";
|
||||
@@ -34,6 +72,8 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
||||
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
controller: scrollController,
|
||||
shrinkWrap: true,
|
||||
slivers: [
|
||||
NestedAppBar(
|
||||
title: Text(t.settings.config.pageTitle),
|
||||
@@ -101,235 +141,255 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
SliverList.list(
|
||||
children: [
|
||||
TipCard(message: t.settings.experimentalMsg),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.logLevel),
|
||||
preferences: ref.watch(ConfigOptions.logLevel.notifier),
|
||||
choices: LogLevel.choices,
|
||||
title: t.settings.config.logLevel,
|
||||
presentChoice: (value) => value.name.toUpperCase(),
|
||||
SliverToBoxAdapter(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
TipCard(message: t.settings.experimentalMsg),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.logLevel),
|
||||
preferences: ref.watch(ConfigOptions.logLevel.notifier),
|
||||
choices: LogLevel.choices,
|
||||
title: t.settings.config.logLevel,
|
||||
presentChoice: (value) => value.name.toUpperCase(),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.route),
|
||||
SwitchListTile(
|
||||
title: Text(experimental(t.settings.config.bypassLan)),
|
||||
value: ref.watch(ConfigOptions.bypassLan),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.bypassLan.notifier).update,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.resolveDestination),
|
||||
value: ref.watch(ConfigOptions.resolveDestination),
|
||||
onChanged: ref
|
||||
.watch(ConfigOptions.resolveDestination.notifier)
|
||||
.update,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.ipv6Mode),
|
||||
preferences: ref.watch(ConfigOptions.ipv6Mode.notifier),
|
||||
choices: IPv6Mode.values,
|
||||
title: t.settings.config.ipv6Mode,
|
||||
presentChoice: (value) => value.present(t),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.dns),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.remoteDnsAddress),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.remoteDnsAddress.notifier),
|
||||
title: t.settings.config.remoteDnsAddress,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy),
|
||||
preferences: ref
|
||||
.watch(ConfigOptions.remoteDnsDomainStrategy.notifier),
|
||||
choices: DomainStrategy.values,
|
||||
title: t.settings.config.remoteDnsDomainStrategy,
|
||||
presentChoice: (value) => value.displayName,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.directDnsAddress),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.directDnsAddress.notifier),
|
||||
title: t.settings.config.directDnsAddress,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.directDnsDomainStrategy),
|
||||
preferences: ref
|
||||
.watch(ConfigOptions.directDnsDomainStrategy.notifier),
|
||||
choices: DomainStrategy.values,
|
||||
title: t.settings.config.directDnsDomainStrategy,
|
||||
presentChoice: (value) => value.displayName,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.enableDnsRouting),
|
||||
value: ref.watch(ConfigOptions.enableDnsRouting),
|
||||
onChanged: ref
|
||||
.watch(ConfigOptions.enableDnsRouting.notifier)
|
||||
.update,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(experimental(t.settings.config.section.mux)),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.enableMux),
|
||||
value: ref.watch(ConfigOptions.enableMux),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.enableMux.notifier).update,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.muxProtocol),
|
||||
preferences: ref.watch(ConfigOptions.muxProtocol.notifier),
|
||||
choices: MuxProtocol.values,
|
||||
title: t.settings.config.muxProtocol,
|
||||
presentChoice: (value) => value.name,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.muxMaxStreams),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.muxMaxStreams.notifier),
|
||||
title: t.settings.config.muxMaxStreams,
|
||||
inputToValue: int.tryParse,
|
||||
digitsOnly: true,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.inbound),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.serviceMode),
|
||||
preferences: ref.watch(ConfigOptions.serviceMode.notifier),
|
||||
choices: ServiceMode.choices,
|
||||
title: t.settings.config.serviceMode,
|
||||
presentChoice: (value) => value.present(t),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.strictRoute),
|
||||
value: ref.watch(ConfigOptions.strictRoute),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.strictRoute.notifier).update,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.tunImplementation),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.tunImplementation.notifier),
|
||||
choices: TunImplementation.values,
|
||||
title: t.settings.config.tunImplementation,
|
||||
presentChoice: (value) => value.name,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.mixedPort),
|
||||
preferences: ref.watch(ConfigOptions.mixedPort.notifier),
|
||||
title: t.settings.config.mixedPort,
|
||||
inputToValue: int.tryParse,
|
||||
digitsOnly: true,
|
||||
validateInput: isPort,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.localDnsPort),
|
||||
preferences: ref.watch(ConfigOptions.localDnsPort.notifier),
|
||||
title: t.settings.config.localDnsPort,
|
||||
inputToValue: int.tryParse,
|
||||
digitsOnly: true,
|
||||
validateInput: isPort,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
experimental(t.settings.config.allowConnectionFromLan),
|
||||
),
|
||||
value: ref.watch(ConfigOptions.allowConnectionFromLan),
|
||||
onChanged: ref
|
||||
.read(ConfigOptions.allowConnectionFromLan.notifier)
|
||||
.update,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.tlsTricks),
|
||||
SwitchListTile(
|
||||
title:
|
||||
Text(experimental(t.settings.config.enableTlsFragment)),
|
||||
value: ref.watch(ConfigOptions.enableTlsFragment),
|
||||
onChanged: ref
|
||||
.watch(ConfigOptions.enableTlsFragment.notifier)
|
||||
.update,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.tlsFragmentSize),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.tlsFragmentSize.notifier),
|
||||
title: t.settings.config.tlsFragmentSize,
|
||||
inputToValue: OptionalRange.tryParse,
|
||||
presentValue: (value) => value.present(t),
|
||||
formatInputValue: (value) => value.format(),
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.tlsFragmentSleep),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.tlsFragmentSleep.notifier),
|
||||
title: t.settings.config.tlsFragmentSleep,
|
||||
inputToValue: OptionalRange.tryParse,
|
||||
presentValue: (value) => value.present(t),
|
||||
formatInputValue: (value) => value.format(),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
experimental(t.settings.config.enableTlsMixedSniCase),
|
||||
),
|
||||
value: ref.watch(ConfigOptions.enableTlsMixedSniCase),
|
||||
onChanged: ref
|
||||
.watch(ConfigOptions.enableTlsMixedSniCase.notifier)
|
||||
.update,
|
||||
),
|
||||
SwitchListTile(
|
||||
title:
|
||||
Text(experimental(t.settings.config.enableTlsPadding)),
|
||||
value: ref.watch(ConfigOptions.enableTlsPadding),
|
||||
onChanged: ref
|
||||
.watch(ConfigOptions.enableTlsPadding.notifier)
|
||||
.update,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.tlsPaddingSize),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.tlsPaddingSize.notifier),
|
||||
title: t.settings.config.tlsPaddingSize,
|
||||
inputToValue: OptionalRange.tryParse,
|
||||
presentValue: (value) => value.format(),
|
||||
formatInputValue: (value) => value.format(),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(experimental(t.settings.config.section.warp)),
|
||||
WarpOptionsTiles(key: ConfigOptionSection._warpKey),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.misc),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.connectionTestUrl),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.connectionTestUrl.notifier),
|
||||
title: t.settings.config.connectionTestUrl,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.settings.config.urlTestInterval),
|
||||
subtitle: Text(
|
||||
ref
|
||||
.watch(ConfigOptions.urlTestInterval)
|
||||
.toApproximateTime(isRelativeToNow: false),
|
||||
),
|
||||
onTap: () async {
|
||||
final urlTestInterval = await SettingsSliderDialog(
|
||||
title: t.settings.config.urlTestInterval,
|
||||
initialValue: ref
|
||||
.watch(ConfigOptions.urlTestInterval)
|
||||
.inMinutes
|
||||
.coerceIn(0, 60)
|
||||
.toDouble(),
|
||||
onReset: ref
|
||||
.read(ConfigOptions.urlTestInterval.notifier)
|
||||
.reset,
|
||||
min: 1,
|
||||
max: 60,
|
||||
divisions: 60,
|
||||
labelGen: (value) => Duration(minutes: value.toInt())
|
||||
.toApproximateTime(isRelativeToNow: false),
|
||||
).show(context);
|
||||
if (urlTestInterval == null) return;
|
||||
await ref
|
||||
.read(ConfigOptions.urlTestInterval.notifier)
|
||||
.update(Duration(minutes: urlTestInterval.toInt()));
|
||||
},
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.clashApiPort),
|
||||
preferences: ref.watch(ConfigOptions.clashApiPort.notifier),
|
||||
title: t.settings.config.clashApiPort,
|
||||
validateInput: isPort,
|
||||
digitsOnly: true,
|
||||
inputToValue: int.tryParse,
|
||||
),
|
||||
const Gap(24),
|
||||
],
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.route),
|
||||
SwitchListTile(
|
||||
title: Text(experimental(t.settings.config.bypassLan)),
|
||||
value: ref.watch(ConfigOptions.bypassLan),
|
||||
onChanged: ref.watch(ConfigOptions.bypassLan.notifier).update,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.resolveDestination),
|
||||
value: ref.watch(ConfigOptions.resolveDestination),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.resolveDestination.notifier).update,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.ipv6Mode),
|
||||
preferences: ref.watch(ConfigOptions.ipv6Mode.notifier),
|
||||
choices: IPv6Mode.values,
|
||||
title: t.settings.config.ipv6Mode,
|
||||
presentChoice: (value) => value.present(t),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.dns),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.remoteDnsAddress),
|
||||
preferences: ref.watch(ConfigOptions.remoteDnsAddress.notifier),
|
||||
title: t.settings.config.remoteDnsAddress,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.remoteDnsDomainStrategy.notifier),
|
||||
choices: DomainStrategy.values,
|
||||
title: t.settings.config.remoteDnsDomainStrategy,
|
||||
presentChoice: (value) => value.displayName,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.directDnsAddress),
|
||||
preferences: ref.watch(ConfigOptions.directDnsAddress.notifier),
|
||||
title: t.settings.config.directDnsAddress,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.directDnsDomainStrategy),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.directDnsDomainStrategy.notifier),
|
||||
choices: DomainStrategy.values,
|
||||
title: t.settings.config.directDnsDomainStrategy,
|
||||
presentChoice: (value) => value.displayName,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.enableDnsRouting),
|
||||
value: ref.watch(ConfigOptions.enableDnsRouting),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.enableDnsRouting.notifier).update,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(experimental(t.settings.config.section.mux)),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.enableMux),
|
||||
value: ref.watch(ConfigOptions.enableMux),
|
||||
onChanged: ref.watch(ConfigOptions.enableMux.notifier).update,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.muxProtocol),
|
||||
preferences: ref.watch(ConfigOptions.muxProtocol.notifier),
|
||||
choices: MuxProtocol.values,
|
||||
title: t.settings.config.muxProtocol,
|
||||
presentChoice: (value) => value.name,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.muxMaxStreams),
|
||||
preferences: ref.watch(ConfigOptions.muxMaxStreams.notifier),
|
||||
title: t.settings.config.muxMaxStreams,
|
||||
inputToValue: int.tryParse,
|
||||
digitsOnly: true,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.inbound),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.serviceMode),
|
||||
preferences: ref.watch(ConfigOptions.serviceMode.notifier),
|
||||
choices: ServiceMode.choices,
|
||||
title: t.settings.config.serviceMode,
|
||||
presentChoice: (value) => value.present(t),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(t.settings.config.strictRoute),
|
||||
value: ref.watch(ConfigOptions.strictRoute),
|
||||
onChanged: ref.watch(ConfigOptions.strictRoute.notifier).update,
|
||||
),
|
||||
ChoicePreferenceWidget(
|
||||
selected: ref.watch(ConfigOptions.tunImplementation),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.tunImplementation.notifier),
|
||||
choices: TunImplementation.values,
|
||||
title: t.settings.config.tunImplementation,
|
||||
presentChoice: (value) => value.name,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.mixedPort),
|
||||
preferences: ref.watch(ConfigOptions.mixedPort.notifier),
|
||||
title: t.settings.config.mixedPort,
|
||||
inputToValue: int.tryParse,
|
||||
digitsOnly: true,
|
||||
validateInput: isPort,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.localDnsPort),
|
||||
preferences: ref.watch(ConfigOptions.localDnsPort.notifier),
|
||||
title: t.settings.config.localDnsPort,
|
||||
inputToValue: int.tryParse,
|
||||
digitsOnly: true,
|
||||
validateInput: isPort,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
experimental(t.settings.config.allowConnectionFromLan),
|
||||
),
|
||||
value: ref.watch(ConfigOptions.allowConnectionFromLan),
|
||||
onChanged: ref
|
||||
.read(ConfigOptions.allowConnectionFromLan.notifier)
|
||||
.update,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.tlsTricks),
|
||||
SwitchListTile(
|
||||
title: Text(experimental(t.settings.config.enableTlsFragment)),
|
||||
value: ref.watch(ConfigOptions.enableTlsFragment),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.enableTlsFragment.notifier).update,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.tlsFragmentSize),
|
||||
preferences: ref.watch(ConfigOptions.tlsFragmentSize.notifier),
|
||||
title: t.settings.config.tlsFragmentSize,
|
||||
inputToValue: OptionalRange.tryParse,
|
||||
presentValue: (value) => value.present(t),
|
||||
formatInputValue: (value) => value.format(),
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.tlsFragmentSleep),
|
||||
preferences: ref.watch(ConfigOptions.tlsFragmentSleep.notifier),
|
||||
title: t.settings.config.tlsFragmentSleep,
|
||||
inputToValue: OptionalRange.tryParse,
|
||||
presentValue: (value) => value.present(t),
|
||||
formatInputValue: (value) => value.format(),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
experimental(t.settings.config.enableTlsMixedSniCase),
|
||||
),
|
||||
value: ref.watch(ConfigOptions.enableTlsMixedSniCase),
|
||||
onChanged: ref
|
||||
.watch(ConfigOptions.enableTlsMixedSniCase.notifier)
|
||||
.update,
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(experimental(t.settings.config.enableTlsPadding)),
|
||||
value: ref.watch(ConfigOptions.enableTlsPadding),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.enableTlsPadding.notifier).update,
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.tlsPaddingSize),
|
||||
preferences: ref.watch(ConfigOptions.tlsPaddingSize.notifier),
|
||||
title: t.settings.config.tlsPaddingSize,
|
||||
inputToValue: OptionalRange.tryParse,
|
||||
presentValue: (value) => value.format(),
|
||||
formatInputValue: (value) => value.format(),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(experimental(t.settings.config.section.warp)),
|
||||
const WarpOptionsTiles(),
|
||||
const SettingsDivider(),
|
||||
SettingsSection(t.settings.config.section.misc),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.connectionTestUrl),
|
||||
preferences:
|
||||
ref.watch(ConfigOptions.connectionTestUrl.notifier),
|
||||
title: t.settings.config.connectionTestUrl,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.settings.config.urlTestInterval),
|
||||
subtitle: Text(
|
||||
ref
|
||||
.watch(ConfigOptions.urlTestInterval)
|
||||
.toApproximateTime(isRelativeToNow: false),
|
||||
),
|
||||
onTap: () async {
|
||||
final urlTestInterval = await SettingsSliderDialog(
|
||||
title: t.settings.config.urlTestInterval,
|
||||
initialValue: ref
|
||||
.watch(ConfigOptions.urlTestInterval)
|
||||
.inMinutes
|
||||
.coerceIn(0, 60)
|
||||
.toDouble(),
|
||||
onReset:
|
||||
ref.read(ConfigOptions.urlTestInterval.notifier).reset,
|
||||
min: 1,
|
||||
max: 60,
|
||||
divisions: 60,
|
||||
labelGen: (value) => Duration(minutes: value.toInt())
|
||||
.toApproximateTime(isRelativeToNow: false),
|
||||
).show(context);
|
||||
if (urlTestInterval == null) return;
|
||||
await ref
|
||||
.read(ConfigOptions.urlTestInterval.notifier)
|
||||
.update(Duration(minutes: urlTestInterval.toInt()));
|
||||
},
|
||||
),
|
||||
ValuePreferenceWidget(
|
||||
value: ref.watch(ConfigOptions.clashApiPort),
|
||||
preferences: ref.watch(ConfigOptions.clashApiPort.notifier),
|
||||
title: t.settings.config.clashApiPort,
|
||||
validateInput: isPort,
|
||||
digitsOnly: true,
|
||||
inputToValue: int.tryParse,
|
||||
),
|
||||
const Gap(24),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
84
lib/features/config_option/widget/quick_settings_modal.dart
Normal file
84
lib/features/config_option/widget/quick_settings_modal.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/router/router.dart';
|
||||
import 'package:hiddify/features/config_option/data/config_option_repository.dart';
|
||||
import 'package:hiddify/features/config_option/notifier/warp_option_notifier.dart';
|
||||
import 'package:hiddify/features/config_option/overview/config_options_page.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class QuickSettingsModal extends HookConsumerWidget {
|
||||
const QuickSettingsModal({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final t = ref.watch(translationsProvider);
|
||||
|
||||
final warpPrefaceCompleted =
|
||||
ref.watch(warpOptionNotifierProvider).consentGiven;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: SegmentedButton(
|
||||
segments: ServiceMode.choices
|
||||
.map(
|
||||
(e) => ButtonSegment(
|
||||
value: e,
|
||||
label: Text(
|
||||
e.presentShort(t),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
tooltip:
|
||||
e.isExperimental ? t.settings.experimental : null,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
selected: {ref.watch(ConfigOptions.serviceMode)},
|
||||
onSelectionChanged: (newSet) => ref
|
||||
.read(ConfigOptions.serviceMode.notifier)
|
||||
.update(newSet.first),
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
if (warpPrefaceCompleted)
|
||||
SwitchListTile(
|
||||
value: ref.watch(ConfigOptions.enableWarp),
|
||||
onChanged: ref.watch(ConfigOptions.enableWarp.notifier).update,
|
||||
title: Text(t.settings.config.enableWarp),
|
||||
)
|
||||
else
|
||||
ListTile(
|
||||
title: Text(t.settings.config.setupWarp),
|
||||
trailing: const Icon(FluentIcons.chevron_right_24_regular),
|
||||
onTap: () =>
|
||||
ConfigOptionsRoute(section: ConfigOptionSection.warp.name)
|
||||
.go(context),
|
||||
),
|
||||
SwitchListTile(
|
||||
value: ref.watch(ConfigOptions.enableTlsFragment),
|
||||
onChanged:
|
||||
ref.watch(ConfigOptions.enableTlsFragment.notifier).update,
|
||||
title: Text(t.settings.config.enableTlsFragment),
|
||||
),
|
||||
SwitchListTile(
|
||||
value: ref.watch(ConfigOptions.enableMux),
|
||||
onChanged: ref.watch(ConfigOptions.enableMux.notifier).update,
|
||||
title: Text(t.settings.config.enableMux),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.settings.config.allOptions),
|
||||
trailing: const Icon(FluentIcons.chevron_right_24_regular),
|
||||
dense: true,
|
||||
onTap: () => const ConfigOptionsRoute().go(context),
|
||||
),
|
||||
const Gap(16),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/model/failures.dart';
|
||||
import 'package:hiddify/core/theme/theme_extensions.dart';
|
||||
import 'package:hiddify/features/config_option/data/config_option_repository.dart';
|
||||
import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart';
|
||||
import 'package:hiddify/features/connection/model/connection_status.dart';
|
||||
import 'package:hiddify/features/connection/notifier/connection_notifier.dart';
|
||||
import 'package:hiddify/features/connection/widget/experimental_feature_notice.dart';
|
||||
import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart';
|
||||
import 'package:hiddify/gen/assets.gen.dart';
|
||||
import 'package:hiddify/utils/alerts.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
@@ -20,6 +22,8 @@ class ConnectionButton extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final t = ref.watch(translationsProvider);
|
||||
final connectionStatus = ref.watch(connectionNotifierProvider);
|
||||
final requiresReconnect =
|
||||
ref.watch(configOptionNotifierProvider).valueOrNull;
|
||||
|
||||
ref.listen(
|
||||
connectionNotifierProvider,
|
||||
@@ -37,55 +41,58 @@ class ConnectionButton extends HookConsumerWidget {
|
||||
|
||||
final buttonTheme = Theme.of(context).extension<ConnectionButtonTheme>()!;
|
||||
|
||||
switch (connectionStatus) {
|
||||
case AsyncData(value: final status):
|
||||
final Color connectionLogoColor = status.isConnected
|
||||
? buttonTheme.connectedColor!
|
||||
: buttonTheme.idleColor!;
|
||||
Future<bool> showExperimentalNotice() async {
|
||||
final hasExperimental = ref.read(ConfigOptions.hasExperimentalFeatures);
|
||||
final canShowNotice = !ref.read(disableExperimentalFeatureNoticeProvider);
|
||||
if (hasExperimental && canShowNotice && context.mounted) {
|
||||
return await const ExperimentalFeatureNoticeDialog().show(context) ??
|
||||
true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return _ConnectionButton(
|
||||
onTap: () async {
|
||||
var canConnect = true;
|
||||
if (status case Disconnected()) {
|
||||
final hasExperimental =
|
||||
ref.read(ConfigOptions.hasExperimentalFeatures);
|
||||
final canShowNotice =
|
||||
!ref.read(disableExperimentalFeatureNoticeProvider);
|
||||
|
||||
if (hasExperimental && canShowNotice && context.mounted) {
|
||||
canConnect = await const ExperimentalFeatureNoticeDialog()
|
||||
.show(context) ??
|
||||
true;
|
||||
}
|
||||
}
|
||||
|
||||
if (canConnect) {
|
||||
await ref
|
||||
return _ConnectionButton(
|
||||
onTap: switch (connectionStatus) {
|
||||
AsyncData(value: Disconnected()) || AsyncError() => () async {
|
||||
if (await showExperimentalNotice()) {
|
||||
return await ref
|
||||
.read(connectionNotifierProvider.notifier)
|
||||
.toggleConnection();
|
||||
}
|
||||
},
|
||||
enabled: !status.isSwitching,
|
||||
label: status.present(t),
|
||||
buttonColor: connectionLogoColor,
|
||||
);
|
||||
case AsyncError():
|
||||
return _ConnectionButton(
|
||||
onTap: () =>
|
||||
ref.read(connectionNotifierProvider.notifier).toggleConnection(),
|
||||
enabled: true,
|
||||
label: const Disconnected().present(t),
|
||||
buttonColor: buttonTheme.idleColor!,
|
||||
);
|
||||
default:
|
||||
// HACK
|
||||
return _ConnectionButton(
|
||||
onTap: () {},
|
||||
enabled: false,
|
||||
label: "",
|
||||
buttonColor: Colors.red,
|
||||
);
|
||||
}
|
||||
AsyncData(value: Connected()) => () async {
|
||||
if (requiresReconnect == true && await showExperimentalNotice()) {
|
||||
return await ref
|
||||
.read(connectionNotifierProvider.notifier)
|
||||
.reconnect(await ref.read(activeProfileProvider.future));
|
||||
}
|
||||
return await ref
|
||||
.read(connectionNotifierProvider.notifier)
|
||||
.toggleConnection();
|
||||
},
|
||||
_ => () {},
|
||||
},
|
||||
enabled: switch (connectionStatus) {
|
||||
AsyncData(value: Connected()) ||
|
||||
AsyncData(value: Disconnected()) ||
|
||||
AsyncError() =>
|
||||
true,
|
||||
_ => false,
|
||||
},
|
||||
label: switch (connectionStatus) {
|
||||
AsyncData(value: Connected()) when requiresReconnect == true =>
|
||||
t.home.connection.reconnect,
|
||||
AsyncData(value: final status) => status.present(t),
|
||||
_ => "",
|
||||
},
|
||||
buttonColor: switch (connectionStatus) {
|
||||
AsyncData(value: Connected()) when requiresReconnect == true =>
|
||||
Colors.teal,
|
||||
AsyncData(value: Connected()) => buttonTheme.connectedColor!,
|
||||
AsyncData(value: _) => buttonTheme.idleColor!,
|
||||
_ => Colors.red,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,11 +139,17 @@ class _ConnectionButton extends StatelessWidget {
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(36),
|
||||
child: Assets.images.logo.svg(
|
||||
colorFilter: ColorFilter.mode(
|
||||
buttonColor,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
child: TweenAnimationBuilder(
|
||||
tween: ColorTween(end: buttonColor),
|
||||
duration: const Duration(milliseconds: 250),
|
||||
builder: (context, value, child) {
|
||||
return Assets.images.logo.svg(
|
||||
colorFilter: ColorFilter.mode(
|
||||
value!,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -147,9 +160,25 @@ class _ConnectionButton extends StatelessWidget {
|
||||
),
|
||||
const Gap(16),
|
||||
ExcludeSemantics(
|
||||
child: Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
transitionBuilder: (child, animation) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(0.0, -0.2),
|
||||
end: Offset.zero,
|
||||
).animate(animation),
|
||||
child: FadeTransition(
|
||||
opacity: animation,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
label,
|
||||
key: ValueKey<String>(label),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -45,6 +45,11 @@ class HomePage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => const QuickSettingsRoute().push(context),
|
||||
icon: const Icon(FluentIcons.options_24_filled),
|
||||
tooltip: t.settings.config.quickSettings,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => const AddProfileRoute().push(context),
|
||||
icon: const Icon(FluentIcons.add_circle_24_filled),
|
||||
|
||||
@@ -29,13 +29,26 @@ enum ServiceMode {
|
||||
return [proxy, tun];
|
||||
}
|
||||
|
||||
bool get isExperimental => switch (this) {
|
||||
tun => PlatformUtils.isDesktop,
|
||||
tunService => PlatformUtils.isDesktop,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
String present(TranslationsEn t) => switch (this) {
|
||||
proxy => t.settings.config.serviceModes.proxy,
|
||||
systemProxy => t.settings.config.serviceModes.systemProxy,
|
||||
tun =>
|
||||
"${t.settings.config.serviceModes.tun}${PlatformUtils.isDesktop ? " (${t.settings.experimental})" : ""}",
|
||||
"${t.settings.config.serviceModes.tun}${isExperimental ? " (${t.settings.experimental})" : ""}",
|
||||
tunService =>
|
||||
"${t.settings.config.serviceModes.tunService}${PlatformUtils.isDesktop ? " (${t.settings.experimental})" : ""}",
|
||||
"${t.settings.config.serviceModes.tunService}${isExperimental ? " (${t.settings.experimental})" : ""}",
|
||||
};
|
||||
|
||||
String presentShort(TranslationsEn t) => switch (this) {
|
||||
proxy => t.settings.config.shortServiceModes.proxy,
|
||||
systemProxy => t.settings.config.shortServiceModes.systemProxy,
|
||||
tun => t.settings.config.shortServiceModes.tun,
|
||||
tunService => t.settings.config.shortServiceModes.tunService,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- device_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- flutter_timezone (0.1.0):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- mobile_scanner (3.5.6):
|
||||
- FlutterMacOS
|
||||
@@ -52,6 +54,7 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- cupertino_http (from `Flutter/ephemeral/.symlinks/plugins/cupertino_http/macos`)
|
||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||
- flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
|
||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||
@@ -77,6 +80,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/cupertino_http/macos
|
||||
device_info_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
||||
flutter_timezone:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
mobile_scanner:
|
||||
@@ -107,6 +112,7 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
cupertino_http: afa11b9e2786b62da2671e4ddd32caf792503748
|
||||
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||
flutter_timezone: 6b906d1740654acb16e50b639835628fea851037
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
mobile_scanner: 54ceceae0c8da2457e26a362a6be5c61154b1829
|
||||
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
||||
|
||||
130
pubspec.lock
130
pubspec.lock
@@ -221,10 +221,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: combine
|
||||
sha256: c400552a66ab05ec98a01927b78cc548233804b6e3240cff3b038204ffda7bee
|
||||
sha256: "8b52083c822a614a448fdd307e78c05266080e9747604b61fca5ddfe736a6c1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.6"
|
||||
version: "0.5.7-0.1.pre"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -245,10 +245,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
|
||||
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+8"
|
||||
version: "0.3.4+1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -309,18 +309,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dart_mappable
|
||||
sha256: "7b6d38ae95f1ae8ffa65df9a5464f14b56c2de94699a035202ca4cd3a0ba249e"
|
||||
sha256: f9f272f2af6c11adf4abc22574eb946df110251052a0d00c03519ecf2442defc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.1"
|
||||
dart_mappable_builder:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: dart_mappable_builder
|
||||
sha256: "98c058f7e80a98ea42d357d888ed1648d96bedac8b16872b58fc7024faefcdfe"
|
||||
sha256: dd42e99b7e605ad6cf4a5c241a777c0f4f76a9769f27c439795355c444283074
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.1"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -389,18 +389,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: drift
|
||||
sha256: b50a8342c6ddf05be53bda1d246404cbad101b64dc73e8d6d1ac1090d119b4e2
|
||||
sha256: "3b276c838ff7f8e19aac18a51f9b388715268f3534eaaf8047c8455ef3c1738d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.15.0"
|
||||
version: "2.16.0"
|
||||
drift_dev:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: drift_dev
|
||||
sha256: c037d9431b6f8dc633652b1469e5f53aaec6e4eb405ed29dd232fa888ef10d88
|
||||
sha256: "66cf3e397448f855523d7b6b7b3789db232b211db96543a42285464d05f3bf72"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.15.0"
|
||||
version: "2.16.0"
|
||||
equatable:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -421,10 +421,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.2"
|
||||
ffigen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -469,10 +469,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fluentui_system_icons
|
||||
sha256: abe7c343e2151e0ad6544653e0b6601686b993bc436ccde72b88cea677db0c0a
|
||||
sha256: bb2dd7b5dc2c14c8a18cab6405a67c4697825d2662553b1b22d1f7ddc1057235
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.226"
|
||||
version: "1.1.229"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -623,10 +623,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_svg
|
||||
sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c
|
||||
sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.9"
|
||||
version: "2.0.10+1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -745,10 +745,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -869,6 +869,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
lint:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -897,18 +921,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.8.0"
|
||||
maybe_just_nothing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -929,10 +953,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: meta
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1001,10 +1025,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_parsing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1393,10 +1417,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.3.0"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1454,26 +1478,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: slang
|
||||
sha256: "95dee03eb3fd1b36c99f365d4eace270a0d83c6148f8e7d1057806ef60cfaf12"
|
||||
sha256: "5e08ac915ac27a3508863f37734280d30c3713d56746cd2e4a5da77413da4b95"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.29.0"
|
||||
version: "3.30.1"
|
||||
slang_build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: slang_build_runner
|
||||
sha256: "929ea4bf24f11e09afd2b01abd658f550da7eb4039ae83d91bc220f942e18cb3"
|
||||
sha256: "2daff2deb2ab8d557a2e7de5405c0ee1376afba5d0231570c2d2c3c56da8a692"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.29.0"
|
||||
version: "3.30.0"
|
||||
slang_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "34c7cf297c608e24d3957a29e75c6790f4dbbfb1a4783d261a6c1e33ede7ad0f"
|
||||
sha256: "9ee040b0d364d3a4d692e4af536acff6ef513870689403494ebc6d59b0dccea6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.29.0"
|
||||
version: "3.30.0"
|
||||
sliver_tools:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1542,10 +1566,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: c4a4c5a4b2a32e2d0f6837b33d7c91a67903891a5b7dbe706cf4b1f6b0c798c5
|
||||
sha256: "072128763f1547e3e9b4735ce846bfd226d68019ccda54db4cd427b12dfdedc9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.4.0"
|
||||
sqlite3_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1734,10 +1758,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
|
||||
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.2.5"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1782,10 +1806,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
|
||||
sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.3.0"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1814,26 +1838,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics
|
||||
sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172"
|
||||
sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.9+2"
|
||||
version: "1.1.11+1"
|
||||
vector_graphics_codec:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics_codec
|
||||
sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d"
|
||||
sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.9+2"
|
||||
version: "1.1.11+1"
|
||||
vector_graphics_compiler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics_compiler
|
||||
sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad"
|
||||
sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.9+2"
|
||||
version: "1.1.11+1"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1867,13 +1891,13 @@ packages:
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: web
|
||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.5.1"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1955,5 +1979,5 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
sdks:
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.16.0"
|
||||
dart: ">=3.3.0 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
|
||||
30
pubspec.yaml
30
pubspec.yaml
@@ -4,7 +4,7 @@ publish_to: "none"
|
||||
version: 0.16.16+1616
|
||||
|
||||
environment:
|
||||
sdk: ">=3.2.0 <4.0.0"
|
||||
sdk: ">=3.3.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
@@ -13,8 +13,8 @@ dependencies:
|
||||
sdk: flutter
|
||||
cupertino_icons: ^1.0.6
|
||||
intl: ^0.18.1
|
||||
slang: ^3.29.0
|
||||
slang_flutter: ^3.29.0
|
||||
slang: ^3.30.1
|
||||
slang_flutter: ^3.30.0
|
||||
fpdart: ^1.1.0
|
||||
freezed_annotation: ^2.4.1
|
||||
json_annotation: ^4.8.1
|
||||
@@ -22,11 +22,11 @@ dependencies:
|
||||
flutter_hooks: ^0.20.5
|
||||
riverpod_annotation: ^2.3.4
|
||||
rxdart: ^0.27.7
|
||||
drift: ^2.15.0
|
||||
drift: ^2.16.0
|
||||
sqlite3_flutter_libs: ^0.5.20
|
||||
shared_preferences: ^2.2.2
|
||||
dio: ^5.4.1
|
||||
ffi: ^2.1.0
|
||||
ffi: ^2.1.2
|
||||
path_provider: ^2.1.1
|
||||
mobile_scanner: ^4.0.0
|
||||
protocol_handler: ^0.2.0
|
||||
@@ -35,12 +35,12 @@ dependencies:
|
||||
window_manager: ^0.3.8
|
||||
tray_manager: ^0.2.1
|
||||
package_info_plus: ^5.0.1
|
||||
url_launcher: ^6.2.4
|
||||
url_launcher: ^6.2.5
|
||||
vclibs: ^0.1.2
|
||||
launch_at_startup: ^0.2.2
|
||||
sentry_flutter: ^7.16.1
|
||||
sentry_dart_plugin: ^1.7.1
|
||||
combine: ^0.5.6
|
||||
combine: ^0.5.7-0.1.pre
|
||||
path: ^1.8.3
|
||||
loggy: ^2.0.3
|
||||
flutter_loggy: ^2.0.2
|
||||
@@ -54,7 +54,7 @@ dependencies:
|
||||
go_router: ^13.2.0
|
||||
flex_color_scheme: ^7.3.1
|
||||
flutter_animate: ^4.5.0
|
||||
flutter_svg: ^2.0.9
|
||||
flutter_svg: ^2.0.10+1
|
||||
gap: ^3.0.1
|
||||
percent_indicator: ^4.2.3
|
||||
sliver_tools: ^0.2.12
|
||||
@@ -71,8 +71,8 @@ dependencies:
|
||||
dio_smart_retry: ^6.0.0
|
||||
cupertino_http: ^1.3.0
|
||||
wolt_modal_sheet: ^0.4.1
|
||||
dart_mappable: ^4.2.0
|
||||
fluentui_system_icons: ^1.1.226
|
||||
dart_mappable: ^4.2.1
|
||||
fluentui_system_icons: ^1.1.229
|
||||
circle_flags: ^4.0.2
|
||||
http: ^1.2.0
|
||||
timezone_to_country: ^2.1.0
|
||||
@@ -86,13 +86,17 @@ dev_dependencies:
|
||||
json_serializable: ^6.7.1
|
||||
freezed: ^2.4.7
|
||||
riverpod_generator: ^2.3.11
|
||||
drift_dev: ^2.15.0
|
||||
drift_dev: ^2.16.0
|
||||
ffigen: ^8.0.2
|
||||
slang_build_runner: ^3.29.0
|
||||
slang_build_runner: ^3.30.0
|
||||
flutter_gen_runner: ^5.4.0
|
||||
go_router_builder: ^2.4.1
|
||||
dependency_validator: ^3.2.3
|
||||
dart_mappable_builder: ^4.2.0
|
||||
dart_mappable_builder: ^4.2.1
|
||||
|
||||
dependency_overrides:
|
||||
# drift & package_info_plus are not compatible
|
||||
web: ^0.5.1
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
Reference in New Issue
Block a user