Merge branch 'be1c1bb580ec039044e8057ae1469153008cdb4d'

This commit is contained in:
Hiddify
2024-03-07 17:23:53 +01:00
19 changed files with 661 additions and 409 deletions

View File

@@ -1,8 +1,6 @@
[![Release Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/RELEASE_TAG/total?style=flat-square&logo=github)](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">

View File

@@ -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 }}"

View File

@@ -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

View File

@@ -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'

View File

@@ -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()

View File

@@ -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"

View File

@@ -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",

View File

@@ -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);

View File

@@ -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);
});
}

View 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),
);
}
}

View File

@@ -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);
}

View File

@@ -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),
],
),
),
],
),

View 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),
],
),
);
}
}

View File

@@ -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,
),
),
),
],

View File

@@ -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),

View File

@@ -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,
};
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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