ComposableFragmentNavigator.kt
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.navigation.fragment.compose
import android.content.Context
import android.util.AttributeSet
import androidx.compose.runtime.Composable
import androidx.navigation.NavArgument
import androidx.navigation.NavDestination
import androidx.navigation.NavType
import androidx.navigation.Navigator
import androidx.navigation.NavigatorProvider
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.get
/**
* This Navigator intercepts the inflation of `navigation` destinations
* in a Navigation with Fragment XML file, reusing the `android:name`
* field as the fully qualified name of the composable function to use
* as the contents of the inflated destination.
*
* Internally, this uses a [ComposableFragment] to implement the
* reflection call.
*/
@Navigator.Name("composable")
class ComposableFragmentNavigator(
private val fragmentNavigator: FragmentNavigator
) : Navigator<FragmentNavigator.Destination>() {
/**
* Construct a [ComposableFragmentNavigator] by retrieving the associated
* [FragmentNavigator] from [provider].
*/
constructor(
provider: NavigatorProvider
) : this(provider[FragmentNavigator::class])
override fun createDestination(): FragmentNavigator.Destination {
// Note how we associate the destination with the given
// fragmentNavigator - not this Navigator. This ensures
// that the default FragmentNavigator controls the actual
// navigate / popBackStack calls, not us.
return Destination(fragmentNavigator)
}
@NavDestination.ClassType(Composable::class)
internal class Destination(
fragmentNavigator: Navigator<out FragmentNavigator.Destination>
) : FragmentNavigator.Destination(fragmentNavigator) {
override fun onInflate(context: Context, attrs: AttributeSet) {
super.onInflate(context, attrs)
// The className that was parsed out is actually the fully
// qualified name of the Composable Function to run, so extract
// that and add it as a default argument on the destination
val fullyQualifiedName = className
val navArgument = NavArgument.Builder()
.setType(NavType.StringType)
.setDefaultValue(fullyQualifiedName)
.build()
addArgument(ComposableFragment.FULLY_QUALIFIED_NAME, navArgument)
// And then ensure that the actual Fragment that is constructed
// is our ComposableFragment
setClassName(ComposableFragment::class.java.name)
}
}
}