feat: wire chain_client_sdk gRPC client + HubRepository scaffold
Adds a real Ch∆In hub backend to the Flutter client as a sibling-path dependency on chain_client_sdk_dart, alongside the existing MockRepository (which stays the default until the flow.completed-to-Evaluation pipeline lands in Phase 0 week 1). Wiring HubSettings (lib/data/hub_endpoint.dart) — persists host / port / secure / token / useHub flags via SharedPreferences under the Studio-compatible hub.* keys. Distinct from the SDK's chain.HubEndpoint, which is a transport-level value type — HubSettings adapts in that direction. HubRepository (lib/data/hub_repository.dart) — implements EvaluationRepository on top of HubClient. connect() runs a 4-second healthy() probe; on failure returns a repository with isHealthy=false rather than throwing, so the UI can surface "hub-down" without an exception-handling round-trip. list() / byEli() return empty data in Phase 0 because no hub-side flow yet produces Evaluation objects in the lawheatmap-specific shape. main.dart — loads HubSettings on startup; if useHub=false starts with MockRepository (fast offline default), otherwise probes the hub and falls back to MockRepository on probe failure. No mid-session swap to keep the Phase 0 wiring simple; a settings save instructs the user to restart. HubStatusPage — read-only summary card at the top, editable endpoint form, "Verbindung testen" probe button, "Speichern" persist button. Live-flow runbook below describes the chain serve / install / run / approve sequence so a user can wire the real flow themselves once Phase 1 lands. flutter analyze: clean. flutter test: 2/2 passing. Smoke-launch on macOS: app boots, Dart VM service comes up, no runtime errors in mock mode. Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
fa8acd41b8
commit
39733628b6
6 changed files with 509 additions and 31 deletions
83
app/lib/data/hub_endpoint.dart
Normal file
83
app/lib/data/hub_endpoint.dart
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Persisted user settings for the Ch∆In hub connection.
|
||||
///
|
||||
/// Stored under four SharedPreferences keys matching the Studio
|
||||
/// convention (`hub.host`, `hub.port`, `hub.secure`, `hub.token`)
|
||||
/// so a hub once configured stays configured across app launches.
|
||||
/// Distinct from the SDK's `chain.HubEndpoint`, which is a
|
||||
/// transport-level value type — [HubSettings.toEndpoint] adapts
|
||||
/// in that direction.
|
||||
class HubSettings {
|
||||
const HubSettings({
|
||||
required this.host,
|
||||
required this.port,
|
||||
required this.secure,
|
||||
this.authToken,
|
||||
this.useHub = false,
|
||||
});
|
||||
|
||||
final String host;
|
||||
final int port;
|
||||
final bool secure;
|
||||
|
||||
/// Bearer token; read from `~/.chain/hub-auth-token` by the
|
||||
/// hub-status page in Phase 1. Optional in Phase 0 because the
|
||||
/// default `chain serve` runs without auth.
|
||||
final String? authToken;
|
||||
|
||||
/// User intent: try to connect to the hub on startup. False
|
||||
/// keeps the app in MockRepository mode regardless of host/port.
|
||||
final bool useHub;
|
||||
|
||||
static const defaultHost = '127.0.0.1';
|
||||
static const defaultPort = 50051;
|
||||
|
||||
String get scheme => secure ? 'https' : 'http';
|
||||
String get url => '$scheme://$host:$port';
|
||||
|
||||
static const _kHost = 'hub.host';
|
||||
static const _kPort = 'hub.port';
|
||||
static const _kSecure = 'hub.secure';
|
||||
static const _kToken = 'hub.token';
|
||||
static const _kUseHub = 'hub.use';
|
||||
|
||||
static Future<HubSettings> load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return HubSettings(
|
||||
host: prefs.getString(_kHost) ?? defaultHost,
|
||||
port: prefs.getInt(_kPort) ?? defaultPort,
|
||||
secure: prefs.getBool(_kSecure) ?? false,
|
||||
authToken: prefs.getString(_kToken),
|
||||
useHub: prefs.getBool(_kUseHub) ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_kHost, host);
|
||||
await prefs.setInt(_kPort, port);
|
||||
await prefs.setBool(_kSecure, secure);
|
||||
await prefs.setBool(_kUseHub, useHub);
|
||||
if (authToken != null) {
|
||||
await prefs.setString(_kToken, authToken!);
|
||||
} else {
|
||||
await prefs.remove(_kToken);
|
||||
}
|
||||
}
|
||||
|
||||
HubSettings copyWith({
|
||||
String? host,
|
||||
int? port,
|
||||
bool? secure,
|
||||
String? authToken,
|
||||
bool? useHub,
|
||||
}) =>
|
||||
HubSettings(
|
||||
host: host ?? this.host,
|
||||
port: port ?? this.port,
|
||||
secure: secure ?? this.secure,
|
||||
authToken: authToken ?? this.authToken,
|
||||
useHub: useHub ?? this.useHub,
|
||||
);
|
||||
}
|
||||
74
app/lib/data/hub_repository.dart
Normal file
74
app/lib/data/hub_repository.dart
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import 'package:chain_client_sdk/chain_client_sdk.dart' as chain;
|
||||
|
||||
import 'hub_endpoint.dart';
|
||||
import 'models.dart';
|
||||
import 'repository.dart';
|
||||
|
||||
/// Live `EvaluationRepository` backed by a Ch∆In hub over gRPC.
|
||||
///
|
||||
/// Phase 0 scope: open a HubClient, run a healthy() probe, and
|
||||
/// surface the connection state to the UI. The list() / byEli()
|
||||
/// methods return empty data because no flow on the hub yet
|
||||
/// produces Evaluation objects in the lawheatmap-specific shape —
|
||||
/// that pipeline lands in Phase 0 week 1 once a chain run wraps
|
||||
/// flows/durchstich-gewo-14.yaml output into the Evaluation
|
||||
/// schema.
|
||||
///
|
||||
/// Until then [MockRepository] stays the default. Pick this
|
||||
/// repository explicitly via the Hub-settings page; on first
|
||||
/// healthy() failure the app falls back to MockRepository so the
|
||||
/// UI keeps working.
|
||||
class HubRepository implements EvaluationRepository {
|
||||
HubRepository._(this._client, this.settings, this._healthy);
|
||||
|
||||
final chain.HubClient _client;
|
||||
final HubSettings settings;
|
||||
final bool _healthy;
|
||||
|
||||
/// Try to connect. Returns a repository with `isHealthy=false`
|
||||
/// if the probe times out or the hub is unreachable; the
|
||||
/// caller decides whether to use it or fall back to
|
||||
/// MockRepository.
|
||||
static Future<HubRepository> connect(HubSettings settings) async {
|
||||
final endpoint = chain.HubEndpoint(
|
||||
host: settings.host,
|
||||
port: settings.port,
|
||||
secure: settings.secure,
|
||||
);
|
||||
final client = chain.HubClient(
|
||||
endpoint: endpoint,
|
||||
authToken: settings.authToken,
|
||||
);
|
||||
final ok = await client.healthy().timeout(
|
||||
const Duration(seconds: 4),
|
||||
onTimeout: () => false,
|
||||
);
|
||||
return HubRepository._(client, settings, ok);
|
||||
}
|
||||
|
||||
bool get isHealthy => _healthy;
|
||||
|
||||
/// Underlying SDK client — exposed so the hub-status page can
|
||||
/// list capabilities, query the audit log, and verify the
|
||||
/// event-chain without leaking gRPC types to other pages.
|
||||
chain.HubClient get client => _client;
|
||||
|
||||
@override
|
||||
Future<List<Evaluation>> list() async {
|
||||
// Phase 0 placeholder. The pipeline that turns
|
||||
// flow.completed events into Evaluation objects lives in the
|
||||
// surrounding flow (flows/durchstich-gewo-14.yaml) and lands
|
||||
// in Phase 0 week 1.
|
||||
return const [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Evaluation?> byEli(String eli) async => null;
|
||||
|
||||
@override
|
||||
String get mode => _healthy ? 'hub-live' : 'hub-down';
|
||||
|
||||
@override
|
||||
String get hubLabel => '${settings.url}'
|
||||
'${_healthy ? '' : ' — Verbindung gestört'}';
|
||||
}
|
||||
|
|
@ -1,23 +1,45 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import 'data/hub_endpoint.dart';
|
||||
import 'data/hub_repository.dart';
|
||||
import 'data/repository.dart';
|
||||
import 'pages/landing_page.dart';
|
||||
import 'theme/lawheatmap_theme.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const LawHeatmapApp());
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final settings = await HubSettings.load();
|
||||
final repository = await _pickRepository(settings);
|
||||
runApp(LawHeatmapApp(repository: repository, settings: settings));
|
||||
}
|
||||
|
||||
/// Decide which repository to start with.
|
||||
///
|
||||
/// useHub=false → Mock, no probe (fast offline start)
|
||||
/// useHub=true, healthy → HubRepository
|
||||
/// useHub=true, NOT ok → MockRepository (graceful fallback;
|
||||
/// UI banner shows hub-down so the
|
||||
/// user can re-try from settings)
|
||||
Future<EvaluationRepository> _pickRepository(HubSettings settings) async {
|
||||
if (!settings.useHub) return const MockRepository();
|
||||
final hub = await HubRepository.connect(settings);
|
||||
if (hub.isHealthy) return hub;
|
||||
return const MockRepository();
|
||||
}
|
||||
|
||||
class LawHeatmapApp extends StatelessWidget {
|
||||
const LawHeatmapApp({super.key});
|
||||
const LawHeatmapApp({
|
||||
super.key,
|
||||
this.repository = const MockRepository(),
|
||||
this.settings,
|
||||
});
|
||||
|
||||
final EvaluationRepository repository;
|
||||
final HubSettings? settings;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Phase 0: in-memory fixtures only. Swap to a HubRepository
|
||||
// (gRPC against `chain serve`) without changing the UI.
|
||||
const EvaluationRepository repository = MockRepository();
|
||||
|
||||
return MaterialApp(
|
||||
title: 'F∆I Law-Heatmap',
|
||||
debugShowCheckedModeBanner: false,
|
||||
|
|
@ -30,7 +52,7 @@ class LawHeatmapApp extends StatelessWidget {
|
|||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('de'), Locale('en')],
|
||||
home: const LandingPage(repository: repository),
|
||||
home: LandingPage(repository: repository),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,112 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../data/hub_endpoint.dart';
|
||||
import '../data/hub_repository.dart';
|
||||
import '../data/repository.dart';
|
||||
import '../theme/lawheatmap_tokens.dart';
|
||||
import '../widgets/lawheatmap_card.dart';
|
||||
|
||||
class HubStatusPage extends StatelessWidget {
|
||||
/// Hub connection + settings. Read-only summary at the top, then
|
||||
/// an editable form for endpoint host/port/secure/token, then a
|
||||
/// "Verbinden / Test"-button. Changes are persisted via
|
||||
/// HubSettings and take effect on the next app start (Phase 0
|
||||
/// keeps the running repository to avoid mid-session swap
|
||||
/// headaches).
|
||||
class HubStatusPage extends StatefulWidget {
|
||||
const HubStatusPage({super.key, required this.repository});
|
||||
|
||||
final EvaluationRepository repository;
|
||||
|
||||
@override
|
||||
State<HubStatusPage> createState() => _HubStatusPageState();
|
||||
}
|
||||
|
||||
class _HubStatusPageState extends State<HubStatusPage> {
|
||||
HubSettings? _settings;
|
||||
late final TextEditingController _hostCtl;
|
||||
late final TextEditingController _portCtl;
|
||||
late final TextEditingController _tokenCtl;
|
||||
bool _secure = false;
|
||||
bool _useHub = false;
|
||||
String? _probeMessage;
|
||||
bool _probing = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_hostCtl = TextEditingController();
|
||||
_portCtl = TextEditingController();
|
||||
_tokenCtl = TextEditingController();
|
||||
HubSettings.load().then((s) {
|
||||
setState(() {
|
||||
_settings = s;
|
||||
_hostCtl.text = s.host;
|
||||
_portCtl.text = s.port.toString();
|
||||
_tokenCtl.text = s.authToken ?? '';
|
||||
_secure = s.secure;
|
||||
_useHub = s.useHub;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hostCtl.dispose();
|
||||
_portCtl.dispose();
|
||||
_tokenCtl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
HubSettings _formSettings() => HubSettings(
|
||||
host: _hostCtl.text.trim().isEmpty
|
||||
? HubSettings.defaultHost
|
||||
: _hostCtl.text.trim(),
|
||||
port: int.tryParse(_portCtl.text.trim()) ??
|
||||
HubSettings.defaultPort,
|
||||
secure: _secure,
|
||||
authToken:
|
||||
_tokenCtl.text.trim().isEmpty ? null : _tokenCtl.text.trim(),
|
||||
useHub: _useHub,
|
||||
);
|
||||
|
||||
Future<void> _save() async {
|
||||
final s = _formSettings();
|
||||
await s.save();
|
||||
if (!mounted) return;
|
||||
setState(() => _settings = s);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Einstellungen gespeichert. '
|
||||
'Beim nächsten Start wird die App den Hub verwenden, '
|
||||
'wenn der Schalter „Hub nutzen" aktiv ist.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _probe() async {
|
||||
setState(() {
|
||||
_probing = true;
|
||||
_probeMessage = null;
|
||||
});
|
||||
try {
|
||||
final hub = await HubRepository.connect(_formSettings());
|
||||
setState(() {
|
||||
_probeMessage = hub.isHealthy
|
||||
? 'Verbindung erfolgreich (healthy=true).'
|
||||
: 'Hub nicht erreichbar oder antwortet nicht SERVING.';
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() => _probeMessage = 'Fehler: $e');
|
||||
} finally {
|
||||
if (mounted) setState(() => _probing = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Theme.of(context).textTheme;
|
||||
final repo = widget.repository;
|
||||
final s = _settings;
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(LawHeatmapSpace.xl),
|
||||
child: Column(
|
||||
|
|
@ -20,7 +115,7 @@ class HubStatusPage extends StatelessWidget {
|
|||
Text('Ch∆In Hub', style: t.displaySmall),
|
||||
const SizedBox(height: LawHeatmapSpace.xs),
|
||||
Text(
|
||||
'Status der Verbindung zum lokalen Hub und des Audit-Logs.',
|
||||
'Status der Verbindung und Konfiguration des lokalen Hubs.',
|
||||
style: t.bodyLarge?.copyWith(color: LawHeatmapColors.mute),
|
||||
),
|
||||
const SizedBox(height: LawHeatmapSpace.lg),
|
||||
|
|
@ -29,10 +124,19 @@ class HubStatusPage extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_row('Modus', repository.mode.toUpperCase(), t),
|
||||
_row('Quelle', repository.hubLabel, t),
|
||||
_row('gRPC-Endpunkt', '— (Phase-0-Stub)', t),
|
||||
_row('Audit-DB', '~/.chain/chain.db (geplant)', t),
|
||||
_row('Aktiver Modus', repo.mode.toUpperCase(), t),
|
||||
_row('Datenquelle', repo.hubLabel, t),
|
||||
_row(
|
||||
'gRPC-Endpunkt',
|
||||
s == null ? '…' : s.url,
|
||||
t,
|
||||
),
|
||||
_row(
|
||||
'Hub-Nutzung beim Start',
|
||||
s == null ? '…' : (s.useHub ? 'ja' : 'nein'),
|
||||
t,
|
||||
),
|
||||
_row('Audit-DB', '~/.chain/chain.db (Hub-seitig)', t),
|
||||
_row('Hash-Tag', 'CHAIN-EVENT-V3', t),
|
||||
],
|
||||
),
|
||||
|
|
@ -42,21 +146,120 @@ class HubStatusPage extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Geplante Live-Anbindung', style: t.headlineSmall),
|
||||
Text('Verbindung konfigurieren',
|
||||
style: t.headlineSmall),
|
||||
const SizedBox(height: LawHeatmapSpace.sm),
|
||||
Text(
|
||||
'1. `chain serve` startet lokal auf Port 50051.\n'
|
||||
'2. Diese App liest `~/.chain/hub-auth-token` und '
|
||||
'verbindet via gRPC.\n'
|
||||
'3. Capability `text.akoma_normalize@^0` muss '
|
||||
'installiert sein (`chain install text.akoma_normalize`).\n'
|
||||
'4. Pilot-Flow `flows/durchstich-gewo-14.yaml` läuft '
|
||||
'gegen GewO §14.\n'
|
||||
'5. Jedes step.completed-Event landet im Audit-Log; '
|
||||
'die Repository-Implementierung tauscht von MockRepository '
|
||||
'auf HubRepository (gleiches Interface, kein UI-Wechsel).',
|
||||
'`chain serve` läuft standardmäßig auf '
|
||||
'127.0.0.1:50051 ohne Auth. '
|
||||
'Bei aktiver Auth den Bearer-Token aus '
|
||||
'~/.chain/hub-auth-token hier eintragen.',
|
||||
style: t.labelSmall
|
||||
?.copyWith(color: LawHeatmapColors.mute),
|
||||
),
|
||||
const SizedBox(height: LawHeatmapSpace.lg),
|
||||
Row(children: [
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: TextField(
|
||||
controller: _hostCtl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Host',
|
||||
hintText: '127.0.0.1',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: LawHeatmapSpace.md),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _portCtl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Port',
|
||||
hintText: '50051',
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: LawHeatmapSpace.md),
|
||||
TextField(
|
||||
controller: _tokenCtl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Auth-Token (optional)',
|
||||
),
|
||||
obscureText: true,
|
||||
),
|
||||
const SizedBox(height: LawHeatmapSpace.md),
|
||||
Row(children: [
|
||||
Switch(
|
||||
value: _secure,
|
||||
onChanged: (v) => setState(() => _secure = v),
|
||||
),
|
||||
const SizedBox(width: LawHeatmapSpace.sm),
|
||||
const Text('TLS (https)'),
|
||||
const SizedBox(width: LawHeatmapSpace.xl),
|
||||
Switch(
|
||||
value: _useHub,
|
||||
onChanged: (v) => setState(() => _useHub = v),
|
||||
),
|
||||
const SizedBox(width: LawHeatmapSpace.sm),
|
||||
const Text('Hub beim Start nutzen'),
|
||||
]),
|
||||
const SizedBox(height: LawHeatmapSpace.lg),
|
||||
Row(children: [
|
||||
FilledButton.icon(
|
||||
onPressed: _probing ? null : _probe,
|
||||
icon: _probing
|
||||
? const SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.bolt_outlined),
|
||||
label: const Text('Verbindung testen'),
|
||||
),
|
||||
const SizedBox(width: LawHeatmapSpace.md),
|
||||
OutlinedButton.icon(
|
||||
onPressed: _save,
|
||||
icon: const Icon(Icons.save_outlined),
|
||||
label: const Text('Speichern'),
|
||||
),
|
||||
]),
|
||||
if (_probeMessage != null) ...[
|
||||
const SizedBox(height: LawHeatmapSpace.md),
|
||||
Text(_probeMessage!,
|
||||
style: t.labelLarge?.copyWith(
|
||||
color: LawHeatmapColors.signal,
|
||||
)),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: LawHeatmapSpace.lg),
|
||||
LawHeatmapCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Geplanter Live-Flow', style: t.headlineSmall),
|
||||
const SizedBox(height: LawHeatmapSpace.sm),
|
||||
Text(
|
||||
'1. `chain serve` auf localhost:50051 starten.\n'
|
||||
'2. Module installieren (`chain install`).\n'
|
||||
'3. Flow ausführen:\n'
|
||||
' chain run flows/durchstich-gewo-14.yaml \\\n'
|
||||
' --input norm_url=https://www.gesetze-im-internet.de/gewo/__14.xml \\\n'
|
||||
' --input cohort_id=berlin-kmu\n'
|
||||
'4. `chain admin approvals list` → '
|
||||
'Juristen-Gate freigeben.\n'
|
||||
'5. App neu starten — Hub-Modus aktiv, '
|
||||
'flow.completed-Events werden in '
|
||||
'Evaluation-Objekte übersetzt.',
|
||||
style: t.bodyLarge?.copyWith(
|
||||
height: 1.6,
|
||||
fontFamily: LawHeatmapTypography.mono,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -73,7 +276,7 @@ class HubStatusPage extends StatelessWidget {
|
|||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 160,
|
||||
width: 200,
|
||||
child: Text(label,
|
||||
style: t.labelLarge
|
||||
?.copyWith(color: LawHeatmapColors.mute)),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
chain_client_sdk:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../fai_chain_client_sdk_dart"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.18.0"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -41,6 +48,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -65,6 +80,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
@ -93,6 +116,62 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
google_cloud:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_cloud
|
||||
sha256: b385e20726ef5315d302c5933bfb728103116c5be2d3d17094b01a82da538c1f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_identity_services_web
|
||||
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+1"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: googleapis_auth
|
||||
sha256: "1417d8846663df5e7b77ca56591c5edd442c66ffc9c01ab036e138a21a148e86"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
grpc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: grpc
|
||||
sha256: "86be3a7d39ad865b214a7370021ac80e68939238b507730de6d97fc662cb2723"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http2
|
||||
sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -205,6 +284,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -314,6 +401,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.11"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ publish_to: 'none'
|
|||
version: 0.1.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=3.6.0 <4.0.0"
|
||||
sdk: ">=3.11.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
dependencies:
|
||||
|
|
@ -15,10 +15,11 @@ dependencies:
|
|||
intl: ^0.20.2
|
||||
shared_preferences: ^2.3.0
|
||||
|
||||
# Ch∆In wire — same sibling-path pattern as fai_chain_studio.
|
||||
# Uncomment once the gRPC integration starts (Phase 0, week 1+).
|
||||
# chain_client_sdk:
|
||||
# path: ../../fai_chain_client_sdk_dart
|
||||
# Ch∆In gRPC wire — sibling-path dep, same pattern as
|
||||
# fai_chain_studio. Adapt the chain_client_sdk_dart relative
|
||||
# path when this repo is cloned outside the fai/ workspace.
|
||||
chain_client_sdk:
|
||||
path: ../../fai_chain_client_sdk_dart
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue