Home > Back-end >  How to give context to fragment in Kotlin?
How to give context to fragment in Kotlin?

Time:01-28

I am really noob into kotlin and I was trying to implement applandeo calendar library o my project in kotlin. Everything works well if you use activities but when changing into fragments I don't know how to give context because "this" is not working as a Context. In te function openDatePicker() the first parameter should be the context, but no idea about how to get it.

Also I don't know if its possible to pass from a fragment to an activity. My project is structured as a main activity with a bottom navigation bar where every elements redirects to the fragment. This code is inside one of those fragments. Any help or idea will be great ! :)

class CalendarFragment : Fragment(), OnDayClickListener, OnSelectDateListener{

private lateinit var binding: CalendarViewFragmentBinding
private val notes = mutableMapOf<EventDay, String>()
private lateinit var appContext: Context

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    val context = this.context
    // Inflate the layout for this fragment
    val calendar_view = inflater.inflate(R.layout.calendar_view_fragment, container, false)
    binding = CalendarViewFragmentBinding.inflate(layoutInflater)
    binding.fabButton.setOnClickListener { openDatePicker() }
    binding.calendarView.setOnDayClickListener(this)
    return calendar_view
}

private fun openDatePicker() {
    DatePickerBuilder(************, this)
        .pickerType(CalendarView.ONE_DAY_PICKER)
        .headerColor(R.color.md_theme_light_primary)
        .todayLabelColor(R.color.md_theme_light_primary)
        .selectionColor(R.color.md_theme_light_secondary)
        .dialogButtonsColor(R.color.md_theme_light_secondary)
        .build()
        .show()
}

I tried functions such as requireContext(), requireActivity(), requireContext().applicationContext, this.context, but no one working as I expect.

CodePudding user response:

Just use it from Fragment. Here is the documentation

private fun openDatePicker() {
    DatePickerBuilder(requireContext(), this)
        .pickerType(CalendarView.ONE_DAY_PICKER)
        .headerColor(R.color.md_theme_light_primary)
        .todayLabelColor(R.color.md_theme_light_primary)
        .selectionColor(R.color.md_theme_light_secondary)
        .dialogButtonsColor(R.color.md_theme_light_secondary)
        .build()
        .show()
}

CodePudding user response:

Your Fragment is attached to its context when its onAttach() callback is invoked. This happens before it reaches the CREATED lifecycle state:

When your fragment reaches the CREATED state, it has been added to a FragmentManager and the onAttach() method has already been called.

What this means is that by the time onCreate is called (or any lifecycle callbacks after that, including onCreateView, onViewCreated, onStart etc.) your fragment will have a context, and you can access it using requireContext().

You could also use getContext(), which you can access as a property with context, but that returns null if the Fragment isn't associated with a context yet - meaning you have to null-check and handle that possibility. requireContext() will throw an exception if you don't have that context yet, but if you're making sure to only call it when the fragment is in the CREATED state (or later) then it will be safe, and you won't need to check the return value. This is the recommended way of doing things.


So as long as you're accessing the Context in a lifecycle callback like onCreateView, requireContext() will work - that's how you get the Fragment's context. Because you're calling openDatePicker from inside onCreateView, you can just use requireContext() in that function - it's safe at that point!

But you can't define it as a normal top-level variable like this:

class MyFragment : Fragment {
    var appContext: Context = requireContext()
}

because that variable is assigned at construction time, which happens way before the fragment reaches the CREATED state. You don't have access to the context at construction time, so any top-level stuff that requires it will end up throwing an exception. This goes for Activities too! That's why you have to assign stuff later, like in onCreate (and this is where lateinit comes in useful, you can have a top-level variable without having to assign it before you're ready)

And no, you can't pass this as a Context when you're in a Fragment, because a Fragment isn't a Context. It works for an Activity because that is a Context - to be more accurate it's able to provide one, but that only works after it's in the CREATED state. That's why you can get in trouble using it in top-level declarations like this

class MyActivity : AppCompatActivity {
    val thing = ThingThatRequiresContext(this)
}

because like we just talked about, at construction time the Activity doesn't have access to a context, and the thing that requires it (if it tries to access it immediately) will end up throwing an exception. You see that error a lot, where an Activity can't be started because something like this is happening during initialisation.

  • Related