REDI-93: host the Views list in the Compose NavHost (Compose<->View interop)
:app now owns the cross-toolkit interop. MainActivity becomes a FragmentActivity and hosts CharacterListFragment inside the Compose NavHost via AndroidFragment at a new @Serializable CharactersViewsRoute (defined in :app, since :app owns interop — the Views module stays nav-agnostic). The overflow menu's 'Open as Views' / 'About' entries and the Views item-click -> detail navigation are all injected as callbacks, keeping the renderers decoupled. Assemble aboutPresentationModule in Koin; add androidx.fragment:fragment-compose. The Material3 Activity theme styles both toolkits.
This commit is contained in:
@@ -2,6 +2,8 @@ plugins {
|
|||||||
alias(libs.plugins.architecture.android.application)
|
alias(libs.plugins.architecture.android.application)
|
||||||
alias(libs.plugins.architecture.compose)
|
alias(libs.plugins.architecture.compose)
|
||||||
alias(libs.plugins.architecture.koin)
|
alias(libs.plugins.architecture.koin)
|
||||||
|
// For the @Serializable CharactersViewsRoute (Compose↔View interop destination).
|
||||||
|
alias(libs.plugins.architecture.kotlinx.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -16,16 +18,23 @@ dependencies {
|
|||||||
implementation(project(":core:data"))
|
implementation(project(":core:data"))
|
||||||
implementation(project(":core:design-system"))
|
implementation(project(":core:design-system"))
|
||||||
|
|
||||||
// Characters feature: data + presentation (Koin modules) + Compose renderer (nav graph).
|
// Characters feature: data + presentation (Koin modules) + both renderers (Compose nav graph,
|
||||||
|
// Views Fragment hosted via interop).
|
||||||
implementation(project(":feature:characters:data"))
|
implementation(project(":feature:characters:data"))
|
||||||
implementation(project(":feature:characters:presentation"))
|
implementation(project(":feature:characters:presentation"))
|
||||||
implementation(project(":feature:characters:presentation-compose"))
|
implementation(project(":feature:characters:presentation-compose"))
|
||||||
|
implementation(project(":feature:characters:presentation-views"))
|
||||||
|
|
||||||
|
// About feature (MVVM contrast).
|
||||||
|
implementation(project(":feature:about:presentation"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.bundles.lifecycle.compose)
|
implementation(libs.bundles.lifecycle.compose)
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
|
// Compose↔View interop: hosts a Fragment inside the Compose NavHost.
|
||||||
|
implementation(libs.androidx.fragment.compose)
|
||||||
// Material Components — required for the Material3 XML Activity theme.
|
// Material Components — required for the Material3 XML Activity theme.
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
// Logging — the DebugTree is planted here; other modules log via Timber's static API.
|
// Logging — the DebugTree is planted here; other modules log via Timber's static API.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.example.architecture
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.example.architecture.core.data.di.coreDataModule
|
import com.example.architecture.core.data.di.coreDataModule
|
||||||
|
import com.example.architecture.feature.about.presentation.di.aboutPresentationModule
|
||||||
import com.example.architecture.feature.characters.data.di.charactersDataModule
|
import com.example.architecture.feature.characters.data.di.charactersDataModule
|
||||||
import com.example.architecture.feature.characters.presentation.di.charactersPresentationModule
|
import com.example.architecture.feature.characters.presentation.di.charactersPresentationModule
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
@@ -31,6 +32,8 @@ class ArchitectureApp : Application() {
|
|||||||
// characters feature
|
// characters feature
|
||||||
charactersDataModule,
|
charactersDataModule,
|
||||||
charactersPresentationModule,
|
charactersPresentationModule,
|
||||||
|
// about feature (MVVM contrast)
|
||||||
|
aboutPresentationModule,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.example.architecture
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route for the characters list rendered with the classic **Views** toolkit. It lives in `:app`
|
||||||
|
* because `:app` owns Compose↔View interop — the `:feature:characters:presentation-views` module
|
||||||
|
* stays navigation-agnostic (it knows nothing about Compose Navigation or this route).
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data object CharactersViewsRoute
|
||||||
@@ -1,16 +1,30 @@
|
|||||||
package com.example.architecture
|
package com.example.architecture
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.compose.AndroidFragment
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.example.architecture.core.design.system.theme.AppTheme
|
import com.example.architecture.core.design.system.theme.AppTheme
|
||||||
|
import com.example.architecture.feature.about.presentation.AboutRoute
|
||||||
|
import com.example.architecture.feature.about.presentation.aboutGraph
|
||||||
|
import com.example.architecture.feature.characters.presentation.compose.CharacterDetailRoute
|
||||||
import com.example.architecture.feature.characters.presentation.compose.CharacterListRoute
|
import com.example.architecture.feature.characters.presentation.compose.CharacterListRoute
|
||||||
import com.example.architecture.feature.characters.presentation.compose.charactersGraph
|
import com.example.architecture.feature.characters.presentation.compose.charactersGraph
|
||||||
|
import com.example.architecture.feature.characters.presentation.views.CharacterListFragment
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
/**
|
||||||
|
* Hosts the single Compose NavHost and owns every cross-feature / cross-toolkit wiring:
|
||||||
|
* - the characters graph (Compose list + detail),
|
||||||
|
* - the About graph (MVVM contrast),
|
||||||
|
* - the Views renderer embedded via [AndroidFragment] (Compose↔View interop).
|
||||||
|
*
|
||||||
|
* Extends [FragmentActivity] (not plain ComponentActivity) so [AndroidFragment] has a FragmentManager.
|
||||||
|
*/
|
||||||
|
class MainActivity : FragmentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
@@ -22,8 +36,23 @@ class MainActivity : ComponentActivity() {
|
|||||||
startDestination = CharacterListRoute,
|
startDestination = CharacterListRoute,
|
||||||
) {
|
) {
|
||||||
charactersGraph(
|
charactersGraph(
|
||||||
onCharacterClick = { /* Detail navigation is wired in the next milestone. */ },
|
navController = navController,
|
||||||
|
onOpenAbout = { navController.navigate(AboutRoute) },
|
||||||
|
onOpenViewsList = { navController.navigate(CharactersViewsRoute) },
|
||||||
)
|
)
|
||||||
|
aboutGraph(
|
||||||
|
onNavigateBack = { navController.popBackStack() },
|
||||||
|
)
|
||||||
|
// Compose↔View interop: the same characters list, rendered by a Fragment. :app
|
||||||
|
// injects the navigation callbacks so the Views module stays nav-agnostic.
|
||||||
|
composable<CharactersViewsRoute> {
|
||||||
|
AndroidFragment<CharacterListFragment> { fragment ->
|
||||||
|
fragment.onCharacterClick = { id ->
|
||||||
|
navController.navigate(CharacterDetailRoute(id))
|
||||||
|
}
|
||||||
|
fragment.onNavigateBack = { navController.popBackStack() }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user