I am trying to add a fragment which has implementation done as part of an external framework. Need to include/reference the fragment in the running application. How this can be done?
below is the navigation XML for reference.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main"
app:startDestination="#id/officeFragment">
<fragment
android:id="#+id/officeFragment"
android:name="com.sample.office.OfficeFragment"
android:label="#string/office">
<argument
android:name="officeId"
app:argType="string"
app:nullable="false" />
<action
android:id="#+id/viewEmployees"
app:destination="#id/profileListFragment"/>
</fragment>
<fragment
android:id="#+id/profileListFragment"
android:name="com.externallibrary.component.people.ui.profilelist.ProfileListFragment"
android:label="#string/people">
<argument
android:name="officeId"
android:defaultValue="#null"
app:argType="string"
app:nullable="true" />
</fragment>
</navigation>
Related
val action = LoginFragmentDirections.actionLoginFragmentToHomeFragment(
User(
_binding.emailEt.editText?.text.toString(),
_binding.passwordEt.editText?.text.toString()
)
)
findNavController().navigate(action)
This is my Button press action
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/loginFragment">
<fragment
android:id="#+id/loginFragment"
android:name="com.example.passion.ui.LoginFragment"
android:label="fragment_logins"
tools:layout="#layout/fragment_logins">
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment" />
</fragment>
<fragment
android:id="#+id/homeFragment"
android:name="com.example.passion.ui.HomeFragment"
android:label="fragment_home"
tools:layout="#layout/fragment_home" >
<argument
android:name="user"
app:argType="com.example.passion.data.models.User"
app:nullable="true"
android:defaultValue="#null" />
</fragment>
</navigation>
This is my navigation graph
Everytime I press button. it adds the fragment. I want to replace it with HomeFragment
Any help would be appreciated!. thanks
I have the below code for my navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/navigation_home">
<fragment
android:id="#+id/navigation_notifications"
android:name="com.elyeproj.bottombarfragmentsswitching.ContainerFragment"
android:label="#string/title_notifications"
tools:layout="#layout/fragment_container" >
<argument
android:name="FragmentKey"
app:argType="string"
android:defaultValue="#string/title_notifications" />
<argument
android:name="FragmentColor"
app:argType="string"
android:defaultValue="#00FFFF" />
</fragment>
</navigation>
Where #string/title_notifications will return Notifications
However, it errors out at the android:defaultValue="#string/title_notifications"
<argument
android:name="FragmentKey"
app:argType="string"
android:defaultValue="#string/title_notifications" />
The error is
Caused by: org.xmlpull.v1.XmlPullParserException: unsupported value 'Notifications' for string. You must use a "reference" type to reference other resources.
The #string/title_notifications definitely has value Notifications, as it is used fine in the fragment tag.
If I convert to
<argument
android:name="FragmentKey"
app:argType="string"
android:defaultValue="Notifications" />
Then it compiles fine.
Why can't the argument tag use String resource?
Looks like I have to use the reference type instead. I cannot mix String and Reference
<argument
android:name="FragmentKey"
app:argType="reference"
android:defaultValue="#string/title_notifications" />
I have the problem that my sample app crashes when I deeplink from one graph part to the other one.
My structure is simple:
Two tabs with own graphs which have each two possible fragments
When I navigate to a detail page (in my sample a book) this just works from my "Shelf" but not from the home screen.
Here is my full graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/sample"
app:startDestination="#id/tab_home">
<navigation
android:id="#+id/tab_home"
app:startDestination="#id/frag_home">
<fragment
android:id="#+id/frag_home"
android:name="eu.rekisoft.android.navbug.HomeFragment"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/cause_bug"
app:destination="#id/frag_hint"/>
<action
android:id="#+id/causeCrash"
app:destination="#id/frag_book">
<argument
android:name="name"
app:argType="string"/>
</action>
</fragment>
<fragment
android:id="#+id/frag_hint"
android:name="eu.rekisoft.android.navbug.HintFragment"
android:label="Hint"
tools:layout="#layout/fragment_hint"/>
</navigation>
<navigation
android:id="#+id/tab_books"
app:startDestination="#id/frag_shelf">
<fragment
android:id="#+id/frag_shelf"
android:name="eu.rekisoft.android.navbug.ShelfFragment"
android:label="Shelf Fragment"
tools:layout="#layout/fragment_shelf">
<action
android:id="#+id/open_book"
app:destination="#id/frag_book">
<argument
android:name="name"
app:argType="string"/>
</action>
</fragment>
<fragment
android:id="#+id/frag_book"
android:name="eu.rekisoft.android.navbug.BookFragment"
android:label="Book Fragment"
tools:layout="#layout/fragment_book">
<argument
android:name="name"
app:argType="string"/>
</fragment>
</navigation>
</navigation>
Here is my crash:
java.lang.IllegalArgumentException: Navigation destination eu.rekisoft.android.navbug:id/frag_book referenced from action eu.rekisoft.android.navbug:id/causeCrash cannot be found from the current destination Destination(eu.rekisoft.android.navbug:id/frag_home) class=eu.rekisoft.android.navbug.HomeFragment
at androidx.navigation.NavController.navigate(NavController.kt:1527)
at androidx.navigation.NavController.navigate(NavController.kt:1464)
at androidx.navigation.NavController.navigate(NavController.kt:1922)
at eu.rekisoft.android.navbug.HomeFragment.onViewCreated$lambda-2$lambda-1(HomeFragment.kt:23)
at eu.rekisoft.android.navbug.HomeFragment.$r8$lambda$q53N5QXeJ-lbbpd7MjwkcKbt9_4(Unknown Source:0)
at eu.rekisoft.android.navbug.HomeFragment$$ExternalSyntheticLambda1.onClick(Unknown Source:2)
Very clear error message, but I don't know how to fix this.
If you want to checkout my code check this GitHub repo.
I am using the navigation component from Jetpack.
My main graph is huge, so I have decided to split it into different graphs and use the <include /> functionality.
Problem is that some of the included graphs receive arguments, but Android seems to ignore the arguments when creating the navigation Directions
Ie:
Main Graph (simplified)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<fragment
android:id="#+id/listFragment"
android:name="com......ListFragment">
<action
android:id="#+id/view_detail"
app:destination="#id/item_detail"/>
</fragment>
<include app:graph="#navigation/included_graph"/>
</navigation>
Included graph: (simplified)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_detail"
app:startDestination="#id/itemDetailFragment">
<argument
android:name="item_id"
app:argType="integer" />
<fragment
android:id="#+id/itemDetailFragment"
android:name="com......ItemDetailFragment">
<argument
android:name="item_id"
app:argType="integer" />
</fragment>
[...]
</navigation>
The class generated by android is
class ListFragmentDirections private constructor() {
companion object {
fun viewDetail(): NavDirections =
ActionOnlyNavDirections(R.id.view_detail)
}
}
It seems the argument received by the included graph is ignored - I can't pass any argument safely.
Is there are workaround for this?
I'm interested in any alternative letting me breaking down the graph into smaller files, based on <include /> or not.
There is a workaround: include the argument to the action explicitly, i.e.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<fragment
android:id="#+id/listFragment"
android:name="com......ListFragment">
<action
android:id="#+id/view_detail"
app:destination="#id/item_detail">
<argument
android:name="item_id"
app:argType="integer" />
</action>
</fragment>
<include app:graph="#navigation/included_graph"/>
</navigation>
I have this in app gradle:
apply plugin: 'androidx.navigation.safeargs'
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0'
and this in the project gradle:
classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
the navigation graph:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/navigation_graph"
app:startDestination="#id/loginPhoneNumberFragment">
<fragment
android:id="#+id/loginPhoneNumberFragment"
android:name="...fragments.LoginPhoneNumberFragment"
android:label="#string/login_activity_title"
tools:layout="#layout/fragment_login_phone_number">
<action
android:id="#+id/action_loginPhoneNumberFragment_to_loginCodeFragment"
app:destination="#id/loginCodeFragment">
<argument
android:name="prefix"
app:argType="string" />
<argument
android:name="phone_number"
app:argType="string" />
</action>
</fragment>
<fragment
android:id="#+id/loginCodeFragment"
android:name="...LoginCodeFragment"
android:label="#string/login_activity_title"
tools:layout="#layout/fragment_login_code" />
</navigation>
LoginPhoneNumberFragment:
val action = LoginPhoneNumberFragmentDirections.actionLoginPhoneNumberFragmentToLoginCodeFragment(prefix, phoneNumber)
view?.findNavController()?.navigate(action)
LoginCodeFragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val prefix = LoginCodeFragmentArgs.fromBundle(arguments).prefix //LoginCodeFragmentArgs is not recognized
}
In the LoginPhoneNumberFragment it creates the "LoginPhoneNumberFragmentDirections" class, but on the destination class, LoginCodeFragment, it doesn't recognize "LoginCodeFragmentArgs".
Can someone please tell me what am I missing?
(I cleaned and rebuilded, and tried Invalidate caches...)
Ok, so after a lot of searching I found out my mistake -
the arguments should be on the Destination fragment, and not on the starting one:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/navigation_graph"
app:startDestination="#id/loginPhoneNumberFragment">
<fragment
android:id="#+id/loginPhoneNumberFragment"
android:name="...fragments.LoginPhoneNumberFragment"
android:label="#string/login_activity_title"
tools:layout="#layout/fragment_login_phone_number">
<action
android:id="#+id/action_loginPhoneNumberFragment_to_loginCodeFragment"
app:destination="#id/loginCodeFragment">
</action>
</fragment>
<fragment
android:id="#+id/loginCodeFragment"
android:name="...fragments.LoginCodeFragment"
android:label="#string/login_activity_title"
tools:layout="#layout/fragment_login_code" >
<argument
android:name="prefix"
app:argType="string"
android:defaultValue="888" />
<argument
android:name="phone_number"
app:argType="string"
android:defaultValue="88888888"/>
</fragment>
</navigation>
You can also add it manually via the navigation graph design - press on the destination fragment and press "+" on the arguments section, it will add it to the text file.
The argument should be in the destination fragment as follows instead of inside the action in source fragment
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/navigation_graph"
app:startDestination="#id/loginPhoneNumberFragment">
<fragment
android:id="#+id/loginPhoneNumberFragment"
android:name="...fragments.LoginPhoneNumberFragment"
android:label="#string/login_activity_title"
tools:layout="#layout/fragment_login_phone_number">
<action
android:id="#+id/action_loginPhoneNumberFragment_to_loginCodeFragment"
app:destination="#id/loginCodeFragment"/>
</fragment>
<fragment
android:id="#+id/loginCodeFragment"
android:name="...LoginCodeFragment"
android:label="#string/login_activity_title"
tools:layout="#layout/fragment_login_code">
<argument
android:name="prefix"
app:argType="string" />
<argument
android:name="phone_number"
app:argType="string" />
</fragment>
</navigation>