Refactor: Update package structure and implement bottom navigation

This commit refactors the package structure for Gradle convention plugins and introduces bottom navigation to the application.

Key changes:
- Moved Gradle convention plugin files from `dev.adriankuta.partymania` to `dev.adriankuta.flights`.
- Added `TopLevelDestination.kt` to define top-level navigation destinations with icons, titles, and routes.
- Implemented `FlightsBottomBar` Composable in `FlightsApp.kt` to display a `NavigationBar` with items for each `TopLevelDestination`.
- Updated `FlightsNavGraph.kt`:
    - Renamed from `FlightsNavGraph.kt` to `navigation/FlightsNavGraph.kt`.
    - Added `navigateToTopLevelDestination` extension function for `NavController` to handle navigation to top-level destinations with appropriate `NavOptions`.
- Updated `HomeNavigation.kt`:
    - Added `navigateToHome` extension function for `NavController`.
- Added `strings.xml` for `ui:home` module with `home_screen_title`.
- Ensured Kotlin serialization plugin is correctly applied in `app/build.gradle.kts`.
This commit is contained in:
2025-06-15 22:09:05 +02:00
parent 762c6338de
commit 13348bc52f
19 changed files with 144 additions and 26 deletions

View File

@ -1,7 +1,7 @@
plugins {
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.flights.android.application.compose)
alias(libs.plugins.flights.android.application.hilt)
alias(libs.plugins.kotlin.serialization)
}
android {

View File

@ -1,23 +0,0 @@
package dev.adriankuta.flights
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import dev.adriankuta.flights.ui.home.navigation.HomeRoute
import dev.adriankuta.flights.ui.home.navigation.homeScreen
@Composable
fun FlightsNavGraph(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
) {
NavHost(
navController = navController,
startDestination = HomeRoute,
modifier = modifier,
) {
homeScreen()
}
}

View File

@ -0,0 +1,51 @@
package dev.adriankuta.flights.navigation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.util.trace
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import dev.adriankuta.flights.ui.home.navigation.HomeRoute
import dev.adriankuta.flights.ui.home.navigation.homeScreen
import dev.adriankuta.flights.ui.home.navigation.navigateToHome
@Composable
fun FlightsNavGraph(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
) {
NavHost(
navController = navController,
startDestination = HomeRoute,
modifier = modifier,
) {
homeScreen()
}
}
fun NavController.navigateToTopLevelDestination(topLevelDestination: TopLevelDestination) {
trace("Navigation: ${topLevelDestination.name}") {
val topLevelNavOptions = navOptions {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
when (topLevelDestination) {
TopLevelDestination.HOME -> navigateToHome(topLevelNavOptions)
TopLevelDestination.STATIONS -> navigateToHome(topLevelNavOptions)
}
}
}

View File

@ -0,0 +1,28 @@
package dev.adriankuta.flights.navigation
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Place
import androidx.compose.material.icons.outlined.Search
import androidx.compose.ui.graphics.vector.ImageVector
import dev.adriankuta.flights.ui.home.navigation.HomeRoute
import kotlin.reflect.KClass
import dev.adriankuta.flights.ui.home.R as homeR
enum class TopLevelDestination(
val icon: ImageVector,
@StringRes val titleTextId: Int,
val route: KClass<*>,
val baseRoute: KClass<*> = route,
) {
HOME(
icon = Icons.Outlined.Search,
titleTextId = homeR.string.home_screen_title,
route = HomeRoute::class,
),
STATIONS(
icon = Icons.Outlined.Place,
titleTextId = homeR.string.home_screen_title,
route = HomeRoute::class,
),
}

View File

@ -1,25 +1,75 @@
package dev.adriankuta.flights.ui
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarDefaults
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import dev.adriankuta.flights.FlightsNavGraph
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import dev.adriankuta.flights.navigation.FlightsNavGraph
import dev.adriankuta.flights.navigation.TopLevelDestination
import dev.adriankuta.flights.navigation.navigateToTopLevelDestination
import dev.adriankuta.flights.ui.designsystem.theme.Elevation
@Composable
fun FlightsApp(
modifier: Modifier = Modifier,
) {
val navController = rememberNavController()
Surface(
tonalElevation = Elevation.Surface,
modifier = modifier,
) {
Scaffold(
snackbarHost = { InAppUpdates() },
bottomBar = {
FlightsBottomBar(
navController = navController,
)
},
) { paddingValues ->
FlightsNavGraph(Modifier.padding(paddingValues))
FlightsNavGraph(
navController = navController,
modifier = Modifier.padding(paddingValues),
)
}
}
}
@Composable
internal fun FlightsBottomBar(
navController: NavHostController = rememberNavController(),
) {
var selectedDestination by rememberSaveable { mutableIntStateOf(0) }
NavigationBar(windowInsets = NavigationBarDefaults.windowInsets) {
TopLevelDestination.entries.forEachIndexed { index, destination ->
NavigationBarItem(
selected = selectedDestination == index,
onClick = {
selectedDestination = index
navController.navigateToTopLevelDestination(TopLevelDestination.HOME)
},
icon = {
Icon(
destination.icon,
contentDescription = null,
)
},
label = { Text(stringResource(destination.titleTextId)) },
)
}
}
}

View File

@ -2,7 +2,9 @@
package dev.adriankuta.flights.ui.home.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import dev.adriankuta.flights.ui.home.HomeScreen
import kotlinx.serialization.Serializable
@ -10,6 +12,12 @@ import kotlinx.serialization.Serializable
@Serializable
data object HomeRoute
fun NavController.navigateToHome(
navOptions: NavOptions,
) {
navigate(route = HomeRoute, navOptions = navOptions)
}
fun NavGraphBuilder.homeScreen() {
composable<HomeRoute> {
HomeScreen()

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="home_screen_title">Search Flight</string>
</resources>