androidkotlinandroid-fragmentsmvvmfragment-backstack

Fragments are getting destroyed when back pressed. I want it to be not destroy when back press so that I can save the states of fragments


I created an Activity and added 3 fragments into it. One is HomeFragment, second is ProductDetailFragment, and third is CartFragment. I have added multiple quantities of a product from ProductDetailFragment and I want the quantity detail to be saved even if I am back to HomeFragment and again coming back to ProductDetailFragment. But as soon as I press back to go to HomeFragment, ProductDetailFragment is destroyed and if coming again to ProductDetailFragment quantity values are all wiped out.

I have tried onSaveInstanceState to store the state even if fragment is destroyed but not working in this case. Lemme show you my code:

ProductDetailFragment

lateinit var binding: FragmentProductDetailBinding
    var quantityValue = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Toast.makeText(activity,"onCreateCalled",Toast.LENGTH_SHORT).show()
        if(savedInstanceState == null) {
            quantityValue = 0
        } else {
            quantityValue = savedInstanceState.getInt("quantity",0)
        }

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
       /// return inflater.inflate(R.layout.fragment_product_detail, container, false)


        binding = FragmentProductDetailBinding.inflate(inflater,container,false)
        binding.productDetails1 = arguments?.getParcelable("product")
        var productDetail = binding.productDetails1

       // var priceAmount = binding.priceAmount.text.toString().toFloat()

        Log.d("PriceAmount", binding.priceAmount.text.toString())

       // Log.d("ProductTitle", binding.productDetails1.title)
                binding.addButton.setOnClickListener {
                // Log.d("ProductDetail",initialValue.toString())
                if (productDetail == null) return@setOnClickListener
                quantityValue = quantityValue + 1
                binding.totalPriceText.text = "Total price"
                binding.quantityText.text = quantityValue.toString()
                binding.priceAmount.text = (productDetail.price * quantityValue).toString()


                // Log.d("ProductDetail",priceAmount.toString())
            }
            binding.removeButton.setOnClickListener {
                if (quantityValue != 0) {
                    if (productDetail == null) return@setOnClickListener
                    quantityValue = quantityValue - 1
                    binding.quantityText.text = quantityValue.toString()
                    //   binding.priceAmount.text = (priceAmount*quantityValue).toString()
                    binding.priceAmount.text = (productDetail.price * quantityValue).toString()

                }
            }

            binding.addCartButton.setOnClickListener{
            val bundle = Bundle()
            bundle.putString("quantity_text", String.format("%d",quantityValue))
            val cartFragment = CartFragment()
                cartFragment.arguments = bundle


            val ft = fragmentManager?.beginTransaction()
            ft?.replace(R.id.flFragment,cartFragment)
            ft?.addToBackStack("")
            ft?.commit()
            // Create the transaction
            // Create the transaction
        }
        Toast.makeText(activity,"onCreateViewCalled",Toast.LENGTH_SHORT).show()

        return binding.root
    }

  override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putInt("quantity", quantityValue)

    }

HomeFragment

class HomeFragment : Fragment() {

    lateinit var binding: FragmentHomeBinding
    private val viewModel: HomeFragmentViewModel by lazy {
        ViewModelProvider(this).get(HomeFragmentViewModel::class.java)

    }



    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = FragmentHomeBinding.inflate(inflater,container,false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.viewModel = viewModel
        binding.lifecycleOwner = viewLifecycleOwner

        val clickListener = object: ClickListener{
            override fun onClick(itemProduct: ProductDataModel) {
                val productDetailFragment = ProductDetailFragment().apply {
                    arguments = bundleOf(Pair("product",itemProduct))
                }
                val ft : FragmentTransaction = fragmentManager!!.beginTransaction()
                ft.replace(R.id.flFragment, productDetailFragment,"PRODUCT_DETAIL_FRAGMENT")
                ft.addToBackStack(null)
                ft.commit()
            }

        }

        viewModel.products.observe(viewLifecycleOwner){
            if (it.size>0){
                //Log.d("HomeFragment",it.get(0).title)
                //Log.d("HomeFragment", it.get(0).image)
                binding.productRecyclerView.adapter = ProductListAdapter(it,clickListener)
            }
        }
    }

    override fun onResume() {
        super.onResume()
        (activity as AppCompatActivity?)!!.supportActionBar!!.hide()

    }

    override fun onStop() {
        super.onStop()
        (activity as AppCompatActivity?)!!.supportActionBar!!.show()
    }

    override fun onDestroy() {
        super.onDestroy()
        Toast.makeText(activity,"onDestroyHomeFrag",Toast.LENGTH_SHORT).show()
    }

}

MainActivity ( In which all fragments are there )

class MainActivity : AppCompatActivity() {
private val viewModel:MainViewModel by viewModels()
`enter code here`var productDetailFragment = ProductDetailFragment()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    installSplashScreen().apply {
        setKeepOnScreenCondition{
            viewModel.isLoading.value
        }

    }
    setContentView(R.layout.activity_main)
    val homeFragment = HomeFragment()

    //Starting the fragment transaction from SupportFragmentManager class
    val ft : FragmentTransaction = supportFragmentManager.beginTransaction()
    ft.add(R.id.flFragment, homeFragment,"HOME_FRAGMENT")
    ft.addToBackStack(null)
    ft.commit()

enter code here} }


Solution

  • You can use SharedViewModel since both fragments share same activity. You can add data that you want to preserve and when you go to the detail page view model still has the data you want because it depends on activity' lifecycle.

    Offical doc: https://developer.android.com/topic/libraries/architecture/viewmodel#sharing