Trio Architecture: Building a Compose‑Based Android Framework on Mavericks
Airbnb’s Android team introduced Trio, a Compose‑only framework built atop the Mavericks state‑management library that replaces Fragment‑based navigation with type‑safe, modular page routing, ViewModel‑driven state, compile‑time validation, and dedicated developer tools, now powering the majority of the app’s screens.
We will present a three‑part series describing how Airbnb’s Android team uses the Mavericks library to build a Compose‑based architecture called Trio.
Trio is an Android framework built on top of the open‑source Mavericks library. It leverages Mavericks to maintain navigation and application state inside a ViewModel while providing a fully Compose UI.
The series will explore the challenges of a Fragment‑based architecture, the motivations for creating Trio, and the core components of the Trio architecture.
Type‑safe navigation between feature modules
Storing navigation state in the ViewModel
Compose‑based page‑to‑page communication (including result handling and bidirectional communication)
Compile‑time validation of navigation and communication interfaces
Developer tools created to support the Trio workflow
Airbnb began developing Trio two years ago; it has been in production for over a year and now powers most pages of the Airbnb Android app, allowing engineers to build 100% Compose UI features.
About Mavericks
Mavericks is a state‑management library that decouples UI from state. All ViewModel data must be stored in a single MavericksState data class. For example:
data class CounterState(
val count: Int = 0
) : MavericksStateState updates are performed via setState with a reducer lambda:
class CounterViewModel : MavericksViewModel
(...) {
fun incrementCount() {
setState {
// this = previous state
this.copy(count = count + 1)
}
}
}MavericksViewModel queues setState calls on a background thread, guaranteeing thread‑safety and atomic updates. State changes are exposed as a Flow that Compose can collect:
counterViewModel.stateFlow.collectAsState().value.countChallenges of a Fragment‑Based Architecture
Integrating Mavericks with a Fragment‑centric architecture introduces several problems: ambiguous ViewModel scopes, difficult type‑safe communication between fragments, synchronization issues between ViewModel‑driven state changes and FragmentManager navigation, testing difficulties, and inefficient UI updates.
These pain points motivated the team to abandon Fragments entirely in favor of a pure Compose solution.
Why Trio Was Built
In 2021 the team started exploring Jetpack Compose and decided to drop Fragments to reduce technical debt and prepare for the future of Android development. They kept Mavericks because of existing expertise and wanted a type‑safe, modular navigation system that works well with Compose.
Trio provides a clear separation of Args (static), Props (dynamic), State, ViewModel, and UI, ensuring type‑safe navigation and communication.
Trio Architecture Overview
A Trio consists of three main classes (Args, Props, State, ViewModel, UI). Each Trio is a self‑contained unit that can be nested to form a custom navigation hierarchy. The ViewModel updates state via Mavericks reducers; the UI observes state and forwards UI events back to the ViewModel.
Example Trio class declaration:
class CounterScreen : Trio<
CounterArgs,
CounterProps,
CounterState,
CounterViewModel,
CounterUI
>(initializer) {
override fun createInitialState(args: CounterArgs, props: CounterProps): CounterState {
return CounterState(args.count)
}
// other factory methods omitted for brevity
}Initializers wrap the initial arguments or state, allowing independent loading of pages during development.
Args are static values (e.g., IDs), while Props are dynamic flows that can change over time.
Factory methods for creating the ViewModel and UI are also defined in the Trio subclass:
override fun createViewModel(initializer: Initializer
) =
CounterViewModel(initializer) override fun createUI(viewModel: CounterViewModel): CounterUI =
CounterUI(viewModel)UI Class
The UI class implements a single composable function Content that receives the current state and renders the UI. UI events are delegated to the ViewModel:
class CounterUI(override val viewModel: CounterViewModel) : UI
{
@Composable
override fun TrioRenderScope.Content(state: CounterState) {
Column {
TopAppBar()
Button(
text = state.count,
modifier = Modifier.clickable { viewModel.incrementCount() }
)
// ...
}
}
}The content recomposes whenever the state changes, enforcing a unidirectional data flow and simplifying testing.
Rendering a Trio
To render a Trio, its Content function is called. The framework collects the ViewModel’s state flow, creates the UI, and places it inside a Box that respects the caller’s constraints:
@Composable
internal fun TrioRenderScope.Content(modifier: Modifier = Modifier) {
key(trioId) {
val activity = LocalContext.current as ComponentActivity
val viewModel = remember { getOrCreateViewModel(activity) }
val ui = remember { createUI(viewModel) }
val state = viewModel.stateFlow.collectAsState(viewModel.currentState).value
Box(propagateMinConstraints = true, modifier = modifier) {
ui.Content(state = state)
}
}
}Animations are supported via a TrioRenderScope that wraps Compose’s AnimatedVisibilityScope :
@Composable
fun ShowTrio(trio: Trio, modifier: Modifier) {
AnimatedVisibility(true, enter = EnterTransition.None, exit = ExitTransition.None) {
val animationScope = TrioRenderScopeImpl(this)
trio.Content(modifier, animationScope)
}
}Additional infrastructure handles lifecycle tracking, state saving, and ViewModel mocking for screenshot tests and IDE previews.
Summary
This introductory article explained the background of Mavericks and Fragments at Airbnb, the reasons for building Trio, and provided an overview of Trio’s architecture, including the Trio class, UI class, and rendering pipeline.
Future articles in the series will dive deeper into Trio’s navigation system and the Props mechanism for dynamic page‑to‑page communication.
Airbnb Technology Team
Official account of the Airbnb Technology Team, sharing Airbnb's tech innovations and real-world implementations, building a world where home is everywhere through technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.