Quick Start Guide
Here's the improved version of your documentation text:
From this tutorial, you will learn how to start using Modo. You will learn how to:
Create a
Screen
and integrate it into an activityPerform basic navigation operations
Pass arguments to screens
Customize
StackScreen
animation and content
At the end of this tutorial, you will have a simple application with a single screen and basic navigation.
Before You Start
Make sure that:
You have Android Studio and the Android SDK installed
You know the basics of Android development, such as creating activities and fragments, and can launch an application.
Build Your First Modo Application with a Single Screen
Setup Dependencies
In your module-level build.gradle(.kts)
file, add the Modo Compose dependency:
Create Your First Screen
To display UI using Modo, you need to create a Screen
implementation. Let's create a simple QuickStartScreen
and implement it step-by-step:
Create a new Kotlin file
QuickStartScreen.kt
:// TODO: provide Parcelable implementation class QuickStartScreen( // TODO: implement screenKey ) : Screen { @Composable override fun Content(modifier: Modifier) { // TODO: implement your UI } }Override the
Content
function to provide the UI for your screen. Don't forget to usemodifier
in your root element:@Composable override fun Content(modifier: Modifier) { Box(modifier = modifier) { Text( text = "Hello, Modo!", modifier = Modifier.align(Alignment.Center) ) } }Implement the
Parcelable
interface for yourScreen
. It's needed to support saving and restoring the screen's structure. You can easily do it by using the parcelable gradle plugin and the@Parcelize
annotation:@Parcelize class QuickStartScreen( ... ) : Screen { ... }Implement the
screenKey
property using thegenerateScreenKey()
function. It's crucial to use this function because it generates a unique key for each screen:@Parcelize class QuickStartScreen( // You need to generate a unique screen key using a special function override val screenKey: ScreenKey = generateScreenKey() ) : Screen { ... }
Complete code:
Integrate the Screen into the Activity
Now that we've created QuickStartScreen
, let's integrate it into an activity.
Create a new activity
QuickStartActivity
and add the following code:class QuickStartActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { // TODO: integrate screen } } }Integrate the
QuickStartScreen
into the activity. To do so, use therememberRootScreen
function to create a root screen that will manage navigation:setContent { val rootScreen = rememberRootScreen { QuickStartScreen() } // TODO: display the root screen }Display the root screen using the
Content
function of the root screen:setContent { val rootScreen = rememberRootScreen { QuickStartScreen() } // Pass fillMaxSize modifier to display it in full screen rootScreen.Content(Modifier.fillMaxSize()) }
Complete code:
Run the Application!
Perform Basic Navigation Operations
To perform navigation, let's use a StackScreen
. It's a container screen that can contain multiple child screens and renders the top one.
Use the
DefaultStackScreen
in your activity:setContent { val rootScreen = rememberRootScreen { DefaultStackScreen( // Use StackNavModel to define the initial screen StackNavModel( QuickStartScreen() ) ) } rootScreen.Content(modifier = Modifier.fillMaxSize()) }Let's create
QuickStartScreenContent
, with a title and a button that will navigate to another screen:@Composable private fun QuickStartScreenContent( modifier: Modifier, openNextScreen: () -> Unit, ) { Column( modifier = modifier, verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Hello, Modo!") Button( onClick = openNextScreen ) { Text(text = "Next screen") } } }To navigate to the next screen, we need to retrieve the
StackNavContainer
. You can get the nearestStackNavContainer
from theContent
function using theLocalStackNavigation
composition local. In our case, it's going to beDefaultStackScreen
that we defined earlier:@Composable override fun Content(modifier: Modifier) { val stackNavigation = LocalStackNavigation.current QuickStartScreenContent( modifier = modifier, openNextScreen = { // TODO: navigate using stackNavigation }, ) }To navigate to the next screen, use the
forward
function of theStackNavContainer
and pass a new screen to it:QuickStartScreenContent( modifier = modifier, openNextScreen = { stackNavigation.forward(QuickStartScreen()) }, )Launch the application and check the result! Try to navigate to the next screen by clicking the button and navigate back using the back button.
Complete code:
Pass Arguments to Screens
To pass arguments to the screen, you can use the constructor argument.
Let's add a screen index to the
QuickStartScreen
:@Parcelize class QuickStartScreen( private val screenIndex: Int, override val screenKey: ScreenKey = generateScreenKey() ) : Screen { ... }Use the
screenIndex
argument in theContent
function to display it:@Composable override fun Content(modifier: Modifier) { val stackNavigation = LocalStackNavigation.current QuickStartScreenContent( modifier = modifier, screenIndex = screenIndex, openNextScreen = { stackNavigation.forward(QuickStartScreen(screenIndex + 1)) }, ) }Use the
screenIndex
in theQuickStartScreenContent
:@Composable private fun QuickStartScreenContent( modifier: Modifier, screenIndex: Int, openNextScreen: () -> Unit, ) { Column( modifier = modifier, verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Hello, Modo! Screen №$screenIndex") Button( onClick = openNextScreen ) { Text(text = "Next screen") } } }
Customize StackScreen Animation and Content
Before, we used the built-in DefaultStackScreen
that implements StackScreen
and provides default animation and content. Let's take DefaultStackScreen
as a starting point and customize it.
Create a Custom StackScreen
Copy the DefaultStackScreen
implementation and change its name to QuickStartStackScreen
:
Everything is quite similar to declaring a Screen
. Let's look at the differences:
private val navModel: StackNavModel
in the constructor is used to store and update the state of the navigation. Declaring it as a constructor parameter allows it to be saved and restored using the@Parcelize
annotation. Don't forget to pass it to theStackScreen
's constructor.TopScreenContent(modifier)
is used to render the last screen in the stack.ScreenTransition(modifier)
insideTopScreenContent
is used to animate the transition between screens.
Custom Animation
Let's customize the animation a little bit. You can do it by passing
transitionSpec
as aScreenTransition
parameter:ScreenTransition( modifier = modifier, transitionSpec = {Inside the
transitionSpec
lambda, we have access to theComposeRendererScope
that contains the old and new states. Let's use the built-incalculateStackTransitionType
to determine the StackTransitionType:transitionSpec = { val screenTransitionType = calculateStackTransitionType()Now we can use
StackTransitionType
to define the animation. Let's useslideInHorizontally
andslideOutHorizontally
with different parameters for the forward and back transitions:val screenTransitionType = calculateStackTransitionType() when (screenTransitionType) { StackTransitionType.Push -> { slideInHorizontally(initialOffsetX = { it }) togetherWith slideOutHorizontally(targetOffsetX = { -it }) } StackTransitionType.Pop -> { slideInHorizontally(initialOffsetX = { -it }) togetherWith slideOutHorizontally(targetOffsetX = { it }) } StackTransitionType.Replace, StackTransitionType.Idle -> fadeIn() togetherWith fadeOut() } }
Customize QuickStartStackScreen Content
You can customize Content
as a regular composable function. Let's modify QuickStartStackScreen
. Draw the background and make screens render with rounded corners:
You can pass a modifier to the
TopScreenContent
function to change it:TopScreenContent( Modifier .background(Color.Cyan) .padding(16.dp) .clip(shape = RoundedCornerShape(32.dp)) .fillMaxSize() .background(Color.White) ) { screenModifier -> }You can also put the
TopScreenContent
inside another container to customize it. Let's put it inside aBox
and add buttons to close the activity:Box(modifier = modifier) { TopScreenContent( ) { screenModifier -> } val context = LocalContext.current IconButton( modifier = Modifier .align(Alignment.TopEnd) .padding(8.dp) .clip(CircleShape) .background(Color.White), onClick = { context.getActivity()?.finish() } ) { Icon( painter = rememberVectorPainter(image = Icons.Default.Close), contentDescription = "Close quick start activity" ) } }
Complete code:
What You've Learned
From this tutorial, you've learned how to build a simple application with Modo and customize it for your needs. For further learning, you can explore the Sample app.