diff --git a/lib/data/model/app/menu/server_func.dart b/lib/data/model/app/menu/server_func.dart index 046dcc9b..4ccff487 100644 --- a/lib/data/model/app/menu/server_func.dart +++ b/lib/data/model/app/menu/server_func.dart @@ -12,21 +12,33 @@ enum ServerFuncBtn { snippet(), iperf(), // pve(), - systemd(1058); + systemd(1058), + portForward(1340); final int? addedVersion; const ServerFuncBtn([this.addedVersion]); static void autoAddNewFuncs(int cur) { - if (cur >= systemd.addedVersion!) { - final prop = Stores.setting.serverFuncBtns; - final list = prop.fetch(); + final prop = Stores.setting.serverFuncBtns; + final list = prop.fetch(); + final originalLength = list.length; + + if (systemd.addedVersion != null && cur >= systemd.addedVersion!) { if (!list.contains(systemd.index)) { list.add(systemd.index); - prop.put(list); } } + + if (portForward.addedVersion != null && cur >= portForward.addedVersion!) { + if (!list.contains(portForward.index)) { + list.add(portForward.index); + } + } + + if (list.length > originalLength) { + prop.put(list); + } } static final defaultIdxs = [ @@ -48,6 +60,7 @@ enum ServerFuncBtn { terminal => Icons.terminal, iperf => Icons.speed, systemd => MingCute.plugin_2_fill, + portForward => Icons.compare_arrows, }; String get toStr => switch (this) { @@ -59,5 +72,6 @@ enum ServerFuncBtn { terminal => libL10n.terminal, iperf => 'iperf', systemd => 'Systemd', + portForward => libL10n.portForward, }; } diff --git a/lib/data/model/server/port_forward.dart b/lib/data/model/server/port_forward.dart new file mode 100644 index 00000000..79b2860c --- /dev/null +++ b/lib/data/model/server/port_forward.dart @@ -0,0 +1,59 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'port_forward.freezed.dart'; +part 'port_forward.g.dart'; + +@freezed +abstract class PortForwardConfig with _$PortForwardConfig { + const factory PortForwardConfig({ + required String id, + required String serverId, + required String name, + @Default('localhost') String localHost, + required int localPort, + required String remoteHost, + required int remotePort, + String? description, + }) = _PortForwardConfig; + + factory PortForwardConfig.fromJson(Map json) => _$PortForwardConfigFromJson(json); + + const PortForwardConfig._(); + + String get displayAddr => '$localHost:$localPort → $remoteHost:$remotePort'; +} + +@freezed +abstract class PortForwardState with _$PortForwardState { + const factory PortForwardState({ + required String serverId, + @Default([]) List configs, + @Default({}) Map activeForwards, + }) = _PortForwardState; +} + +class PortForwardStatus { + final String id; + final bool isActive; + final String? error; + + const PortForwardStatus({ + required this.id, + this.isActive = false, + this.error, + }); + + PortForwardStatus copyWith({ + String? id, + bool? isActive, + Object? error = _sentinel, + }) { + return PortForwardStatus( + id: id ?? this.id, + isActive: isActive ?? this.isActive, + error: error == _sentinel ? this.error : error as String?, + ); + } +} + +const _sentinel = Object(); diff --git a/lib/data/model/server/port_forward.freezed.dart b/lib/data/model/server/port_forward.freezed.dart new file mode 100644 index 00000000..567c5742 --- /dev/null +++ b/lib/data/model/server/port_forward.freezed.dart @@ -0,0 +1,573 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'port_forward.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$PortForwardConfig { + + String get id; String get serverId; String get name; String get localHost; int get localPort; String get remoteHost; int get remotePort; String? get description; +/// Create a copy of PortForwardConfig +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$PortForwardConfigCopyWith get copyWith => _$PortForwardConfigCopyWithImpl(this as PortForwardConfig, _$identity); + + /// Serializes this PortForwardConfig to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is PortForwardConfig&&(identical(other.id, id) || other.id == id)&&(identical(other.serverId, serverId) || other.serverId == serverId)&&(identical(other.name, name) || other.name == name)&&(identical(other.localHost, localHost) || other.localHost == localHost)&&(identical(other.localPort, localPort) || other.localPort == localPort)&&(identical(other.remoteHost, remoteHost) || other.remoteHost == remoteHost)&&(identical(other.remotePort, remotePort) || other.remotePort == remotePort)&&(identical(other.description, description) || other.description == description)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,serverId,name,localHost,localPort,remoteHost,remotePort,description); + +@override +String toString() { + return 'PortForwardConfig(id: $id, serverId: $serverId, name: $name, localHost: $localHost, localPort: $localPort, remoteHost: $remoteHost, remotePort: $remotePort, description: $description)'; +} + + +} + +/// @nodoc +abstract mixin class $PortForwardConfigCopyWith<$Res> { + factory $PortForwardConfigCopyWith(PortForwardConfig value, $Res Function(PortForwardConfig) _then) = _$PortForwardConfigCopyWithImpl; +@useResult +$Res call({ + String id, String serverId, String name, String localHost, int localPort, String remoteHost, int remotePort, String? description +}); + + + + +} +/// @nodoc +class _$PortForwardConfigCopyWithImpl<$Res> + implements $PortForwardConfigCopyWith<$Res> { + _$PortForwardConfigCopyWithImpl(this._self, this._then); + + final PortForwardConfig _self; + final $Res Function(PortForwardConfig) _then; + +/// Create a copy of PortForwardConfig +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? serverId = null,Object? name = null,Object? localHost = null,Object? localPort = null,Object? remoteHost = null,Object? remotePort = null,Object? description = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,serverId: null == serverId ? _self.serverId : serverId // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,localHost: null == localHost ? _self.localHost : localHost // ignore: cast_nullable_to_non_nullable +as String,localPort: null == localPort ? _self.localPort : localPort // ignore: cast_nullable_to_non_nullable +as int,remoteHost: null == remoteHost ? _self.remoteHost : remoteHost // ignore: cast_nullable_to_non_nullable +as String,remotePort: null == remotePort ? _self.remotePort : remotePort // ignore: cast_nullable_to_non_nullable +as int,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [PortForwardConfig]. +extension PortForwardConfigPatterns on PortForwardConfig { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _PortForwardConfig value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _PortForwardConfig() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _PortForwardConfig value) $default,){ +final _that = this; +switch (_that) { +case _PortForwardConfig(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _PortForwardConfig value)? $default,){ +final _that = this; +switch (_that) { +case _PortForwardConfig() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String serverId, String name, String localHost, int localPort, String remoteHost, int remotePort, String? description)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _PortForwardConfig() when $default != null: +return $default(_that.id,_that.serverId,_that.name,_that.localHost,_that.localPort,_that.remoteHost,_that.remotePort,_that.description);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String serverId, String name, String localHost, int localPort, String remoteHost, int remotePort, String? description) $default,) {final _that = this; +switch (_that) { +case _PortForwardConfig(): +return $default(_that.id,_that.serverId,_that.name,_that.localHost,_that.localPort,_that.remoteHost,_that.remotePort,_that.description);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String serverId, String name, String localHost, int localPort, String remoteHost, int remotePort, String? description)? $default,) {final _that = this; +switch (_that) { +case _PortForwardConfig() when $default != null: +return $default(_that.id,_that.serverId,_that.name,_that.localHost,_that.localPort,_that.remoteHost,_that.remotePort,_that.description);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _PortForwardConfig extends PortForwardConfig { + const _PortForwardConfig({required this.id, required this.serverId, required this.name, this.localHost = 'localhost', required this.localPort, required this.remoteHost, required this.remotePort, this.description}): super._(); + factory _PortForwardConfig.fromJson(Map json) => _$PortForwardConfigFromJson(json); + +@override final String id; +@override final String serverId; +@override final String name; +@override@JsonKey() final String localHost; +@override final int localPort; +@override final String remoteHost; +@override final int remotePort; +@override final String? description; + +/// Create a copy of PortForwardConfig +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$PortForwardConfigCopyWith<_PortForwardConfig> get copyWith => __$PortForwardConfigCopyWithImpl<_PortForwardConfig>(this, _$identity); + +@override +Map toJson() { + return _$PortForwardConfigToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PortForwardConfig&&(identical(other.id, id) || other.id == id)&&(identical(other.serverId, serverId) || other.serverId == serverId)&&(identical(other.name, name) || other.name == name)&&(identical(other.localHost, localHost) || other.localHost == localHost)&&(identical(other.localPort, localPort) || other.localPort == localPort)&&(identical(other.remoteHost, remoteHost) || other.remoteHost == remoteHost)&&(identical(other.remotePort, remotePort) || other.remotePort == remotePort)&&(identical(other.description, description) || other.description == description)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,serverId,name,localHost,localPort,remoteHost,remotePort,description); + +@override +String toString() { + return 'PortForwardConfig(id: $id, serverId: $serverId, name: $name, localHost: $localHost, localPort: $localPort, remoteHost: $remoteHost, remotePort: $remotePort, description: $description)'; +} + + +} + +/// @nodoc +abstract mixin class _$PortForwardConfigCopyWith<$Res> implements $PortForwardConfigCopyWith<$Res> { + factory _$PortForwardConfigCopyWith(_PortForwardConfig value, $Res Function(_PortForwardConfig) _then) = __$PortForwardConfigCopyWithImpl; +@override @useResult +$Res call({ + String id, String serverId, String name, String localHost, int localPort, String remoteHost, int remotePort, String? description +}); + + + + +} +/// @nodoc +class __$PortForwardConfigCopyWithImpl<$Res> + implements _$PortForwardConfigCopyWith<$Res> { + __$PortForwardConfigCopyWithImpl(this._self, this._then); + + final _PortForwardConfig _self; + final $Res Function(_PortForwardConfig) _then; + +/// Create a copy of PortForwardConfig +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? serverId = null,Object? name = null,Object? localHost = null,Object? localPort = null,Object? remoteHost = null,Object? remotePort = null,Object? description = freezed,}) { + return _then(_PortForwardConfig( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,serverId: null == serverId ? _self.serverId : serverId // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,localHost: null == localHost ? _self.localHost : localHost // ignore: cast_nullable_to_non_nullable +as String,localPort: null == localPort ? _self.localPort : localPort // ignore: cast_nullable_to_non_nullable +as int,remoteHost: null == remoteHost ? _self.remoteHost : remoteHost // ignore: cast_nullable_to_non_nullable +as String,remotePort: null == remotePort ? _self.remotePort : remotePort // ignore: cast_nullable_to_non_nullable +as int,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + + +} + +/// @nodoc +mixin _$PortForwardState { + + String get serverId; List get configs; Map get activeForwards; +/// Create a copy of PortForwardState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$PortForwardStateCopyWith get copyWith => _$PortForwardStateCopyWithImpl(this as PortForwardState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is PortForwardState&&(identical(other.serverId, serverId) || other.serverId == serverId)&&const DeepCollectionEquality().equals(other.configs, configs)&&const DeepCollectionEquality().equals(other.activeForwards, activeForwards)); +} + + +@override +int get hashCode => Object.hash(runtimeType,serverId,const DeepCollectionEquality().hash(configs),const DeepCollectionEquality().hash(activeForwards)); + +@override +String toString() { + return 'PortForwardState(serverId: $serverId, configs: $configs, activeForwards: $activeForwards)'; +} + + +} + +/// @nodoc +abstract mixin class $PortForwardStateCopyWith<$Res> { + factory $PortForwardStateCopyWith(PortForwardState value, $Res Function(PortForwardState) _then) = _$PortForwardStateCopyWithImpl; +@useResult +$Res call({ + String serverId, List configs, Map activeForwards +}); + + + + +} +/// @nodoc +class _$PortForwardStateCopyWithImpl<$Res> + implements $PortForwardStateCopyWith<$Res> { + _$PortForwardStateCopyWithImpl(this._self, this._then); + + final PortForwardState _self; + final $Res Function(PortForwardState) _then; + +/// Create a copy of PortForwardState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? serverId = null,Object? configs = null,Object? activeForwards = null,}) { + return _then(_self.copyWith( +serverId: null == serverId ? _self.serverId : serverId // ignore: cast_nullable_to_non_nullable +as String,configs: null == configs ? _self.configs : configs // ignore: cast_nullable_to_non_nullable +as List,activeForwards: null == activeForwards ? _self.activeForwards : activeForwards // ignore: cast_nullable_to_non_nullable +as Map, + )); +} + +} + + +/// Adds pattern-matching-related methods to [PortForwardState]. +extension PortForwardStatePatterns on PortForwardState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _PortForwardState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _PortForwardState() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _PortForwardState value) $default,){ +final _that = this; +switch (_that) { +case _PortForwardState(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _PortForwardState value)? $default,){ +final _that = this; +switch (_that) { +case _PortForwardState() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String serverId, List configs, Map activeForwards)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _PortForwardState() when $default != null: +return $default(_that.serverId,_that.configs,_that.activeForwards);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String serverId, List configs, Map activeForwards) $default,) {final _that = this; +switch (_that) { +case _PortForwardState(): +return $default(_that.serverId,_that.configs,_that.activeForwards);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String serverId, List configs, Map activeForwards)? $default,) {final _that = this; +switch (_that) { +case _PortForwardState() when $default != null: +return $default(_that.serverId,_that.configs,_that.activeForwards);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _PortForwardState implements PortForwardState { + const _PortForwardState({required this.serverId, final List configs = const [], final Map activeForwards = const {}}): _configs = configs,_activeForwards = activeForwards; + + +@override final String serverId; + final List _configs; +@override@JsonKey() List get configs { + if (_configs is EqualUnmodifiableListView) return _configs; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_configs); +} + + final Map _activeForwards; +@override@JsonKey() Map get activeForwards { + if (_activeForwards is EqualUnmodifiableMapView) return _activeForwards; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_activeForwards); +} + + +/// Create a copy of PortForwardState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$PortForwardStateCopyWith<_PortForwardState> get copyWith => __$PortForwardStateCopyWithImpl<_PortForwardState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PortForwardState&&(identical(other.serverId, serverId) || other.serverId == serverId)&&const DeepCollectionEquality().equals(other._configs, _configs)&&const DeepCollectionEquality().equals(other._activeForwards, _activeForwards)); +} + + +@override +int get hashCode => Object.hash(runtimeType,serverId,const DeepCollectionEquality().hash(_configs),const DeepCollectionEquality().hash(_activeForwards)); + +@override +String toString() { + return 'PortForwardState(serverId: $serverId, configs: $configs, activeForwards: $activeForwards)'; +} + + +} + +/// @nodoc +abstract mixin class _$PortForwardStateCopyWith<$Res> implements $PortForwardStateCopyWith<$Res> { + factory _$PortForwardStateCopyWith(_PortForwardState value, $Res Function(_PortForwardState) _then) = __$PortForwardStateCopyWithImpl; +@override @useResult +$Res call({ + String serverId, List configs, Map activeForwards +}); + + + + +} +/// @nodoc +class __$PortForwardStateCopyWithImpl<$Res> + implements _$PortForwardStateCopyWith<$Res> { + __$PortForwardStateCopyWithImpl(this._self, this._then); + + final _PortForwardState _self; + final $Res Function(_PortForwardState) _then; + +/// Create a copy of PortForwardState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? serverId = null,Object? configs = null,Object? activeForwards = null,}) { + return _then(_PortForwardState( +serverId: null == serverId ? _self.serverId : serverId // ignore: cast_nullable_to_non_nullable +as String,configs: null == configs ? _self._configs : configs // ignore: cast_nullable_to_non_nullable +as List,activeForwards: null == activeForwards ? _self._activeForwards : activeForwards // ignore: cast_nullable_to_non_nullable +as Map, + )); +} + + +} + +// dart format on diff --git a/lib/data/model/server/port_forward.g.dart b/lib/data/model/server/port_forward.g.dart new file mode 100644 index 00000000..fe29c834 --- /dev/null +++ b/lib/data/model/server/port_forward.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'port_forward.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_PortForwardConfig _$PortForwardConfigFromJson(Map json) => + _PortForwardConfig( + id: json['id'] as String, + serverId: json['serverId'] as String, + name: json['name'] as String, + localHost: json['localHost'] as String? ?? 'localhost', + localPort: (json['localPort'] as num).toInt(), + remoteHost: json['remoteHost'] as String, + remotePort: (json['remotePort'] as num).toInt(), + description: json['description'] as String?, + ); + +Map _$PortForwardConfigToJson(_PortForwardConfig instance) => + { + 'id': instance.id, + 'serverId': instance.serverId, + 'name': instance.name, + 'localHost': instance.localHost, + 'localPort': instance.localPort, + 'remoteHost': instance.remoteHost, + 'remotePort': instance.remotePort, + 'description': instance.description, + }; diff --git a/lib/data/provider/port_forward_provider.dart b/lib/data/provider/port_forward_provider.dart new file mode 100644 index 00000000..a658cfbd --- /dev/null +++ b/lib/data/provider/port_forward_provider.dart @@ -0,0 +1,197 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:dartssh2/dartssh2.dart'; +import 'package:fl_lib/fl_lib.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:server_box/data/model/server/port_forward.dart'; +import 'package:server_box/data/provider/server/single.dart'; +import 'package:server_box/data/res/store.dart'; + +part 'port_forward_provider.g.dart'; + +@Riverpod(keepAlive: true) +class PortForwardNotifier extends _$PortForwardNotifier { + final Map _forwards = {}; + final Set _inFlight = {}; + + @override + PortForwardState build(String serverId) { + ref.onDispose(() => dispose()); + ref.listen(serverProvider(serverId), (prev, next) { + if (next.client == null && prev?.client != null) { + for (final entry in _forwards.values) { + entry.close().catchError((_) {}); + } + _forwards.clear(); + state = state.copyWith(activeForwards: {}); + } + }); + final configs = Stores.portForward.fetch(serverId); + return PortForwardState(serverId: serverId, configs: configs); + } + + String get _serverId => state.serverId; + + SSHClient get _client { + final serverState = ref.read(serverProvider(_serverId)); + final client = serverState.client; + if (client == null) { + throw StateError('SSH client is not connected'); + } + return client; + } + + void dispose() { + for (final entry in _forwards.values) { + entry.close().catchError((_) {}); + } + _forwards.clear(); + } + + Future addConfig(PortForwardConfig config) async { + final configWithServerId = config.copyWith(serverId: _serverId); + Stores.portForward.put(configWithServerId); + final configs = [...state.configs, configWithServerId]; + state = state.copyWith(configs: configs); + } + + Future updateConfig(PortForwardConfig oldConfig, PortForwardConfig newConfig) async { + await stopForward(oldConfig.id); + final configWithServerId = newConfig.copyWith(serverId: _serverId); + Stores.portForward.update(oldConfig, configWithServerId); + final configs = state.configs.map((c) => c.id == oldConfig.id ? configWithServerId : c).toList(); + state = state.copyWith(configs: configs); + } + + Future removeConfig(String id) async { + await stopForward(id); + final config = state.configs.firstWhereOrNull((c) => c.id == id); + if (config != null) { + Stores.portForward.delete(config); + } + final configs = state.configs.where((c) => c.id != id).toList(); + final activeForwards = Map.from(state.activeForwards)..remove(id); + state = state.copyWith(configs: configs, activeForwards: activeForwards); + } + + Future startForward(String id) async { + if (!_inFlight.add(id)) return; + try { + final config = state.configs.firstWhereOrNull((c) => c.id == id); + if (config == null) { + Loggers.app.warning('Port forward config not found: $id'); + return; + } + + final existing = _forwards[id]; + if (existing != null) { + await existing.close().catchError((_) {}); + _forwards.remove(id); + } + + try { + final serverSocket = await ServerSocket.bind(config.localHost, config.localPort); + + Loggers.app.info('Port forward started: ${config.localHost}:${config.localPort} -> ${config.remoteHost}:${config.remotePort}'); + + final entry = _LocalForwardEntry(serverSocket: serverSocket); + entry.start(config.remoteHost, config.remotePort, () => _client); + _forwards[id] = entry; + + _updateStatus(id, PortForwardStatus(id: id, isActive: true)); + } catch (e) { + Loggers.app.warning('Port forward failed to start: $e'); + _updateStatus(id, PortForwardStatus(id: id, isActive: false, error: e.toString())); + } + } finally { + _inFlight.remove(id); + } + } + + Future stopForward(String id) async { + if (!_inFlight.add(id)) return; + try { + final entry = _forwards[id]; + if (entry != null) { + await entry.close().catchError((_) {}); + _forwards.remove(id); + Loggers.app.info('Port forward stopped: $id'); + } + _updateStatus(id, PortForwardStatus(id: id, isActive: false)); + } finally { + _inFlight.remove(id); + } + } + + Future toggleForward(String id) async { + final isActive = state.activeForwards[id]?.isActive ?? false; + if (isActive) { + await stopForward(id); + } else { + await startForward(id); + } + } + + void _updateStatus(String id, PortForwardStatus status) { + final activeForwards = Map.from(state.activeForwards); + activeForwards[id] = status; + state = state.copyWith(activeForwards: activeForwards); + } +} + +class _LocalForwardEntry { + final ServerSocket serverSocket; + final List<_ActiveConnection> _connections = []; + StreamSubscription? _subscription; + + _LocalForwardEntry({required this.serverSocket}); + + void start(String remoteHost, int remotePort, SSHClient Function() clientGetter) { + _subscription = serverSocket.listen((socket) async { + try { + final forward = await clientGetter().forwardLocal(remoteHost, remotePort); + final conn = _ActiveConnection(socket: socket, forward: forward); + _connections.add(conn); + final pipe1 = forward.stream.cast>().pipe(socket).catchError((_) {}); + final pipe2 = socket.cast>().pipe(forward.sink).catchError((_) {}); + Future.wait([pipe1, pipe2]).whenComplete(() { + _connections.remove(conn); + conn.close(); + }); + } catch (e, s) { + Loggers.app.warning('Port forward connection failed', e, s); + socket.destroy(); + } + }); + } + + Future close() async { + await _subscription?.cancel(); + await serverSocket.close(); + final connections = _connections.toList(); + for (final conn in connections) { + await conn.close(); + } + _connections.clear(); + } +} + +class _ActiveConnection { + final Socket socket; + final SSHForwardChannel forward; + + _ActiveConnection({ + required this.socket, + required this.forward, + }); + + Future close() async { + try { + socket.destroy(); + } catch (_) {} + try { + await forward.close(); + } catch (_) {} + } +} diff --git a/lib/data/provider/port_forward_provider.g.dart b/lib/data/provider/port_forward_provider.g.dart new file mode 100644 index 00000000..3ce95687 --- /dev/null +++ b/lib/data/provider/port_forward_provider.g.dart @@ -0,0 +1,109 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'port_forward_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(PortForwardNotifier) +const portForwardProvider = PortForwardNotifierFamily._(); + +final class PortForwardNotifierProvider + extends $NotifierProvider { + const PortForwardNotifierProvider._({ + required PortForwardNotifierFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'portForwardProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$portForwardNotifierHash(); + + @override + String toString() { + return r'portForwardProvider' + '' + '($argument)'; + } + + @$internal + @override + PortForwardNotifier create() => PortForwardNotifier(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(PortForwardState value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } + + @override + bool operator ==(Object other) { + return other is PortForwardNotifierProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$portForwardNotifierHash() => + r'e9a93e4e4ee526d334eaaba0e3e0093de7a337fd'; + +final class PortForwardNotifierFamily extends $Family + with + $ClassFamilyOverride< + PortForwardNotifier, + PortForwardState, + PortForwardState, + PortForwardState, + String + > { + const PortForwardNotifierFamily._() + : super( + retry: null, + name: r'portForwardProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: false, + ); + + PortForwardNotifierProvider call(String serverId) => + PortForwardNotifierProvider._(argument: serverId, from: this); + + @override + String toString() => r'portForwardProvider'; +} + +abstract class _$PortForwardNotifier extends $Notifier { + late final _$args = ref.$arg as String; + String get serverId => _$args; + + PortForwardState build(String serverId); + @$mustCallSuper + @override + void runBuild() { + final created = build(_$args); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + PortForwardState, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/lib/data/provider/pve.g.dart b/lib/data/provider/pve.g.dart index 476c92d1..201ee95d 100644 --- a/lib/data/provider/pve.g.dart +++ b/lib/data/provider/pve.g.dart @@ -58,7 +58,7 @@ final class PveNotifierProvider } } -String _$pveNotifierHash() => r'a66699f64eae680064a1904f475d0a241d6cb3f8'; +String _$pveNotifierHash() => r'1f80a27896013a275e5222f19e5ee3c3a68e2f84'; final class PveNotifierFamily extends $Family with $ClassFamilyOverride { diff --git a/lib/data/res/github_id.dart b/lib/data/res/github_id.dart index 5ab5ee4e..5edde434 100644 --- a/lib/data/res/github_id.dart +++ b/lib/data/res/github_id.dart @@ -152,6 +152,8 @@ abstract final class GithubIds { 'kuvaldini', 'aliferne', 'canronglan', + 'nickgirga', + 'xxnuo' }; } diff --git a/lib/data/res/store.dart b/lib/data/res/store.dart index 8954f92a..da2321a1 100644 --- a/lib/data/res/store.dart +++ b/lib/data/res/store.dart @@ -3,6 +3,7 @@ import 'package:get_it/get_it.dart'; import 'package:server_box/data/store/connection_stats.dart'; import 'package:server_box/data/store/container.dart'; import 'package:server_box/data/store/history.dart'; +import 'package:server_box/data/store/port_forward.dart'; import 'package:server_box/data/store/private_key.dart'; import 'package:server_box/data/store/server.dart'; import 'package:server_box/data/store/setting.dart'; @@ -19,6 +20,7 @@ abstract final class Stores { static HistoryStore get history => getIt(); // Keep the legacy box registered so existing connection stats DB files remain intact. static ConnectionStatsStore get connectionStats => getIt(); + static PortForwardStore get portForward => getIt(); /// All stores that need backup static List get _allBackup => [ @@ -29,6 +31,7 @@ abstract final class Stores { snippet, history, connectionStats, + portForward, ]; static Future init() async { @@ -39,6 +42,7 @@ abstract final class Stores { getIt.registerLazySingleton(() => SnippetStore.instance); getIt.registerLazySingleton(() => HistoryStore.instance); getIt.registerLazySingleton(() => ConnectionStatsStore.instance); + getIt.registerLazySingleton(() => PortForwardStore.instance); await Future.wait(_allBackup.map((store) => store.init())); } diff --git a/lib/data/store/port_forward.dart b/lib/data/store/port_forward.dart new file mode 100644 index 00000000..6a0c64dc --- /dev/null +++ b/lib/data/store/port_forward.dart @@ -0,0 +1,67 @@ +import 'package:fl_lib/fl_lib.dart'; +import 'package:server_box/data/model/server/port_forward.dart'; + +class PortForwardStore extends HiveStore { + PortForwardStore._() : super('port_forward'); + + static final instance = PortForwardStore._(); + + void put(PortForwardConfig config) { + set(config.id, config); + } + + List fetch(String serverId) { + final configs = []; + for (final key in keys()) { + final config = get( + key, + fromObj: (val) { + if (val is PortForwardConfig) return val; + if (val is Map) { + final map = val.toStrDynMap; + if (map == null) return null; + try { + final config = PortForwardConfig.fromJson(map as Map); + put(config); + return config; + } catch (e) { + dprint('Parsing PortForwardConfig from JSON', e); + } + } + return null; + }, + ); + if (config != null && config.serverId == serverId) { + configs.add(config); + } + } + return configs; + } + + void delete(PortForwardConfig config) { + remove(config.id); + } + + void deleteByServer(String serverId) { + final keysToDelete = []; + for (final key in keys()) { + final config = get(key); + if (config?.serverId == serverId) { + keysToDelete.add(key); + } + } + for (final key in keysToDelete) { + remove(key); + } + } + + void update(PortForwardConfig old, PortForwardConfig newConfig) { + if (!have(old)) { + throw Exception('Old config: $old not found'); + } + delete(old); + put(newConfig); + } + + bool have(PortForwardConfig config) => get(config.id) != null; +} diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart index 136d462f..95dd8168 100644 --- a/lib/generated/l10n/l10n.dart +++ b/lib/generated/l10n/l10n.dart @@ -1643,6 +1643,48 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Podman Docker emulation detected. Please switch to Podman in settings.'** String get podmanDockerEmulationDetected; + + /// No description provided for @portForwardBeta. + /// + /// In en, this message translates to: + /// **'This feature is still in beta testing. Functionality is not guaranteed.'** + String get portForwardBeta; + + /// No description provided for @portForward_startPrompt. + /// + /// In en, this message translates to: + /// **'Add a port forward rule to get started'** + String get portForward_startPrompt; + + /// No description provided for @portForward_localHost. + /// + /// In en, this message translates to: + /// **'Local Host'** + String get portForward_localHost; + + /// No description provided for @portForward_localPort. + /// + /// In en, this message translates to: + /// **'Local Port'** + String get portForward_localPort; + + /// No description provided for @portForward_remoteHost. + /// + /// In en, this message translates to: + /// **'Remote Host'** + String get portForward_remoteHost; + + /// No description provided for @portForward_remotePort. + /// + /// In en, this message translates to: + /// **'Remote Port'** + String get portForward_remotePort; + + /// No description provided for @portForward_deleteConfirmFmt. + /// + /// In en, this message translates to: + /// **'Delete {name}?'** + String portForward_deleteConfirmFmt(Object name); } class _AppLocalizationsDelegate diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart index 42785d35..dd1e5370 100644 --- a/lib/generated/l10n/l10n_de.dart +++ b/lib/generated/l10n/l10n_de.dart @@ -891,4 +891,29 @@ class AppLocalizationsDe extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Podman Docker-Emulation erkannt. Bitte wechseln Sie in den Einstellungen zu Podman.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart index a2979309..06386246 100644 --- a/lib/generated/l10n/l10n_en.dart +++ b/lib/generated/l10n/l10n_en.dart @@ -882,4 +882,29 @@ class AppLocalizationsEn extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Podman Docker emulation detected. Please switch to Podman in settings.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart index 2bfd80a3..a024193b 100644 --- a/lib/generated/l10n/l10n_es.dart +++ b/lib/generated/l10n/l10n_es.dart @@ -893,4 +893,29 @@ class AppLocalizationsEs extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Detectada emulación de Podman Docker. Por favor, cambie a Podman en la configuración.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart index 2f39be8c..789c4825 100644 --- a/lib/generated/l10n/l10n_fr.dart +++ b/lib/generated/l10n/l10n_fr.dart @@ -896,4 +896,29 @@ class AppLocalizationsFr extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Émulation Podman Docker détectée. Veuillez passer à Podman dans les paramètres.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart index 22bc3978..bfdc42d2 100644 --- a/lib/generated/l10n/l10n_id.dart +++ b/lib/generated/l10n/l10n_id.dart @@ -882,4 +882,29 @@ class AppLocalizationsId extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Emulasi Podman Docker terdeteksi. Silakan beralih ke Podman di pengaturan.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_it.dart b/lib/generated/l10n/l10n_it.dart index e9c10586..192d90b9 100644 --- a/lib/generated/l10n/l10n_it.dart +++ b/lib/generated/l10n/l10n_it.dart @@ -888,4 +888,29 @@ class AppLocalizationsIt extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Rilevata emulazione Docker Podman. Passa a Podman nelle impostazioni.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart index 47a7697d..fd89adc5 100644 --- a/lib/generated/l10n/l10n_ja.dart +++ b/lib/generated/l10n/l10n_ja.dart @@ -852,4 +852,29 @@ class AppLocalizationsJa extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Podman Docker エミュレーションが検出されました。設定で Podman に切り替えてください。'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_ko.dart b/lib/generated/l10n/l10n_ko.dart index 25c5376c..bcfbd1de 100644 --- a/lib/generated/l10n/l10n_ko.dart +++ b/lib/generated/l10n/l10n_ko.dart @@ -851,4 +851,29 @@ class AppLocalizationsKo extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Podman Docker 에뮬레이션이 감지되었습니다. 설정에서 Podman으로 전환해 주세요.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart index 86396c90..d9d0de79 100644 --- a/lib/generated/l10n/l10n_nl.dart +++ b/lib/generated/l10n/l10n_nl.dart @@ -889,4 +889,29 @@ class AppLocalizationsNl extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Podman Docker-emulatie gedetecteerd. Schakel over naar Podman in de instellingen.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart index d2cbc7cc..1ee540f5 100644 --- a/lib/generated/l10n/l10n_pt.dart +++ b/lib/generated/l10n/l10n_pt.dart @@ -884,4 +884,29 @@ class AppLocalizationsPt extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Emulação Podman Docker detectada. Por favor, alterne para Podman nas configurações.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart index cda82788..fdffa64b 100644 --- a/lib/generated/l10n/l10n_ru.dart +++ b/lib/generated/l10n/l10n_ru.dart @@ -888,4 +888,29 @@ class AppLocalizationsRu extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Обнаружена эмуляция Podman Docker. Пожалуйста, переключитесь на Podman в настройках.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart index c8f5f918..7f34487c 100644 --- a/lib/generated/l10n/l10n_tr.dart +++ b/lib/generated/l10n/l10n_tr.dart @@ -883,4 +883,29 @@ class AppLocalizationsTr extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Podman Docker emülasyonu tespit edildi. Lütfen ayarlarda Podman\'a geçin.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart index b53c172e..a4fc2d74 100644 --- a/lib/generated/l10n/l10n_uk.dart +++ b/lib/generated/l10n/l10n_uk.dart @@ -888,4 +888,29 @@ class AppLocalizationsUk extends AppLocalizations { @override String get podmanDockerEmulationDetected => 'Виявлено емуляцію Podman Docker. Будь ласка, переключіться на Podman у налаштуваннях.'; + + @override + String get portForwardBeta => + 'This feature is still in beta testing. Functionality is not guaranteed.'; + + @override + String get portForward_startPrompt => + 'Add a port forward rule to get started'; + + @override + String get portForward_localHost => 'Local Host'; + + @override + String get portForward_localPort => 'Local Port'; + + @override + String get portForward_remoteHost => 'Remote Host'; + + @override + String get portForward_remotePort => 'Remote Port'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return 'Delete $name?'; + } } diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart index e2758aa4..f1959e49 100644 --- a/lib/generated/l10n/l10n_zh.dart +++ b/lib/generated/l10n/l10n_zh.dart @@ -832,6 +832,29 @@ class AppLocalizationsZh extends AppLocalizations { @override String get podmanDockerEmulationDetected => '检测到 Podman Docker 仿真。请在设置中切换到 Podman。'; + + @override + String get portForwardBeta => '此功能仍在测试阶段,不保证功能可用性。'; + + @override + String get portForward_startPrompt => '添加端口映射规则以开始使用'; + + @override + String get portForward_localHost => '本地主机'; + + @override + String get portForward_localPort => '本地端口'; + + @override + String get portForward_remoteHost => '远端主机'; + + @override + String get portForward_remotePort => '远端端口'; + + @override + String portForward_deleteConfirmFmt(Object name) { + return '删除 $name?'; + } } /// The translations for Chinese, as used in Taiwan (`zh_TW`). diff --git a/lib/hive/hive_adapters.dart b/lib/hive/hive_adapters.dart index 63ecce7e..b1053c0d 100644 --- a/lib/hive/hive_adapters.dart +++ b/lib/hive/hive_adapters.dart @@ -2,6 +2,7 @@ import 'package:hive_ce/hive.dart'; import 'package:server_box/data/model/app/menu/server_func.dart'; import 'package:server_box/data/model/app/net_view.dart'; import 'package:server_box/data/model/server/custom.dart'; +import 'package:server_box/data/model/server/port_forward.dart'; import 'package:server_box/data/model/server/private_key_info.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/server/snippet.dart'; @@ -19,5 +20,6 @@ import 'package:server_box/data/model/ssh/virtual_key.dart'; AdapterSpec(), AdapterSpec(), AdapterSpec(), + AdapterSpec(), ]) part 'hive_adapters.g.dart'; diff --git a/lib/hive/hive_adapters.g.dart b/lib/hive/hive_adapters.g.dart index 630a06f0..f3663333 100644 --- a/lib/hive/hive_adapters.g.dart +++ b/lib/hive/hive_adapters.g.dart @@ -436,6 +436,8 @@ class ServerFuncBtnAdapter extends TypeAdapter { return ServerFuncBtn.iperf; case 8: return ServerFuncBtn.systemd; + case 9: + return ServerFuncBtn.portForward; default: return ServerFuncBtn.terminal; } @@ -458,6 +460,8 @@ class ServerFuncBtnAdapter extends TypeAdapter { writer.writeByte(6); case ServerFuncBtn.systemd: writer.writeByte(8); + case ServerFuncBtn.portForward: + writer.writeByte(9); } } @@ -607,3 +611,58 @@ class SystemTypeAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +class PortForwardConfigAdapter extends TypeAdapter { + @override + final typeId = 10; + + @override + PortForwardConfig read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return PortForwardConfig( + id: fields[0] as String, + serverId: fields[7] as String, + name: fields[1] as String, + localHost: fields[2] == null ? 'localhost' : fields[2] as String, + localPort: (fields[3] as num).toInt(), + remoteHost: fields[4] as String, + remotePort: (fields[5] as num).toInt(), + description: fields[6] as String?, + ); + } + + @override + void write(BinaryWriter writer, PortForwardConfig obj) { + writer + ..writeByte(8) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.name) + ..writeByte(2) + ..write(obj.localHost) + ..writeByte(3) + ..write(obj.localPort) + ..writeByte(4) + ..write(obj.remoteHost) + ..writeByte(5) + ..write(obj.remotePort) + ..writeByte(6) + ..write(obj.description) + ..writeByte(7) + ..write(obj.serverId); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is PortForwardConfigAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/hive/hive_adapters.g.yaml b/lib/hive/hive_adapters.g.yaml index f5439221..e4abd0b5 100644 --- a/lib/hive/hive_adapters.g.yaml +++ b/lib/hive/hive_adapters.g.yaml @@ -1,7 +1,7 @@ # Generated by Hive CE # Manual modifications may be necessary for certain migrations # Check in to version control -nextTypeId: 10 +nextTypeId: 11 types: PrivateKeyInfo: typeId: 1 @@ -167,7 +167,7 @@ types: index: 2 ServerFuncBtn: typeId: 6 - nextIndex: 9 + nextIndex: 10 fields: terminal: index: 0 @@ -183,6 +183,8 @@ types: index: 6 systemd: index: 8 + portForward: + index: 9 ServerCustom: typeId: 7 nextIndex: 9 @@ -223,3 +225,23 @@ types: index: 1 windows: index: 2 + PortForwardConfig: + typeId: 10 + nextIndex: 8 + fields: + id: + index: 0 + name: + index: 1 + localHost: + index: 2 + localPort: + index: 3 + remoteHost: + index: 4 + remotePort: + index: 5 + description: + index: 6 + serverId: + index: 7 diff --git a/lib/hive/hive_registrar.g.dart b/lib/hive/hive_registrar.g.dart index b1da7739..79e37bd0 100644 --- a/lib/hive/hive_registrar.g.dart +++ b/lib/hive/hive_registrar.g.dart @@ -13,6 +13,7 @@ extension HiveRegistrar on HiveInterface { registerAdapter(ConnectionResultAdapter()); registerAdapter(ConnectionStatAdapter()); registerAdapter(NetViewTypeAdapter()); + registerAdapter(PortForwardConfigAdapter()); registerAdapter(PrivateKeyInfoAdapter()); registerAdapter(ServerConnectionStatsAdapter()); registerAdapter(ServerCustomAdapter()); @@ -31,6 +32,7 @@ extension IsolatedHiveRegistrar on IsolatedHiveInterface { registerAdapter(ConnectionResultAdapter()); registerAdapter(ConnectionStatAdapter()); registerAdapter(NetViewTypeAdapter()); + registerAdapter(PortForwardConfigAdapter()); registerAdapter(PrivateKeyInfoAdapter()); registerAdapter(ServerConnectionStatsAdapter()); registerAdapter(ServerCustomAdapter()); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e58c8ba0..5a8e5a4c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -255,5 +255,12 @@ "writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist.", "writeScriptTip": "After connecting to the server, a script will be written to `~/.config/server_box` \n | `/tmp/server_box` to monitor the system status. You can review the script content.", "menuGitHubRepository": "GitHub Repository", - "podmanDockerEmulationDetected": "Podman Docker emulation detected. Please switch to Podman in settings." + "podmanDockerEmulationDetected": "Podman Docker emulation detected. Please switch to Podman in settings.", + "portForwardBeta": "This feature is still in beta testing. Functionality is not guaranteed.", + "portForward_startPrompt": "Add a port forward rule to get started", + "portForward_localHost": "Local Host", + "portForward_localPort": "Local Port", + "portForward_remoteHost": "Remote Host", + "portForward_remotePort": "Remote Port", + "portForward_deleteConfirmFmt": "Delete {name}?" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 608d9465..ad8705d6 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -252,5 +252,12 @@ "writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等", "writeScriptTip": "在连接服务器后,会向 `~/.config/server_box` \n | `/tmp/server_box` 写入脚本来监测系统状态,你可以审查脚本内容。", "menuGitHubRepository": "GitHub 仓库", - "podmanDockerEmulationDetected": "检测到 Podman Docker 仿真。请在设置中切换到 Podman。" + "podmanDockerEmulationDetected": "检测到 Podman Docker 仿真。请在设置中切换到 Podman。", + "portForwardBeta": "此功能仍在测试阶段,不保证功能可用性。", + "portForward_startPrompt": "添加端口映射规则以开始使用", + "portForward_localHost": "本地主机", + "portForward_localPort": "本地端口", + "portForward_remoteHost": "远端主机", + "portForward_remotePort": "远端端口", + "portForward_deleteConfirmFmt": "删除 {name}?" } diff --git a/lib/view/page/port_forward.dart b/lib/view/page/port_forward.dart new file mode 100644 index 00000000..046a8719 --- /dev/null +++ b/lib/view/page/port_forward.dart @@ -0,0 +1,321 @@ +import 'package:fl_lib/fl_lib.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:server_box/core/extension/context/locale.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/server/port_forward.dart'; +import 'package:server_box/data/provider/port_forward_provider.dart'; + +final class PortForwardPage extends ConsumerStatefulWidget { + final SpiRequiredArgs args; + + const PortForwardPage({super.key, required this.args}); + + static const route = AppRouteArg(page: PortForwardPage.new, path: '/port_forward'); + + @override + ConsumerState createState() => _PortForwardPageState(); +} + +final class _PortForwardPageState extends ConsumerState { + late final PortForwardNotifier _notifier; + + @override + void initState() { + super.initState(); + _notifier = ref.read(portForwardProvider(widget.args.spi.id).notifier); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + _showBetaWarning(); + }); + } + + void _showBetaWarning() { + context.showRoundDialog( + title: libL10n.attention, + child: Text(context.l10n.portForwardBeta), + actions: [Btnx.ok], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: Text(libL10n.portForward), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: _onAdd, + ), + ], + ), + body: _buildBody(), + ); + } + + Widget _buildBody() { + final state = ref.watch(portForwardProvider(widget.args.spi.id)); + final configs = state.configs; + + if (configs.isEmpty) { + return _buildEmpty(); + } + + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: configs.length, + itemBuilder: (context, index) { + final config = configs[index]; + final status = state.activeForwards[config.id]; + return _buildConfigTile(config, status); + }, + ); + } + + Widget _buildEmpty() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.compare_arrows, size: 64, color: Colors.grey), + const SizedBox(height: 16), + Text(libL10n.empty, style: UIs.textGrey), + const SizedBox(height: 8), + Text(context.l10n.portForward_startPrompt, style: UIs.text13Grey), + ], + ), + ); + } + + Widget _buildConfigTile(PortForwardConfig config, PortForwardStatus? status) { + final isActive = status?.isActive ?? false; + final hasError = status?.error != null; + final colorScheme = Theme.of(context).colorScheme; + + return ListTile( + leading: Icon( + isActive ? Icons.link : Icons.link_off, + color: isActive ? colorScheme.primary : (hasError ? colorScheme.error : colorScheme.onSurfaceVariant), + ), + title: Text(config.name), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(config.displayAddr, style: UIs.text13Grey), + if (hasError) Text(status!.error!, style: TextStyle(color: colorScheme.error, fontSize: 12)), + ], + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Switch( + value: isActive, + onChanged: (_) => _notifier.toggleForward(config.id), + ), + PopupMenu( + items: [ + PopupMenuItem( + value: 'edit', + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.edit, size: 18), + const SizedBox(width: 8), + Text(libL10n.edit), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.delete, size: 18), + const SizedBox(width: 8), + Text(libL10n.delete), + ], + ), + ), + ], + onSelected: (val) { + if (val == 'edit') { + _onEdit(config); + } else if (val == 'delete') { + _onDelete(config); + } + }, + ), + ], + ), + isThreeLine: hasError, + ).cardx.paddingSymmetric(horizontal: 13, vertical: 4); + } + + void _onAdd() { + _showConfigDialog(null); + } + + void _onEdit(PortForwardConfig config) { + _showConfigDialog(config); + } + + void _onDelete(PortForwardConfig config) async { + final sure = await context.showRoundDialog( + title: libL10n.attention, + child: Text(context.l10n.portForward_deleteConfirmFmt(config.name)), + actions: Btnx.cancelOk, + ); + if (sure == true) { + await _notifier.removeConfig(config.id); + } + } + + void _showConfigDialog(PortForwardConfig? existing) { + showDialog( + context: context, + builder: (ctx) => _PortForwardConfigDialog( + existing: existing, + serverId: widget.args.spi.id, + onSave: (config) async { + if (existing == null) { + await _notifier.addConfig(config); + } else { + final wasActive = ref.read(portForwardProvider(widget.args.spi.id)).activeForwards[existing.id]?.isActive ?? false; + await _notifier.updateConfig(existing, config); + if (wasActive) { + await _notifier.startForward(config.id); + } + } + }, + ), + ); + } +} + +class _PortForwardConfigDialog extends StatefulWidget { + final PortForwardConfig? existing; + final String serverId; + final Future Function(PortForwardConfig config) onSave; + + const _PortForwardConfigDialog({ + required this.existing, + required this.serverId, + required this.onSave, + }); + + @override + State<_PortForwardConfigDialog> createState() => _PortForwardConfigDialogState(); +} + +class _PortForwardConfigDialogState extends State<_PortForwardConfigDialog> { + late final TextEditingController nameController; + late final TextEditingController localHostController; + late final TextEditingController localPortController; + late final TextEditingController remoteHostController; + late final TextEditingController remotePortController; + late final TextEditingController descController; + bool _saving = false; + + @override + void initState() { + super.initState(); + nameController = TextEditingController(text: widget.existing?.name ?? ''); + localHostController = TextEditingController(text: widget.existing?.localHost ?? 'localhost'); + localPortController = TextEditingController(text: widget.existing?.localPort.toString() ?? ''); + remoteHostController = TextEditingController(text: widget.existing?.remoteHost ?? ''); + remotePortController = TextEditingController(text: widget.existing?.remotePort.toString() ?? ''); + descController = TextEditingController(text: widget.existing?.description ?? ''); + } + + @override + void dispose() { + nameController.dispose(); + localHostController.dispose(); + localPortController.dispose(); + remoteHostController.dispose(); + remotePortController.dispose(); + descController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.existing == null ? libL10n.add : libL10n.edit), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Input(controller: nameController, hint: libL10n.name), + const SizedBox(height: 8), + Row( + children: [ + Expanded(child: Input(controller: localHostController, hint: context.l10n.portForward_localHost)), + const SizedBox(width: 8), + Expanded(child: Input(controller: localPortController, hint: context.l10n.portForward_localPort, type: TextInputType.number)), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded(child: Input(controller: remoteHostController, hint: context.l10n.portForward_remoteHost)), + const SizedBox(width: 8), + Expanded(child: Input(controller: remotePortController, hint: context.l10n.portForward_remotePort, type: TextInputType.number)), + ], + ), + const SizedBox(height: 8), + Input(controller: descController, hint: libL10n.note), + ], + ), + ), + actions: [ + Btn.cancel(), + Btn.ok( + onTap: () async { + if (_saving) return; + setState(() => _saving = true); + try { + final name = nameController.text.trim(); + final localHost = localHostController.text.trim(); + final localPort = int.tryParse(localPortController.text.trim()) ?? 0; + final remoteHost = remoteHostController.text.trim(); + final remotePort = int.tryParse(remotePortController.text.trim()) ?? 0; + final desc = descController.text.trim(); + + if (name.isEmpty || + localHost.isEmpty || + localPort <= 0 || + localPort > 65535 || + remoteHost.isEmpty || + remotePort <= 0 || + remotePort > 65535) { + if (mounted) context.showSnackBar(libL10n.invalid); + return; + } + + final config = PortForwardConfig( + id: widget.existing?.id ?? ShortId.generate(), + serverId: widget.serverId, + name: name, + localHost: localHost, + localPort: localPort, + remoteHost: remoteHost, + remotePort: remotePort, + description: desc.isEmpty ? null : desc, + ); + + await widget.onSave(config); + if (mounted) Navigator.of(context).pop(); + } catch (e, s) { + Loggers.app.warning('Failed to save port forward config', e, s); + if (mounted) context.showSnackBar(libL10n.error); + } finally { + if (mounted) setState(() => _saving = false); + } + }, + ), + ], + ); + } +} diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index d84158e1..3b33a071 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -15,6 +15,7 @@ import 'package:server_box/data/provider/snippet.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/page/container/container.dart'; import 'package:server_box/view/page/iperf.dart'; +import 'package:server_box/view/page/port_forward.dart'; import 'package:server_box/view/page/process.dart'; import 'package:server_box/view/page/ssh/page/page.dart'; import 'package:server_box/view/page/storage/sftp.dart'; @@ -206,6 +207,11 @@ void _onTapMoreBtns(ServerFuncBtn value, Spi spi, BuildContext context, WidgetRe // ); // } break; + case ServerFuncBtn.portForward: + if (!_checkClient(context, spi.id, ref)) return; + final args = SpiRequiredArgs(spi); + PortForwardPage.route.go(context, args); + break; } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 820c3a7e..adc190d4 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index d3790845..32f9bfe9 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color flutter_secure_storage_linux + gtk screen_retriever_linux url_launcher_linux window_manager diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 6abf80cb..1058d345 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import app_links import dynamic_color import file_picker import flutter_secure_storage_macos @@ -19,6 +20,7 @@ import wakelock_plus import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) diff --git a/packages/fl_lib b/packages/fl_lib index 61d62d23..bbadd10e 160000 --- a/packages/fl_lib +++ b/packages/fl_lib @@ -1 +1 @@ -Subproject commit 61d62d23a885d8dc1b36bae32d7c4572125e1f92 +Subproject commit bbadd10e1250bd89ba753136726f7da25854618a diff --git a/pubspec.lock b/pubspec.lock index 113284d3..8cecceb7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "91.0.0" analyzer: @@ -14,7 +14,7 @@ packages: description: name: analyzer sha256: a40a0cee526a7e1f387c6847bd8a5ccbf510a75952ef8a28338e989558072cb0 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "8.4.0" analyzer_buffer: @@ -22,7 +22,7 @@ packages: description: name: analyzer_buffer sha256: aba2f75e63b3135fd1efaa8b6abefe1aa6e41b6bd9806221620fa48f98156033 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.1.11" analyzer_plugin: @@ -30,7 +30,7 @@ packages: description: name: analyzer_plugin sha256: "08cfefa90b4f4dd3b447bda831cecf644029f9f8e22820f6ee310213ebe2dd53" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.13.10" animations: @@ -38,7 +38,7 @@ packages: description: name: animations sha256: "18938cefd7dcc04e1ecac0db78973761a01e4bc2d6bfae0cfa596bfeac9e96ab" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" ansicolor: @@ -46,15 +46,47 @@ packages: description: name: ansicolor sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.3" + app_links: + dependency: transitive + description: + name: app_links + sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.4.1" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" archive: dependency: transitive description: name: archive sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.9" args: @@ -62,7 +94,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.7.0" asn1lib: @@ -70,7 +102,7 @@ packages: description: name: asn1lib sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.6.5" async: @@ -78,7 +110,7 @@ packages: description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.13.0" boolean_selector: @@ -86,7 +118,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" build: @@ -94,7 +126,7 @@ packages: description: name: build sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.5" build_config: @@ -102,7 +134,7 @@ packages: description: name: build_config sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.0" build_daemon: @@ -110,7 +142,7 @@ packages: description: name: build_daemon sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.1" build_runner: @@ -118,7 +150,7 @@ packages: description: name: build_runner sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.13.1" built_collection: @@ -126,7 +158,7 @@ packages: description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.1" built_value: @@ -134,7 +166,7 @@ packages: description: name: built_value sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "8.12.4" camera: @@ -142,7 +174,7 @@ packages: description: name: camera sha256: "4142a19a38e388d3bab444227636610ba88982e36dff4552d5191a86f65dc437" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.11.4" camera_android_camerax: @@ -150,7 +182,7 @@ packages: description: name: camera_android_camerax sha256: "8516fe308bc341a5067fb1a48edff0ddfa57c0d3cdcc9dbe7ceca3ba119e2577" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.6.30" camera_avfoundation: @@ -158,7 +190,7 @@ packages: description: name: camera_avfoundation sha256: "11b4aee2f5e5e038982e152b4a342c749b414aa27857899d20f4323e94cb5f0b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.23+2" camera_platform_interface: @@ -166,7 +198,7 @@ packages: description: name: camera_platform_interface sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.12.0" camera_web: @@ -174,7 +206,7 @@ packages: description: name: camera_web sha256: "57f49a635c8bf249d07fb95eb693d7e4dda6796dedb3777f9127fb54847beba7" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.3.5+3" characters: @@ -182,7 +214,7 @@ packages: description: name: characters sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" charset: @@ -190,7 +222,7 @@ packages: description: name: charset sha256: "27802032a581e01ac565904ece8c8962564b1070690794f0072f6865958ce8b9" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" checked_yaml: @@ -198,7 +230,7 @@ packages: description: name: checked_yaml sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.4" choice: @@ -206,7 +238,7 @@ packages: description: name: choice sha256: "52d07065e8056beba5b26cff7786134cbfa24927b1f5bf60a05d50058597b2d9" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.2" circle_chart: @@ -221,7 +253,7 @@ packages: description: name: cli_config sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" clock: @@ -229,7 +261,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" code_assets: @@ -237,7 +269,7 @@ packages: description: name: code_assets sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" code_builder: @@ -245,7 +277,7 @@ packages: description: name: code_builder sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.11.1" collection: @@ -253,7 +285,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" computer: @@ -270,7 +302,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" coverage: @@ -278,7 +310,7 @@ packages: description: name: coverage sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.15.0" cross_file: @@ -286,7 +318,7 @@ packages: description: name: cross_file sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.3.5+2" crypto: @@ -294,7 +326,7 @@ packages: description: name: crypto sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.7" csslib: @@ -302,7 +334,7 @@ packages: description: name: csslib sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" custom_lint_core: @@ -310,7 +342,7 @@ packages: description: name: custom_lint_core sha256: "85b339346154d5646952d44d682965dfe9e12cae5febd706f0db3aa5010d6423" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.8.1" custom_lint_visitor: @@ -318,7 +350,7 @@ packages: description: name: custom_lint_visitor sha256: "91f2a81e9f0abb4b9f3bb529f78b6227ce6050300d1ae5b1e2c69c66c7a566d8" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0+8.4.0" dart_style: @@ -326,7 +358,7 @@ packages: description: name: dart_style sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.3" dartssh2: @@ -341,7 +373,7 @@ packages: description: name: dbus sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.12" dio: @@ -349,7 +381,7 @@ packages: description: name: dio sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.9.2" dio_web_adapter: @@ -357,7 +389,7 @@ packages: description: name: dio_web_adapter sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" dynamic_color: @@ -365,7 +397,7 @@ packages: description: name: dynamic_color sha256: "43a5a6679649a7731ab860334a5812f2067c2d9ce6452cf069c5e0c25336c17c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.8.1" easy_isolate: @@ -373,7 +405,7 @@ packages: description: name: easy_isolate sha256: "5c1dd21d77af0ac82e0ce25ddda652a17b87cee56fb4d8a708e13be0a2adc180" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.1" equatable: @@ -381,7 +413,7 @@ packages: description: name: equatable sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.8" extended_image: @@ -389,7 +421,7 @@ packages: description: name: extended_image sha256: f6cbb1d798f51262ed1a3d93b4f1f2aa0d76128df39af18ecb77fa740f88b2e0 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "10.0.1" extended_image_library: @@ -397,7 +429,7 @@ packages: description: name: extended_image_library sha256: "1f9a24d3a00c2633891c6a7b5cab2807999eb2d5b597e5133b63f49d113811fe" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.0.1" fake_async: @@ -405,7 +437,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" ffi: @@ -413,7 +445,7 @@ packages: description: name: ffi sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" file: @@ -421,7 +453,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" file_picker: @@ -429,7 +461,7 @@ packages: description: name: file_picker sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "10.3.10" fixnum: @@ -437,7 +469,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" fl_build: @@ -452,7 +484,7 @@ packages: description: name: fl_chart sha256: b938f77d042cbcd822936a7a359a7235bad8bd72070de1f827efc2cc297ac888 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" fl_lib: @@ -472,7 +504,7 @@ packages: description: name: flutter_displaymode sha256: ecd44b1e902b0073b42ff5b55bf283f38e088270724cdbb7f7065ccf54aa60a8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.0" flutter_gbk2utf8: @@ -480,7 +512,7 @@ packages: description: name: flutter_gbk2utf8 sha256: c17323808d6ae7cfaf7676669e0130c33df6be322eb807cdd32face5824c1134 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" flutter_highlight: @@ -488,7 +520,7 @@ packages: description: name: flutter_highlight sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.0" flutter_lints: @@ -496,7 +528,7 @@ packages: description: name: flutter_lints sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.0.0" flutter_localizations: @@ -509,7 +541,7 @@ packages: description: name: flutter_markdown_plus sha256: "039177906850278e8fb1cd364115ee0a46281135932fa8ecea8455522166d2de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.7" flutter_markdown_plus_latex: @@ -517,7 +549,7 @@ packages: description: name: flutter_markdown_plus_latex sha256: "2e7698b291f0657ca445efab730bb25a8c5851037e882cb7bf47d16a5c218de7" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" flutter_math_fork: @@ -525,7 +557,7 @@ packages: description: name: flutter_math_fork sha256: "6d5f2f1aa57ae539ffb0a04bb39d2da67af74601d685a161aff7ce5bda5fa407" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.4" flutter_native_splash: @@ -533,7 +565,7 @@ packages: description: name: flutter_native_splash sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.7" flutter_plugin_android_lifecycle: @@ -541,7 +573,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.33" flutter_riverpod: @@ -549,7 +581,7 @@ packages: description: name: flutter_riverpod sha256: "9e2d6907f12cc7d23a846847615941bddee8709bf2bfd274acdf5e80bcf22fde" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" flutter_secure_storage: @@ -557,7 +589,7 @@ packages: description: name: flutter_secure_storage sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.2.4" flutter_secure_storage_linux: @@ -565,7 +597,7 @@ packages: description: name: flutter_secure_storage_linux sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.3" flutter_secure_storage_macos: @@ -573,7 +605,7 @@ packages: description: name: flutter_secure_storage_macos sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.3" flutter_secure_storage_platform_interface: @@ -581,7 +613,7 @@ packages: description: name: flutter_secure_storage_platform_interface sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" flutter_secure_storage_web: @@ -589,7 +621,7 @@ packages: description: name: flutter_secure_storage_web sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" flutter_secure_storage_windows: @@ -597,7 +629,7 @@ packages: description: name: flutter_secure_storage_windows sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" flutter_staggered_grid_view: @@ -605,7 +637,7 @@ packages: description: name: flutter_staggered_grid_view sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.0" flutter_svg: @@ -613,7 +645,7 @@ packages: description: name: flutter_svg sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.4" flutter_test: @@ -631,7 +663,7 @@ packages: description: name: freezed sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.3" freezed_annotation: @@ -639,7 +671,7 @@ packages: description: name: freezed_annotation sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" frontend_server_client: @@ -647,7 +679,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" get_it: @@ -655,7 +687,7 @@ packages: description: name: get_it sha256: "568d62f0e68666fb5d95519743b3c24a34c7f19d834b0658c46e26d778461f66" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.2.1" glob: @@ -663,7 +695,7 @@ packages: description: name: glob sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.3" graphs: @@ -671,15 +703,23 @@ packages: description: name: graphs sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.2" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" highlight: dependency: "direct main" description: name: highlight sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.0" hive_ce: @@ -687,7 +727,7 @@ packages: description: name: hive_ce sha256: "8e9980e68643afb1e765d3af32b47996552a64e190d03faf622cea07c1294418" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.19.3" hive_ce_flutter: @@ -695,7 +735,7 @@ packages: description: name: hive_ce_flutter sha256: "2677e95a333ff15af43ccd06af7eb7abbf1a4f154ea071997f3de4346cae913a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.4" hive_ce_generator: @@ -703,7 +743,7 @@ packages: description: name: hive_ce_generator sha256: b19ac263cb37529513508ba47352c41e6de72ba879952898d9c18c9c8a955921 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.0" hooks: @@ -711,7 +751,7 @@ packages: description: name: hooks sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" html: @@ -719,7 +759,7 @@ packages: description: name: html sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.15.6" http: @@ -727,7 +767,7 @@ packages: description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.6.0" http_client_helper: @@ -735,7 +775,7 @@ packages: description: name: http_client_helper sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" http_multi_server: @@ -743,7 +783,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.2" http_parser: @@ -751,7 +791,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" icloud_storage: @@ -759,7 +799,7 @@ packages: description: name: icloud_storage sha256: fa91d9c3b4264651f01a4f5b99cffa354ffe455623b13ecf92be86d88b1e26ea - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" icons_plus: @@ -767,7 +807,7 @@ packages: description: name: icons_plus sha256: "8e2f601b8605d45dd55b106a0da084a1809125077a49574ca22e8bcd5b6e86f0" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.0.0" image: @@ -775,7 +815,7 @@ packages: description: name: image sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.8.0" intl: @@ -783,7 +823,7 @@ packages: description: name: intl sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.20.2" io: @@ -791,7 +831,7 @@ packages: description: name: io sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" isolate_channel: @@ -799,7 +839,7 @@ packages: description: name: isolate_channel sha256: a9d3d620695bc984244dafae00b95e4319d6974b2d77f4b9e1eb4f2efe099094 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.6.1" isolate_contactor: @@ -807,7 +847,7 @@ packages: description: name: isolate_contactor sha256: "6ba8434ceb58238a1389d6365111a3efe7baa1c68a66f4db6d63d351cf6c3a0f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.0" isolate_manager: @@ -815,7 +855,7 @@ packages: description: name: isolate_manager sha256: "22ed0c25f80ec3b5f21e3a55d060f4650afff33f27c2dff34c0f9409d5759ae5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.5+1" js: @@ -823,7 +863,7 @@ packages: description: name: js sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.6.7" json_annotation: @@ -831,7 +871,7 @@ packages: description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.0" json_serializable: @@ -839,7 +879,7 @@ packages: description: name: json_serializable sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.11.2" leak_tracker: @@ -847,7 +887,7 @@ packages: description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -855,7 +895,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.10" leak_tracker_testing: @@ -863,7 +903,7 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" lints: @@ -871,7 +911,7 @@ packages: description: name: lints sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.0" local_auth: @@ -879,7 +919,7 @@ packages: description: name: local_auth sha256: ae6f382f638108c6becd134318d7c3f0a93875383a54010f61d7c97ac05d5137 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.1" local_auth_android: @@ -887,7 +927,7 @@ packages: description: name: local_auth_android sha256: b41970749c2d43791790724b76917eeee1e90de76e6b0eec3edca03a329bf44c - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.7" local_auth_darwin: @@ -895,7 +935,7 @@ packages: description: name: local_auth_darwin sha256: a8c3d4e17454111f7fd31ff72a31222359f6059f7fe956c2dcfe0f88f49826d4 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.3" local_auth_platform_interface: @@ -903,7 +943,7 @@ packages: description: name: local_auth_platform_interface sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" local_auth_windows: @@ -911,7 +951,7 @@ packages: description: name: local_auth_windows sha256: be12c5b8ba5e64896983123655c5f67d2484ecfcc95e367952ad6e3bff94cb16 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" locale_names: @@ -919,7 +959,7 @@ packages: description: name: locale_names sha256: "7a89ca54072f4f13d0f5df5a9ba69337554bf2fd057d1dd2a238898f3f159374" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" logging: @@ -927,7 +967,7 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.0" markdown: @@ -935,23 +975,23 @@ packages: description: name: markdown sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.3.1" matcher: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.dev" + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.19" + version: "0.12.18" material_color_utilities: dependency: transitive description: name: material_color_utilities sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.13.0" meta: @@ -959,7 +999,7 @@ packages: description: name: meta sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.17.0" mime: @@ -967,7 +1007,7 @@ packages: description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.0" mockito: @@ -975,7 +1015,7 @@ packages: description: name: mockito sha256: a45d1aa065b796922db7b9e7e7e45f921aed17adf3a8318a1f47097e7e695566 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.6.3" multi_split_view: @@ -983,7 +1023,7 @@ packages: description: name: multi_split_view sha256: "06f5126a65d3010ce0a9d5c003e793041fe99377b23e3534bb05059f79a580e9" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.6.1" native_toolchain_c: @@ -991,7 +1031,7 @@ packages: description: name: native_toolchain_c sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.17.6" nested: @@ -999,7 +1039,7 @@ packages: description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" node_preamble: @@ -1007,7 +1047,7 @@ packages: description: name: node_preamble sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.2" objective_c: @@ -1015,7 +1055,7 @@ packages: description: name: objective_c sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.3.0" package_config: @@ -1023,7 +1063,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" package_info_plus: @@ -1031,7 +1071,7 @@ packages: description: name: package_info_plus sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.0.0" package_info_plus_platform_interface: @@ -1039,7 +1079,7 @@ packages: description: name: package_info_plus_platform_interface sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.1" path: @@ -1047,7 +1087,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" path_parsing: @@ -1055,7 +1095,7 @@ packages: description: name: path_parsing sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" path_provider: @@ -1063,7 +1103,7 @@ packages: description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.5" path_provider_android: @@ -1071,7 +1111,7 @@ packages: description: name: path_provider_android sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.22" path_provider_foundation: @@ -1079,7 +1119,7 @@ packages: description: name: path_provider_foundation sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.6.0" path_provider_linux: @@ -1087,7 +1127,7 @@ packages: description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -1095,7 +1135,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" path_provider_windows: @@ -1103,7 +1143,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.0" petitparser: @@ -1111,7 +1151,7 @@ packages: description: name: petitparser sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.2" pinenacl: @@ -1119,7 +1159,7 @@ packages: description: name: pinenacl sha256: "57e907beaacbc3c024a098910b6240758e899674de07d6949a67b52fd984cbdf" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.6.0" plain_notification_token: @@ -1134,7 +1174,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.6" plugin_platform_interface: @@ -1142,7 +1182,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" pointycastle: @@ -1150,7 +1190,7 @@ packages: description: name: pointycastle sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" pool: @@ -1158,7 +1198,7 @@ packages: description: name: pool sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.2" posix: @@ -1166,7 +1206,7 @@ packages: description: name: posix sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.5.0" pretty_qr_code: @@ -1174,7 +1214,7 @@ packages: description: name: pretty_qr_code sha256: "474f8a4512113fba06f14a6ec9bbf42353b4e651d7a520e3096f2a9b6bbe7a8a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.6.0" provider: @@ -1182,7 +1222,7 @@ packages: description: name: provider sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.5+1" pub_semver: @@ -1190,7 +1230,7 @@ packages: description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" pubspec_parse: @@ -1198,7 +1238,7 @@ packages: description: name: pubspec_parse sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" qr: @@ -1206,7 +1246,7 @@ packages: description: name: qr sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" qr_code_dart_decoder: @@ -1214,7 +1254,7 @@ packages: description: name: qr_code_dart_decoder sha256: "4044f13a071da6102f7e9bc44a6b1ce577604d7846bcbeb1be412a137b825017" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.1.2" qr_code_dart_scan: @@ -1222,7 +1262,7 @@ packages: description: name: qr_code_dart_scan sha256: "81443d940f8f27baaa4b9aeaa8d3d2155ad2c0b9842a9bacb03dab85c111e2f6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.11.5" quiver: @@ -1230,7 +1270,7 @@ packages: description: name: quiver sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.2" re_editor: @@ -1238,7 +1278,7 @@ packages: description: name: re_editor sha256: dd4e6ca7350a8fa0cda4e425b82a0c3c010f0f6b3f618c74223e05b8129ab629 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.8.0" re_highlight: @@ -1246,7 +1286,7 @@ packages: description: name: re_highlight sha256: "6c4ac3f76f939fb7ca9df013df98526634e17d8f7460e028bd23a035870024f2" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.0.3" responsive_framework: @@ -1254,7 +1294,7 @@ packages: description: name: responsive_framework sha256: a8e1c13d4ba980c60cbf6fa1e9907cd60662bf2585184d7c96ca46c43de91552 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.1" riverpod: @@ -1262,7 +1302,7 @@ packages: description: name: riverpod sha256: c406de02bff19d920b832bddfb8283548bfa05ce41c59afba57ce643e116aa59 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" riverpod_analyzer_utils: @@ -1270,7 +1310,7 @@ packages: description: name: riverpod_analyzer_utils sha256: a0f68adb078b790faa3c655110a017f9a7b7b079a57bbd40f540e80dce5fcd29 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0-dev.7" riverpod_annotation: @@ -1278,7 +1318,7 @@ packages: description: name: riverpod_annotation sha256: "7230014155777fc31ba3351bc2cb5a3b5717b11bfafe52b1553cb47d385f8897" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" riverpod_generator: @@ -1286,7 +1326,7 @@ packages: description: name: riverpod_generator sha256: "49894543a42cf7a9954fc4e7366b6d3cb2e6ec0fa07775f660afcdd92d097702" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" screen_retriever: @@ -1294,7 +1334,7 @@ packages: description: name: screen_retriever sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" screen_retriever_linux: @@ -1302,7 +1342,7 @@ packages: description: name: screen_retriever_linux sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" screen_retriever_macos: @@ -1310,7 +1350,7 @@ packages: description: name: screen_retriever_macos sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" screen_retriever_platform_interface: @@ -1318,7 +1358,7 @@ packages: description: name: screen_retriever_platform_interface sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" screen_retriever_windows: @@ -1326,7 +1366,7 @@ packages: description: name: screen_retriever_windows sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" screenshot: @@ -1334,7 +1374,7 @@ packages: description: name: screenshot sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" share_plus: @@ -1342,7 +1382,7 @@ packages: description: name: share_plus sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "12.0.1" share_plus_platform_interface: @@ -1350,7 +1390,7 @@ packages: description: name: share_plus_platform_interface sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.0" shared_preferences: @@ -1358,7 +1398,7 @@ packages: description: name: shared_preferences sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.5.4" shared_preferences_android: @@ -1366,7 +1406,7 @@ packages: description: name: shared_preferences_android sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.21" shared_preferences_foundation: @@ -1374,7 +1414,7 @@ packages: description: name: shared_preferences_foundation sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.5.6" shared_preferences_linux: @@ -1382,7 +1422,7 @@ packages: description: name: shared_preferences_linux sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.1" shared_preferences_platform_interface: @@ -1390,7 +1430,7 @@ packages: description: name: shared_preferences_platform_interface sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.1" shared_preferences_web: @@ -1398,7 +1438,7 @@ packages: description: name: shared_preferences_web sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.3" shared_preferences_windows: @@ -1406,7 +1446,7 @@ packages: description: name: shared_preferences_windows sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.1" shelf: @@ -1414,7 +1454,7 @@ packages: description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.2" shelf_packages_handler: @@ -1422,7 +1462,7 @@ packages: description: name: shelf_packages_handler sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" shelf_static: @@ -1430,7 +1470,7 @@ packages: description: name: shelf_static sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.3" shelf_web_socket: @@ -1438,7 +1478,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" sky_engine: @@ -1451,7 +1491,7 @@ packages: description: name: source_gen sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.2.2" source_helper: @@ -1459,7 +1499,7 @@ packages: description: name: source_helper sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.8" source_map_stack_trace: @@ -1467,7 +1507,7 @@ packages: description: name: source_map_stack_trace sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" source_maps: @@ -1475,7 +1515,7 @@ packages: description: name: source_maps sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.10.13" source_span: @@ -1483,7 +1523,7 @@ packages: description: name: source_span sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.2" stack_trace: @@ -1491,7 +1531,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.12.1" state_notifier: @@ -1499,7 +1539,7 @@ packages: description: name: state_notifier sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" stream_channel: @@ -1507,7 +1547,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" stream_transform: @@ -1515,7 +1555,7 @@ packages: description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" string_scanner: @@ -1523,7 +1563,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" term_glyph: @@ -1531,39 +1571,39 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.2" test: dependency: "direct dev" description: name: test - sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" - url: "https://pub.dev" + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.30.0" + version: "1.29.0" test_api: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" - url: "https://pub.dev" + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.10" + version: "0.7.9" test_core: dependency: transitive description: name: test_core - sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" - url: "https://pub.dev" + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.16" + version: "0.6.15" tuple: dependency: transitive description: name: tuple sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.2" typed_data: @@ -1571,7 +1611,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" universal_io: @@ -1579,7 +1619,7 @@ packages: description: name: universal_io sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.1" url_launcher: @@ -1587,7 +1627,7 @@ packages: description: name: url_launcher sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.3.2" url_launcher_android: @@ -1595,7 +1635,7 @@ packages: description: name: url_launcher_android sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.3.28" url_launcher_ios: @@ -1603,7 +1643,7 @@ packages: description: name: url_launcher_ios sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.4.1" url_launcher_linux: @@ -1611,7 +1651,7 @@ packages: description: name: url_launcher_linux sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.2" url_launcher_macos: @@ -1619,7 +1659,7 @@ packages: description: name: url_launcher_macos sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.5" url_launcher_platform_interface: @@ -1627,7 +1667,7 @@ packages: description: name: url_launcher_platform_interface sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.2" url_launcher_web: @@ -1635,7 +1675,7 @@ packages: description: name: url_launcher_web sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.2" url_launcher_windows: @@ -1643,7 +1683,7 @@ packages: description: name: url_launcher_windows sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.5" uuid: @@ -1651,7 +1691,7 @@ packages: description: name: uuid sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.5.3" vector_graphics: @@ -1659,7 +1699,7 @@ packages: description: name: vector_graphics sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.20" vector_graphics_codec: @@ -1667,7 +1707,7 @@ packages: description: name: vector_graphics_codec sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.13" vector_graphics_compiler: @@ -1675,7 +1715,7 @@ packages: description: name: vector_graphics_compiler sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" vector_math: @@ -1683,7 +1723,7 @@ packages: description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" vm_service: @@ -1691,7 +1731,7 @@ packages: description: name: vm_service sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "15.0.2" wake_on_lan: @@ -1699,7 +1739,7 @@ packages: description: name: wake_on_lan sha256: a0db43df0cd05181f476f38ec63345a763b7d3b9d8ab25cabbff45881780cb8e - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.1+3" wakelock_plus: @@ -1707,7 +1747,7 @@ packages: description: name: wakelock_plus sha256: "8b12256f616346910c519a35606fb69b1fe0737c06b6a447c6df43888b097f39" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.1" wakelock_plus_platform_interface: @@ -1715,7 +1755,7 @@ packages: description: name: wakelock_plus_platform_interface sha256: "24b84143787220a403491c2e5de0877fbbb87baf3f0b18a2a988973863db4b03" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" watch_connectivity: @@ -1730,7 +1770,7 @@ packages: description: name: watcher sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" web: @@ -1738,7 +1778,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" web_socket: @@ -1746,7 +1786,7 @@ packages: description: name: web_socket sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" web_socket_channel: @@ -1754,7 +1794,7 @@ packages: description: name: web_socket_channel sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" webdav_client_plus: @@ -1762,7 +1802,7 @@ packages: description: name: webdav_client_plus sha256: "0f992fe05a46674a800d9fd8fdc5c54952ff739da155c558776a62ca0c2bed3a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" webkit_inspection_protocol: @@ -1770,7 +1810,7 @@ packages: description: name: webkit_inspection_protocol sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" win32: @@ -1778,7 +1818,7 @@ packages: description: name: win32 sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.15.0" window_manager: @@ -1786,7 +1826,7 @@ packages: description: name: window_manager sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.5.1" xdg_directories: @@ -1794,7 +1834,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" xml: @@ -1802,7 +1842,7 @@ packages: description: name: xml sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.6.1" xterm: @@ -1817,7 +1857,7 @@ packages: description: name: yaml sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.3" yaml_writer: @@ -1825,7 +1865,7 @@ packages: description: name: yaml_writer sha256: "69651cd7238411179ac32079937d4aa9a2970150d6b2ae2c6fe6de09402a5dc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.0" zmodem: @@ -1833,7 +1873,7 @@ packages: description: name: zmodem sha256: "3b7e5b29f3a7d8aee472029b05165a68438eff2f3f7766edf13daba1e297adbf" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.0.6" zxing_lib: @@ -1841,7 +1881,7 @@ packages: description: name: zxing_lib sha256: f9170470b6bc947d21a6783486f88ef48aad66fc1380c8acd02b118418ec0ce0 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.4" sdks: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 61c38279..a271dfc9 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -15,6 +16,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 421b2378..1fa81ed8 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + app_links dynamic_color flutter_secure_storage_windows local_auth_windows