Skip to content

Commit f91d0e2

Browse files
authored
Add Fragment and View Interop recipe (#226)
* Add Fragment and View Interop recipe This commit introduces a new recipe demonstrating how to use `AndroidFragment` and `AndroidView` within a Navigation3 application. Changes include: * Add `InteropActivity` and `MyCustomFragment` to demonstrate embedding Fragments and Views in Composables. * Add `androidx.fragment:fragment-compose` dependency to version catalog and build file. * Register `InteropActivity` in the manifest and add it to the `RecipePickerActivity` list. * Add `README.md` documenting the new recipe. Fixes #42 * Remove redundant FrameLayout in MyCustomFragment This commit simplifies the view hierarchy in `MyCustomFragment` by removing the unnecessary `FrameLayout` wrapper. Changes: * Update `onCreateView` to return the `TextView` directly.
1 parent 2aa8514 commit f91d0e2

File tree

8 files changed

+125
-0
lines changed

8 files changed

+125
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ Note: If you find a bug or have a feature request for Material3 Adaptive Scenes
6565
- **[Returning Results as Events](app/src/main/java/com/example/nav3recipes/results/event)**: Returning results as events to content in another NavEntry.
6666
- **[Returning Results as State](app/src/main/java/com/example/nav3recipes/results/state)**: Returning results as state stored in a CompositionLocal.
6767

68+
### Interop
69+
- **[Fragment and View Interop](app/src/main/java/com/example/nav3recipes/interop)**: Demonstrates how to use Fragment and Views in Navigation3.
70+
6871
### Future recipes
6972
The most upvoted [recipe requests]([url](https://github.com/android/nav3-recipes/issues?q=is%3Aissue%20state%3Aopen%20label%3Arecipe-request)) will be considered for implementation. Don't see your recipe? [File a request for one here](https://github.com/android/nav3-recipes/issues/new?template=1-recipe-request.md)
7073

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ dependencies {
8080
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
8181
implementation(libs.androidx.material.icons.extended)
8282
implementation(libs.androidx.navigation2)
83+
implementation(libs.androidx.fragment.compose)
8384
implementation(project(":common"))
8485

8586
implementation(libs.hilt.android)

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@
189189
android:exported="true"
190190
android:theme="@style/Theme.Nav3Recipes">
191191
</activity>
192+
<activity
193+
android:name=".interop.InteropActivity"
194+
android:exported="true"
195+
android:theme="@style/Theme.Nav3Recipes">
196+
</activity>
192197
</application>
193198

194199
</manifest>

app/src/main/java/com/example/nav3recipes/RecipePickerActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import com.example.nav3recipes.scenes.twopane.TwoPaneActivity
6666
import com.example.nav3recipes.sharedviewmodel.SharedViewModelActivity
6767
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
6868
import com.example.nav3recipes.deeplink.advanced.AdvancedCreateDeepLinkActivity
69+
import com.example.nav3recipes.interop.InteropActivity
6970

7071
/**
7172
* Activity to show all available recipes and allow users to launch each one.
@@ -118,6 +119,9 @@ private val recipes = listOf(
118119
Heading("Deeplink"),
119120
Recipe("Parse Intent", CreateDeepLinkActivity::class.java),
120121
Recipe("Synthetic BackStack", AdvancedCreateDeepLinkActivity::class.java),
122+
123+
Heading("Interop"),
124+
Recipe("Fragment and View Interop", InteropActivity::class.java),
121125
)
122126

123127
class RecipePickerActivity : ComponentActivity() {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.example.nav3recipes.interop
2+
3+
import android.annotation.SuppressLint
4+
import android.os.Bundle
5+
import android.widget.TextView
6+
import androidx.activity.compose.setContent
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.wrapContentSize
10+
import androidx.compose.material3.Button
11+
import androidx.compose.material3.Text
12+
import androidx.fragment.compose.AndroidFragment
13+
import androidx.compose.ui.Modifier
14+
import androidx.compose.ui.viewinterop.AndroidView
15+
import androidx.fragment.app.FragmentActivity
16+
import androidx.lifecycle.compose.dropUnlessResumed
17+
import androidx.navigation3.runtime.NavKey
18+
import androidx.navigation3.runtime.entryProvider
19+
import androidx.navigation3.runtime.rememberNavBackStack
20+
import androidx.navigation3.ui.NavDisplay
21+
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
22+
import kotlinx.serialization.Serializable
23+
24+
@Serializable
25+
private data object FragmentRoute : NavKey
26+
27+
@Serializable
28+
private data class ViewRoute(val id: String) : NavKey
29+
30+
class InteropActivity : FragmentActivity() {
31+
32+
@SuppressLint("SetTextI18n")
33+
override fun onCreate(savedInstanceState: Bundle?) {
34+
setEdgeToEdgeConfig()
35+
super.onCreate(savedInstanceState)
36+
setContent {
37+
val backStack = rememberNavBackStack(FragmentRoute)
38+
39+
NavDisplay(
40+
backStack = backStack,
41+
onBack = { backStack.removeLastOrNull() },
42+
entryProvider = entryProvider {
43+
entry<FragmentRoute> {
44+
Column(Modifier.fillMaxSize().wrapContentSize()) {
45+
AndroidFragment<MyCustomFragment>()
46+
Button(onClick = dropUnlessResumed {
47+
backStack.add(ViewRoute("123"))
48+
}) {
49+
Text("Go to View")
50+
}
51+
}
52+
}
53+
entry<ViewRoute> { key ->
54+
AndroidView(
55+
modifier = Modifier.fillMaxSize().wrapContentSize(),
56+
factory = { context ->
57+
TextView(context).apply {
58+
text = "My View with key: ${key.id}"
59+
}
60+
}
61+
)
62+
}
63+
}
64+
)
65+
}
66+
}
67+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.example.nav3recipes.interop
2+
3+
import android.annotation.SuppressLint
4+
import android.os.Bundle
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.widget.TextView
9+
import androidx.fragment.app.Fragment
10+
11+
class MyCustomFragment : Fragment() {
12+
@SuppressLint("SetTextI18n")
13+
override fun onCreateView(
14+
inflater: LayoutInflater,
15+
container: ViewGroup?,
16+
savedInstanceState: Bundle?
17+
): View {
18+
return TextView(requireContext()).apply {
19+
text = "My Fragment"
20+
}
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Interop Recipe
2+
3+
This recipe demonstrates how to use `AndroidFragment` and `AndroidView` within a Navigation3 application.
4+
5+
## Features
6+
7+
- **AndroidFragment**: Shows how to embed a Fragment inside a Composable destination.
8+
- **AndroidView**: Shows how to embed a classic Android View inside a Composable destination.
9+
10+
## Key Components
11+
12+
- `InteropActivity`: The main activity hosting the navigation.
13+
- `MyCustomFragment`: A simple Fragment used in the example.
14+
- `AndroidFragment<T>`: A Composable that hosts a Fragment.
15+
- `AndroidView`: A Composable that hosts an Android View.
16+
17+
## Usage
18+
19+
1. Run the `InteropActivity`.
20+
2. The initial screen shows a Fragment.
21+
3. Click "Go to View" to navigate to a screen displaying a `TextView`.

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ lifecycleRuntimeKtx = "2.10.0"
2424
lifecycleViewmodel = "2.10.0"
2525
activityCompose = "1.12.0"
2626
composeBom = "2025.11.00"
27+
fragment = "1.8.9"
2728
navigation2 = "2.9.6"
2829
navigation3 = "1.0.0"
2930
material3 = "1.4.0"
@@ -51,6 +52,7 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3",
5152
androidx-material3-windowsizeclass = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "material3" }
5253
androidx-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout" }
5354
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
55+
androidx-fragment-compose = { module = "androidx.fragment:fragment-compose", version.ref = "fragment" }
5456
androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycleViewmodel" }
5557
androidx-navigation2 = { module = "androidx.navigation:navigation-compose", version.ref = "navigation2" }
5658
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3" }

0 commit comments

Comments
 (0)