feat(ui): hub-endpoint settings dialog with persistence

Studio is no longer hardcoded to localhost:50051. A gear icon
in the sidebar footer opens a small settings dialog where the
operator picks host / port / TLS-on-or-off. Saved values
persist via shared_preferences and Studio reconnects to the
new endpoint immediately.

- data/hub.dart: HubService.loadPersistedEndpoint() called once
  before the first frame; reconnect() persists on every change.
- widgets/fai_settings_dialog.dart: typed form with port range
  validation, live URL preview pill, error surfacing if the
  reconnect fails.
- main.dart: gear icon at the sidebar bottom, replaces the
  static "platform v0.10.42" footer.

shared_preferences added as dependency. flutter analyze clean,
flutter test 2/2, macOS debug build succeeds.

Bumps fai_studio 0.4.0 -> 0.5.0.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-05 22:12:47 +02:00
parent 4352ecc28c
commit 521a8baab8
9 changed files with 365 additions and 11 deletions

View file

@ -6,6 +6,7 @@
// protobuf imports.
import 'package:fai_dart_sdk/fai_dart_sdk.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HubService {
HubService._();
@ -17,11 +18,38 @@ class HubService {
/// targets. Shown in the navigation rail.
String get endpointLabel => _client.endpoint.toString();
/// Reconnect to a new endpoint. Useful if the user changes
/// the hub URL at runtime (Phase 1+).
HubEndpoint get currentEndpoint => _client.endpoint;
static const _kHostKey = 'hub.host';
static const _kPortKey = 'hub.port';
static const _kSecureKey = 'hub.secure';
/// Read persisted endpoint and reconnect if it differs from
/// the default. Called once at app start.
Future<void> loadPersistedEndpoint() async {
final prefs = await SharedPreferences.getInstance();
final host = prefs.getString(_kHostKey);
final port = prefs.getInt(_kPortKey);
final secure = prefs.getBool(_kSecureKey);
if (host == null) return;
final endpoint = HubEndpoint(
host: host,
port: port ?? 50051,
secure: secure ?? false,
);
if (endpoint.toString() != _client.endpoint.toString()) {
await reconnect(endpoint);
}
}
/// Reconnect to a new endpoint and persist for next launch.
Future<void> reconnect(HubEndpoint endpoint) async {
await _client.close();
_client = HubClient(endpoint: endpoint);
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_kHostKey, endpoint.host);
await prefs.setInt(_kPortKey, endpoint.port);
await prefs.setBool(_kSecureKey, endpoint.secure);
}
Future<bool> healthy() => _client.healthy();