diff --git a/.github/release_message.md b/.github/release_message.md
index a8aa9d8e..9d281a37 100644
--- a/.github/release_message.md
+++ b/.github/release_message.md
@@ -1,8 +1,6 @@
[](https://img.shields.io/github/downloads/hiddify/hiddify-next/RELEASE_TAG/)
-
-
-
+
**Release Highlights:**
@@ -27,6 +25,8 @@
+
+
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9cec7dc8..bac14463 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -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 }}"
diff --git a/Makefile b/Makefile
index ee84238c..c310634f 100644
--- a/Makefile
+++ b/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
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 869d8e28..cd56b934 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -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'
diff --git a/android/build.gradle b/android/build.gradle
index d02fac26..bc157bd1 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -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()
diff --git a/android/settings.gradle b/android/settings.gradle
index 44e62bcf..cf00f3cb 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -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"
diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json
index 3aee9c91..2bb34ce8 100644
--- a/assets/translations/strings_en.i18n.json
+++ b/assets/translations/strings_en.i18n.json
@@ -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",
diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart
index b57726fc..c9abc2fb 100644
--- a/lib/bootstrap.dart
+++ b/lib/bootstrap.dart
@@ -93,7 +93,6 @@ Future
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 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 _init(
int? timeout,
}) async {
final stopWatch = Stopwatch()..start();
+ Logger.bootstrap.info("initializing [$name]");
Future 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);
diff --git a/lib/core/database/connection/database_connection.dart b/lib/core/database/connection/database_connection.dart
index 046a44f4..16cffb43 100644
--- a/lib/core/database/connection/database_connection.dart
+++ b/lib/core/database/connection/database_connection.dart
@@ -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);
});
}
diff --git a/lib/core/router/routes.dart b/lib/core/router/routes.dart
index f7a1f349..0cfce287 100644
--- a/lib/core/router/routes.dart
+++ b/lib/core/router/routes.dart
@@ -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? _dynamicRootKey =
path: "config-options",
name: ConfigOptionsRoute.name,
),
+ TypedGoRoute(
+ path: "quick-settings",
+ name: QuickSettingsRoute.name,
+ ),
TypedGoRoute(
path: "settings",
name: SettingsRoute.name,
@@ -108,6 +113,10 @@ class MobileWrapperRoute extends ShellRouteData {
path: "profiles/:id",
name: ProfileDetailsRoute.name,
),
+ TypedGoRoute(
+ path: "quick-settings",
+ name: QuickSettingsRoute.name,
+ ),
],
),
TypedGoRoute(
@@ -277,6 +286,22 @@ class LogsOverviewRoute extends GoRouteData {
}
}
+class QuickSettingsRoute extends GoRouteData {
+ const QuickSettingsRoute();
+ static const name = "Quick Settings";
+
+ static final GlobalKey $parentNavigatorKey = rootNavigatorKey;
+
+ @override
+ Page 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? $parentNavigatorKey = _dynamicRootKey;
@@ -304,12 +330,15 @@ class ConfigOptionsRoute extends GoRouteData {
@override
Page 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),
+ );
}
}
diff --git a/lib/features/config_option/notifier/config_option_notifier.dart b/lib/features/config_option/notifier/config_option_notifier.dart
index 4c04e621..cd7793b5 100644
--- a/lib/features/config_option/notifier/config_option_notifier.dart
+++ b/lib/features/config_option/notifier/config_option_notifier.dart
@@ -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);
}
diff --git a/lib/features/config_option/overview/config_options_page.dart b/lib/features/config_option/overview/config_options_page.dart
index a916cad1..bc9764ff 100644
--- a/lib/features/config_option/overview/config_options_page.dart
+++ b/lib/features/config_option/overview/config_options_page.dart
@@ -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),
- ],
+ ),
),
],
),
diff --git a/lib/features/config_option/widget/quick_settings_modal.dart b/lib/features/config_option/widget/quick_settings_modal.dart
new file mode 100644
index 00000000..e546091c
--- /dev/null
+++ b/lib/features/config_option/widget/quick_settings_modal.dart
@@ -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),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/features/home/widget/connection_button.dart b/lib/features/home/widget/connection_button.dart
index 7ea7d0bf..d7074460 100644
--- a/lib/features/home/widget/connection_button.dart
+++ b/lib/features/home/widget/connection_button.dart
@@ -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()!;
- switch (connectionStatus) {
- case AsyncData(value: final status):
- final Color connectionLogoColor = status.isConnected
- ? buttonTheme.connectedColor!
- : buttonTheme.idleColor!;
+ Future 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(
+ begin: const Offset(0.0, -0.2),
+ end: Offset.zero,
+ ).animate(animation),
+ child: FadeTransition(
+ opacity: animation,
+ child: child,
+ ),
+ );
+ },
+ child: Text(
+ label,
+ key: ValueKey(label),
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
),
),
],
diff --git a/lib/features/home/widget/home_page.dart b/lib/features/home/widget/home_page.dart
index 13b20d30..0bf6d4a5 100644
--- a/lib/features/home/widget/home_page.dart
+++ b/lib/features/home/widget/home_page.dart
@@ -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),
diff --git a/lib/singbox/model/singbox_config_enum.dart b/lib/singbox/model/singbox_config_enum.dart
index 49c157a7..82990e8a 100644
--- a/lib/singbox/model/singbox_config_enum.dart
+++ b/lib/singbox/model/singbox_config_enum.dart
@@ -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,
};
}
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index 6bdb9749..613ba947 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -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
diff --git a/pubspec.lock b/pubspec.lock
index 71c3cdda..a568bb9a 100644
--- a/pubspec.lock
+++ b/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"
diff --git a/pubspec.yaml b/pubspec.yaml
index ab48500a..2dc27de1 100644
--- a/pubspec.yaml
+++ b/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