Compose Frontend
mero-compose is an optional Jetpack Compose layer that ships alongside the core
SDK — a MeroClient view-model exposing a StateFlow<MeroAuthState>, a
MeroProvider/useMero pair for passing it down the tree, and ready-made
LoginSheet / ConnectButton composables. It’s the analog of mero-react’s
MeroProvider/useMero + LoginModal.
Add the optional dependency (see install):
implementation("com.calimero.mero:mero-compose:0.1.0")Wire it up
Section titled “Wire it up”Build a MeroClient, provide it with MeroProvider, then read it anywhere below
with useMero():
import com.calimero.mero.compose.*
@Composablefun App(context: Context) { // Production factory: EncryptedPrefs token store + cross-process refresh lock val client = remember { MeroClient.create(context, baseUrl = "https://your-node.example") }
MeroProvider(client) { val c = useMero() // the client, anywhere in the tree val state by c.state.collectAsStateWithLifecycle() if (state.isAuthenticated) HomeScreen() else LoginSheet() }}MeroClient
Section titled “MeroClient”A plain (non-composable) class wrapping a Mero, exposing one observable state
flow. MeroAuthState carries isAuthenticated, isLoading, nodeUrl,
applicationId, contextId, contextIdentity, and error.
class MeroClient( val mero: Mero, baseUrl: String, allowedNodeUrls: List<String> = emptyList(),) { val state: StateFlow<MeroAuthState> val rpc: RpcClient // passthrough to mero.rpc
suspend fun login(username: String, password: String, bootstrapSecret: String? = null) fun startSsoLogin(context: Context, options: AuthLoginOptions) // opens a Custom Tab fun handleAuthCallback(url: String): Boolean // parse + adopt SSO tokens suspend fun logout(clientId: String? = null)
companion object { // Production factory: EncryptedPrefs store + cross-process refresh lock. fun create(context: Context, baseUrl: String, allowedNodeUrls: List<String> = emptyList()): MeroClient }}handleAuthCallback trust-checks the callback’s node_url against baseUrl /
allowedNodeUrls before adopting the tokens, so a malicious deep link can’t point
you at another node. Construct MeroClient(mero, baseUrl) directly with a
pre-built Mero (e.g. a MemoryTokenStore) in tests.
Drop-in composables
Section titled “Drop-in composables”Both default their client to useMero(), so inside a MeroProvider you can
drop them in with no arguments:
| Composable | Purpose |
|---|---|
MeroProvider(client) { … } |
provides the MeroClient via a CompositionLocal |
useMero() |
reads the provided client (throws if no provider above) |
LoginSheet(showBootstrapSecret, onAuthenticated) |
credential form → client.login(...) |
ConnectButton(clientId, onConnect) |
“Connect” when signed out, “Log out” when signed in |
LoginSheet( showBootstrapSecret = true, // shows the first-login setup-code field onAuthenticated = { /* navigate home */ },)
ConnectButton(onConnect = { /* open your login sheet */ })SSO from Compose
Section titled “SSO from Compose”val client = useMero()// kick off hosted login (opens a Custom Tab)client.startSsoLogin(context, AuthLoginOptions(callbackUrl = "myapp://auth-callback", mode = "login"))
// in the deep-link Activity that receives myapp://auth-callback:val ok = client.handleAuthCallback(intent.dataString ?: "")