3

I am implementing a date picker from my fragment. I want to display the selected date on my fragment.

The date picker dialog fragment:

class DatePickerDialogFragment :  DialogFragment() , DatePickerDialog.OnDateSetListener {


    @NotNull
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        //return super.onCreateDialog(savedInstanceState)
        // Use the current date as the default date in the picker.
        val c: Calendar = Calendar.getInstance()
        val year: Int = c.get(Calendar.YEAR)
        val month: Int = c.get(Calendar.MONTH)
        val day: Int = c.get(Calendar.DAY_OF_MONTH)
        // Create a new instance of DatePickerDialog and return it.
        return DatePickerDialog(requireActivity(), this, year, month, day)
    }


    override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
        val selectedDate = arrayOf<String>(year.toString(),month.toString(),dayOfMonth.toString())
        val action = DatePickerDialogFragmentDirections.actionDatePickerDialogFragmentToDatePickerDemo(month.toString(),year.toString(),dayOfMonth.toString())
        //Navigation.findNavController().navigate(action)
        findNavController().navigate(action)
    }
}

The DatePickerDemo class:

class DatePickerDemo : Fragment() ,View.OnClickListener {
    private var mSelectedMonth: String? = null
    private var mSelectedDay: String? = null
    private var mSelectedYear: String? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_date_picker, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btnShowDate.setOnClickListener(this)
        arguments?.let {
            mSelectedMonth = DatePickerDemoArgs.fromBundle(it).month.toString()
            mSelectedDay = DatePickerDemoArgs.fromBundle(it).day.toString()
            mSelectedYear = DatePickerDemoArgs.fromBundle(it).year.toString()
        }
        if(mSelectedMonth != null && mSelectedDay != null && mSelectedYear != null){
            processDatePickerResult(mSelectedYear!!.toInt(),mSelectedMonth!!.toInt(),mSelectedDay!!.toInt())
        }
    }

    override fun onClick(v: View?) {
        findNavController().navigate(R.id.datePickerDialogFragment)
    }
}

The navigation graph for the fragment and dialog fragment is as follows:

<fragment
        android:id="@+id/datePickerDemo"
        android:name="com.kgandroid.droidcafeuimaster.DatePickerDemo"
        android:label="DatePickerDemo" >
        <action
            android:id="@+id/action_datePickerDemo_to_datePickerDialogFragment"
            app:destination="@id/datePickerDialogFragment" />
        <argument
            android:name="year"
            app:argType="string"
            app:nullable="true"
            android:defaultValue="@null" />
        <argument
            android:name="month"
            app:argType="string"
            app:nullable="true"
            android:defaultValue="@null" />
        <argument
            android:name="day"
            app:argType="string"
            app:nullable="true"
            android:defaultValue="@null" />
    </fragment>
  <dialog
        android:id="@+id/datePickerDialogFragment"
        android:name="com.kgandroid.droidcafeuimaster.DatePickerDialogFragment"
        android:label="fragment_date_picker_dialog"
        tools:layout="@layout/fragment_date_picker_dialog" >
        <action
            android:id="@+id/action_datePickerDialogFragment_to_datePickerDemo"
            app:destination="@id/datePickerDemo" />
    </dialog>

The problem:

As obvious, I don't need the arguments in DatePikerDemo fragment when the fragment is launched. I need it when I am returning from DatePickerDialogFragment and display it as toast. So I have added null value to the arguments as you can see: app:nullable="true" android:defaultValue="@null" in the nav graph. But the problem is, the compiler is showing error:

Too many arguments for public open fun actionDatePickerDialogFragmentToDatePickerDemo

Without the default value the program is compiling but crashing with the error:

 java.lang.IllegalArgumentException: Required argument "year" is missing and does not have an android:defaultValue

which is pretty obvious. So what is the solution for this?

Noticed that this kind of problems occur when communicating from fragments to dilogs and vice-versa through navigation library.

Hawklike
  • 952
  • 16
  • 23
kgandroid
  • 5,507
  • 5
  • 39
  • 69

2 Answers2

3

Your arguments are fine (sample):

<argument
    android:name="month"
    app:argType="string"
    app:nullable="true"
    android:defaultValue="@null" />

Looks like you might declared wrong plugin in gradle.build file for safeargs. You are using Kotlin so use plugin for Kotlin.

Here is the reference to the solution: https://stackoverflow.com/a/60941161/619673

In short:
Go to your build.gradle file and change one of your plugins from the top.
(androidx.navigation.safeargs -> androidx.navigation.safeargs.kotlin):

It should be:

plugins {
    id 'com.android.library'
    id 'org.jetbrains.kotlin.android'
    id 'androidx.navigation.safeargs.kotlin' <---- kotlin
    id 'kotlin-parcelize'
}

And make sure you are using the latest version of the library: https://developer.android.com/jetpack/androidx/releases/navigation#2.5.1

Reference: https://stackoverflow.com/a/69638315/619673

deadfish
  • 11,996
  • 12
  • 87
  • 136
-2

If an argument type supports null values, you can declare a default value of null by using android:defaultValue="@null"

<argument
            android:name="customer"
            app:argType="com.test.CustomerEntity"
            app:nullable="true"
            android:defaultValue="@null"/>

In the Add Argument Link window that appears, enter the argument name, argument type, whether the argument is nullable( app:nullable="true"), and a default value, if needed.

https://developer.android.com/guide/navigation/navigation-pass-data

ViramP
  • 1,659
  • 11
  • 11