Client Lifecycle
One-time initialization
Call KalamClient.init() once at app startup. It initializes the underlying Rust runtime.
await KalamClient.init();Do not call KalamClient.connect(...) before init().
Creating a client
final client = await KalamClient.connect(
url: 'http://localhost:8080',
authProvider: () async => Auth.jwt(await getJwt()),
timeout: const Duration(seconds: 30),
maxRetries: 3,
disableCompression: false,
logLevel: Level.debug,
logListener: (entry) => debugPrint(entry.toString()),
);Notes:
connect(...)returns a ready-to-use handle. The SDK manages the WebSocket connection lifecycle automatically.- Most methods use HTTP; subscriptions additionally use WebSocket under the hood.
- With
wsLazyConnect: true(the default),connect()does not open the shared WebSocket immediately. - On native Dart targets, the shared subscription connection defaults to MessagePack serialization for lower overhead on first subscription and live updates.
wsLazyConnect (default: true)
By default, the WebSocket connection is lazy: it is deferred until the
first subscribe() call. This avoids unnecessary connections when the client
is only used for HTTP queries.
Set wsLazyConnect: false to establish the connection eagerly:
final client = await KalamClient.connect(
url: 'http://localhost:8080',
authProvider: () async => Auth.jwt(await getJwt()),
wsLazyConnect: false, // connect WebSocket immediately
);Auth refresh model
authProvideris the connect-time auth API.connect()resolves auth once up front, and the first lazysubscribe()reuses those already-resolved credentials.- Use
refreshAuth()to proactively re-runauthProviderand push updated credentials into the client. - Return
Auth.basic(...)if you want the SDK to perform a Basic-to-JWT exchange before first use.
Refreshing auth in place
If you provided an authProvider, you can re-fetch and push updated credentials into the client:
await client.refreshAuth();
Timer.periodic(const Duration(minutes: 55), (_) => client.refreshAuth());With the default Auth.none() provider, refreshAuth() simply re-resolves anonymous auth.
Manual WebSocket controls
The client also exposes a few explicit shared-socket controls:
isConnectedreturns whether the shared WebSocket is currently open.disconnectWebSocket()closes the shared WebSocket.reconnectWebSocket()first tries a fast reconnect with the current auth already held in memory. If the server rejects that reconnect for auth reasons, the SDK refreshes auth once and retries.
final connected = await client.isConnected;
if (!connected) {
await client.reconnectWebSocket();
}Mobile live-connection pattern
For Flutter apps with live screens, keep one KalamClient alive for the whole signed-in session and reconnect it when the app returns to the foreground.
class LiveClientController with WidgetsBindingObserver {
final KalamClient client;
LiveClientController(this.client);
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
client.reconnectWebSocket();
}
}
}That avoids waiting for a stale socket to be detected passively and is the fastest path back to a live subscription after resume.
Connection handlers
Use connectionHandlers to receive lifecycle events (connect/disconnect/errors) and optional raw message debugging.
final client = await KalamClient.connect(
url: 'http://localhost:8080',
authProvider: () async => Auth.jwt(await getJwt()),
connectionHandlers: ConnectionHandlers(
onConnect: () => print('connected'),
onDisconnect: (reason) => print('disconnected: ${reason.message}'),
onError: (error) => print('error: ${error.message}'),
onReceive: (message) => print('[recv] $message'),
onSend: (message) => print('[send] $message'),
),
);Handlers are only active when you pass at least one callback.
Connection events are buffered on the native side while Dart pulls them. The buffer is bounded to protect mobile memory; lifecycle events keep the newest signals when the buffer is full, and high-volume onReceive / onSend debug messages may be dropped under sustained load. Use onReceive / onSend only for local debugging; avoid logging sensitive data (tokens, PII) in production.
SDK logging level and redirection
KalamClient.connect(...) accepts:
logLevel: Level?(frompackage:logger/logger.dart)logListener: LogListener?
Example:
import 'package:logger/logger.dart' show Level;
final client = await KalamClient.connect(
url: 'http://localhost:8080',
authProvider: () async => Auth.jwt(await getJwt()),
logLevel: Level.info,
logListener: (entry) {
myLogger.i('[${entry.tag}] ${entry.message}');
},
);If no logListener is provided, logs are emitted with print(...) and appear in flutter run/Logcat.
Disposing
Call dispose() when the client is no longer needed:
await client.dispose();After disposing, do not call query() or subscribe() on the same instance.
If you keep a single client for the whole app lifetime, dispose it only when the app is shutting down.