0

I have an activity which has a container for fragments.

I have three fragments in that particular activity. When I rotate the screen it comes to first fragment. How can I make it show the second fragment if it was the current screen.

I tried to add this method Handle Fragment On Screen Orientation Changes?.

But I am having error:

java.lang.IllegalStateException: Fragment VerifyOtp{d123b5f} (79fc7cef-725d-48cb-9591-3fa9da9f864f) is not currently in the FragmentManager

MainActivity code:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var binding: ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)

        setContentView(binding.root)

        val sharedPref = getSharedPreferences("loggedUser", Context.MODE_PRIVATE)
        val userPhone = sharedPref.getString("userPhone", null)
        val location = sharedPref.getString("location", null)

        if(userPhone == null){
            val loginFragment = LoginFragment()
            supportFragmentManager.beginTransaction().replace(R.id.loginFrameLayout, loginFragment).commit()
        }
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        instantiateFragment(savedInstanceState)
    }

    private fun instantiateFragment(inState: Bundle){
        val manager = supportFragmentManager
        val transaction = manager.beginTransaction()

        if(inState != null){
            manager.getFragment(inState, "VERIFY_OTP")
        }

    }
}

LoginFragment:

class LoginFragment : Fragment() {

    private var _binding: FragmentLoginBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        _binding = FragmentLoginBinding.inflate(inflater, container, false)
        val view = binding.root


        ArrayAdapter.createFromResource(
            view.context,
            R.array.country_code,
            android.R.layout.simple_spinner_item
        ).also { adapter ->
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
            binding.countryCode.adapter = adapter
        }


        val fragmentVerifyOTP = VerifyOtp()


        binding.btnLogin.setOnClickListener {
            var userPhoneNumber = binding.userPhoneNumber.text.toString()

            if(userPhoneNumber.isEmpty() || userPhoneNumber.length < 10) {
                binding.userPhoneNumber.error = "Phone Number Required"
            }else{
                val arguments = Bundle()
                arguments.putString("Phone", userPhoneNumber)
                fragmentVerifyOTP.arguments = arguments

                val transaction = activity?.supportFragmentManager?.beginTransaction()
                if (transaction != null) {
                    transaction.replace(R.id.loginFrameLayout, fragmentVerifyOTP, "VERIFY_OTP")
                    transaction.addToBackStack("")
                    transaction.commit()
                }
            }
        }
        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
    
}

VerifyOTP:

class VerifyOtp : Fragment() {

    private var _binding: FragmentVerifyOtpBinding? = null
    private val binding get() = _binding!!


    private lateinit var phone: String


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        _binding = FragmentVerifyOtpBinding.inflate(inflater, container, false)
        val view = binding.root

        val bundle = this.arguments

        if(bundle != null){
            phone = bundle.getString("Phone").toString()
        }

        val application = requireNotNull(this.activity).application
        val dataSource = AppDatabase.getInstance(application)

        val viewModelFactory = LoginSharedViewModelFactory(phone, dataSource)
        val viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(LoginSharedViewModel::class.java)
        viewModel.setPhoneNumber(phone)


        viewModel.phoneNumber.observe(viewLifecycleOwner, { phone ->
            binding.tvOTPPhoneNumber.text = phone
        })

        binding.btnVerify.setOnClickListener {
            val isFieldsSet = checkAllFields()
            if(isFieldsSet && viewModel.newUser()){
                val transaction = fragmentManager?.beginTransaction()
                if (transaction != null) {
                    transaction.replace(R.id.loginFrameLayout, UserDetails())
                    transaction.addToBackStack("")
                    transaction.commit()
                }
            }else if(isFieldsSet && !viewModel.newUser()){
                insertToSharedPreference(viewModel.phoneNumber.value,
                    viewModel.currentUser.value?.location
                )
                val user = viewModel.phoneNumber.value
                val intent = Intent(view.context, Home::class.java).apply {
                    putExtra("USER_PHONE", user)
                }
                startActivity(intent)
            }
        }


        return view
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val manager = activity?.supportFragmentManager
        manager?.putFragment(outState, "VERIFY_OTP", VerifyOtp())
    }
pepperlove
  • 170
  • 14

1 Answers1

2

You have to save which is the current screen using onSaveInstanceState when screen starts rotate and the restore the fragment after screen rotated using onRestoreInstanceState.

First make sure to add this to the activity. this will let us use state changes.

android:configChanges="orientation|screenSize"

Then,

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the current fragment tag or id
    savedInstanceState.putString("current_fragment", SCREEN_TAG_OR_ID);
    super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    current_fragment = savedInstanceState.getString("current_fragment");
    // use this to show the current screen 
}
plsankar
  • 437
  • 1
  • 5
  • 11