feat(core): A.6.1 — use OS connectivity signals to drive reconnect
Extends the A.6 supervisor with an OS-level connectivity hint so a returning network triggers a redial immediately instead of waiting out the current backoff slot (up to 60 s). The watchdog remains the authoritative loss detector — the OS signal is advisory. * `chanora_core::NetworkState` (Unknown / Online / Offline) is owned by `ChanoraSession` via a `tokio::sync::watch::Sender`. `set_network_state()` / `network_state()` are the public accessors. * The supervisor's watch-phase `select!` gains a `network_rx` branch: Offline pre-charges watchdog misses (capped at `MAX_MISSES - 1`) so the next probe failure trips immediately; Online clears stale misses. This shrinks UI-banner latency on a Wi-Fi drop from ~15 s to ~5 s. * The reconnect-loop's backoff sleep races against Online: a transition cuts the sleep short and resets the attempt counter so future losses start at the smallest backoff window again. * `chanora_bridge` adds `BridgeNetworkState` (mirror enum) and a sync `set_network_state(state)` function. On platforms with no signal wired the supervisor stays at Unknown and falls back to pure watchdog/backoff — no behavioural regression vs A.6. * Flutter adds `connectivity_plus ^6.1.0` and wires `_wireConnectivity()` in `main()`: seeds with `checkConnectivity()` then forwards every `onConnectivityChanged` to the bridge, mapping any non-`none` transport to Online. Verified on Moto G Stylus 5G (Android 14): `svc wifi disable && svc data disable` for ~40 s — reconnect banner appeared promptly because the watchdog was pre-charged. After `svc wifi enable && svc data enable` the supervisor woke from its 15 s backoff slot and reconnected within seconds; the channel tree re-rendered without user action.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'l10n/generated/app_localizations.dart';
|
||||
@@ -20,9 +21,42 @@ import 'src/rust/frb_generated.dart';
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await RustLib.init();
|
||||
// Push the OS-reported connectivity state into the core supervisor.
|
||||
// The supervisor uses this to short-circuit reconnect backoff when
|
||||
// the network comes back, and to pre-charge the loss watchdog when
|
||||
// the OS already knows we're offline (A.6.1).
|
||||
unawaited(_wireConnectivity());
|
||||
runApp(const ChanoraApp());
|
||||
}
|
||||
|
||||
/// Map `connectivity_plus`' list-of-results to our coarse tri-state.
|
||||
/// We consider the device "Online" if any of the reported transports
|
||||
/// is non-`none`. This is intentionally permissive — the supervisor's
|
||||
/// watchdog still verifies reachability against the actual server.
|
||||
rust.BridgeNetworkState _mapConnectivity(List<ConnectivityResult> results) {
|
||||
if (results.isEmpty) return rust.BridgeNetworkState.unknown;
|
||||
final allNone = results.every((r) => r == ConnectivityResult.none);
|
||||
if (allNone) return rust.BridgeNetworkState.offline;
|
||||
return rust.BridgeNetworkState.online;
|
||||
}
|
||||
|
||||
Future<void> _wireConnectivity() async {
|
||||
final connectivity = Connectivity();
|
||||
// Seed with the current value so the supervisor has a real reading
|
||||
// before the first transition.
|
||||
try {
|
||||
final initial = await connectivity.checkConnectivity();
|
||||
rust.setNetworkState(state: _mapConnectivity(initial));
|
||||
} catch (_) {
|
||||
// Best effort; if the plugin isn't available on this platform
|
||||
// we stay at Unknown and the supervisor falls back to its
|
||||
// watchdog-only behaviour.
|
||||
}
|
||||
connectivity.onConnectivityChanged.listen((results) {
|
||||
rust.setNetworkState(state: _mapConnectivity(results));
|
||||
});
|
||||
}
|
||||
|
||||
class ChanoraApp extends StatelessWidget {
|
||||
const ChanoraApp({super.key});
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||
part 'api.freezed.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `runtime`, `session`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`, `from`
|
||||
|
||||
/// Connect to a TeamSpeak-compatible server and return the initial
|
||||
/// state snapshot. Honours the DEC-006 single-connection invariant
|
||||
@@ -37,6 +37,13 @@ Future<void> startAudio() => RustLib.instance.api.crateApiStartAudio();
|
||||
Future<void> setPtt({required bool active}) =>
|
||||
RustLib.instance.api.crateApiSetPtt(active: active);
|
||||
|
||||
/// Notify the core of the latest OS-reported connectivity state.
|
||||
/// Called by the Flutter side from `connectivity_plus` callbacks.
|
||||
/// The core's supervisor uses this to (a) pre-charge the watchdog
|
||||
/// on Offline and (b) short-circuit reconnect backoff on Online.
|
||||
void setNetworkState({required BridgeNetworkState state}) =>
|
||||
RustLib.instance.api.crateApiSetNetworkState(state: state);
|
||||
|
||||
/// Subscribe to lifecycle events. Each call yields a fresh
|
||||
/// subscription; multiple subscribers are supported. On slow
|
||||
/// consumers, events are dropped rather than blocking the supervisor
|
||||
@@ -185,6 +192,19 @@ sealed class BridgeEvent with _$BridgeEvent {
|
||||
const factory BridgeEvent.audioStopped() = BridgeEvent_AudioStopped;
|
||||
}
|
||||
|
||||
/// Coarse OS-reported network state. Mirrors
|
||||
/// [`chanora_core::NetworkState`] across the bridge.
|
||||
enum BridgeNetworkState {
|
||||
/// No signal seen yet.
|
||||
unknown,
|
||||
|
||||
/// OS reports a usable network.
|
||||
online,
|
||||
|
||||
/// OS reports no network.
|
||||
offline,
|
||||
}
|
||||
|
||||
/// Server snapshot as seen by Dart.
|
||||
class BridgeSnapshot {
|
||||
/// Server name.
|
||||
|
||||
@@ -67,7 +67,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.12.0';
|
||||
|
||||
@override
|
||||
int get rustContentHash => -1896742393;
|
||||
int get rustContentHash => -1212711005;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@@ -94,6 +94,8 @@ abstract class RustLibApi extends BaseApi {
|
||||
|
||||
Future<bool> crateApiIsConnected();
|
||||
|
||||
void crateApiSetNetworkState({required BridgeNetworkState state});
|
||||
|
||||
Future<void> crateApiSetPtt({required bool active});
|
||||
|
||||
Future<BridgeSnapshot> crateApiSnapshot();
|
||||
@@ -281,6 +283,29 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
TaskConstMeta get kCrateApiIsConnectedConstMeta =>
|
||||
const TaskConstMeta(debugName: "is_connected", argNames: []);
|
||||
|
||||
@override
|
||||
void crateApiSetNetworkState({required BridgeNetworkState state}) {
|
||||
return handler.executeSync(
|
||||
SyncTask(
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_bridge_network_state(state, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_unit,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiSetNetworkStateConstMeta,
|
||||
argValues: [state],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiSetNetworkStateConstMeta =>
|
||||
const TaskConstMeta(debugName: "set_network_state", argNames: ["state"]);
|
||||
|
||||
@override
|
||||
Future<void> crateApiSetPtt({required bool active}) {
|
||||
return handler.executeNormal(
|
||||
@@ -291,7 +316,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 7,
|
||||
funcId: 8,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -318,7 +343,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 8,
|
||||
funcId: 9,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -345,7 +370,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 9,
|
||||
funcId: 10,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -477,6 +502,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
BridgeNetworkState dco_decode_bridge_network_state(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return BridgeNetworkState.values[raw as int];
|
||||
}
|
||||
|
||||
@protected
|
||||
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -493,6 +524,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
int dco_decode_i_32(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as int;
|
||||
}
|
||||
|
||||
@protected
|
||||
PlatformInt64 dco_decode_i_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -665,6 +702,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
BridgeNetworkState sse_decode_bridge_network_state(
|
||||
SseDeserializer deserializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var inner = sse_decode_i_32(deserializer);
|
||||
return BridgeNetworkState.values[inner];
|
||||
}
|
||||
|
||||
@protected
|
||||
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -684,6 +730,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getInt32();
|
||||
}
|
||||
|
||||
@protected
|
||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -748,12 +800,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getInt32();
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_AnyhowException(
|
||||
AnyhowException self,
|
||||
@@ -871,6 +917,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_bridge_network_state(
|
||||
BridgeNetworkState self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.index, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_bridge_snapshot(
|
||||
BridgeSnapshot self,
|
||||
@@ -885,6 +940,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_list_bridge_client(self.clients, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putInt32(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -947,10 +1008,4 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
void sse_encode_unit(void self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putInt32(self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BridgeEvent dco_decode_bridge_event(dynamic raw);
|
||||
|
||||
@protected
|
||||
BridgeNetworkState dco_decode_bridge_network_state(dynamic raw);
|
||||
|
||||
@protected
|
||||
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_i_32(dynamic raw);
|
||||
|
||||
@protected
|
||||
PlatformInt64 dco_decode_i_64(dynamic raw);
|
||||
|
||||
@@ -104,9 +110,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BridgeEvent sse_decode_bridge_event(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BridgeNetworkState sse_decode_bridge_network_state(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
||||
|
||||
@@ -135,9 +149,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_AnyhowException(
|
||||
AnyhowException self,
|
||||
@@ -174,12 +185,21 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_bridge_event(BridgeEvent self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_bridge_network_state(
|
||||
BridgeNetworkState self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_bridge_snapshot(
|
||||
BridgeSnapshot self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
||||
|
||||
@@ -212,9 +232,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
|
||||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
|
||||
@@ -50,9 +50,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BridgeEvent dco_decode_bridge_event(dynamic raw);
|
||||
|
||||
@protected
|
||||
BridgeNetworkState dco_decode_bridge_network_state(dynamic raw);
|
||||
|
||||
@protected
|
||||
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_i_32(dynamic raw);
|
||||
|
||||
@protected
|
||||
PlatformInt64 dco_decode_i_64(dynamic raw);
|
||||
|
||||
@@ -106,9 +112,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BridgeEvent sse_decode_bridge_event(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BridgeNetworkState sse_decode_bridge_network_state(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
||||
|
||||
@@ -137,9 +151,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_AnyhowException(
|
||||
AnyhowException self,
|
||||
@@ -176,12 +187,21 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_bridge_event(BridgeEvent self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_bridge_network_state(
|
||||
BridgeNetworkState self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_bridge_snapshot(
|
||||
BridgeSnapshot self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
||||
|
||||
@@ -214,9 +234,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
|
||||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
|
||||
@@ -129,6 +129,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
connectivity_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_plus_platform_interface
|
||||
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -161,6 +177,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.7"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dbus
|
||||
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.12"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -169,6 +193,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -216,6 +248,11 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freezed:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -360,6 +397,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
nm:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nm
|
||||
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -376,6 +421,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -541,6 +602,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -39,6 +39,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.8
|
||||
flutter_rust_bridge: 2.12.0
|
||||
freezed_annotation: ^3.1.0
|
||||
connectivity_plus: ^6.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user