Passing data between fragments via adapter (Kotlin Android)

Viewed 962

Very new to coding ~1 month. Apologies if this is answered elsewhere, but spent hours looking through other similar questions but cant get it quite right. What: Trying to pass the position click data from fragment 1 (MainFragment) to fragment 2 (MapsFragment) to load the corresponding map. Goes through the adapter onclick method because I want to use RecyclerView on MainFragment. Project is buildable but crashes when I click an item on recyclerview (cant open map). Whats wrong????

Thanks ahead of time!!

EDIT: ANSWERED Thank you everyone for comments. Was able to solve this by 1) changing lateinit var on mapsfragment to a regular var + nullable, and 2) changing my bundle line in the main fragment to

val bundle = bundleOf(EXTRA_USER_MAP to sports[position])
view.findNavController(.navigate(R.id.action_mainFragment_to_mapsFragment, bundle)

woohoo Kotlin!

Main Fragment:

const val EXTRA_USER_MAP = "EXTRA_USER_MAP"
private const val TAG = "MainFragment"

class MainFragment : Fragment() {

override fun onCreateView ... // code to inflate my fragment view

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val sports = generateData()

        myMaps.layoutManager = LinearLayoutManager(activity)

        myMaps.adapter = this.context?.let {
            MapsAdapter(it, sports, object: MapsAdapter.OnClickListener {
                override fun onItemClick(position: Int) {
                    Log.i(TAG, "onItemClick $position")

                    // When user taps on view in recyclerview, navigate to new fragment and opens corresponding map
                    val bundle =  Bundle()
                    bundle.putSerializable(EXTRA_USER_MAP, sports[position])

                    view.findNavController().navigate(R.id.action_mainFragment_to_mapsFragment)
                }
            })
        }
    }
private fun generateData(): List<Sport> { ... // code for my data set

Maps Fragment

private const val TAG = "MapsFragment"
class MapsFragment : Fragment(), OnMapReadyCallback {

    private lateinit var map: GoogleMap
    private lateinit var sport: Sport

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val bundle = arguments
        if (bundle !=null) {
            sport = bundle.getSerializable(EXTRA_USER_MAP) as Sport
        }

        val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
        mapFragment?.getMapAsync(this)
    }

    // Actions for map
    override fun onMapReady(googleMap: GoogleMap) {
        map = googleMap
        Log.i(TAG, "Map rendered: ${sport.title}") // Logcat check once working
...

Related Adapter code

class MapsAdapter(
    val context: Context,
    val sports: List<Sport>,
    val onClickListener: OnClickListener)
    : RecyclerView.Adapter<MapsAdapter.ViewHolder>() {

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

    interface OnClickListener {
        fun onItemClick(position: Int)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val sport = sports[position]
        holder.itemView.setOnClickListener {
            Log.i(TAG, "Tapped on position $position")
            onClickListener.onItemClick(position)

        }
...
1 Answers

You can have a shared view model where you can have a LiveData property and you can change his value from MainFragment and listen to his changes from MapsFragment and do everything you want or you can have a listener and can listen when user click and get the position. Hope this will helpful for you

Related