I have a bunch of popup Dialogs throughout the app I'm working on. What I wanted to do is turn them all into a BottomSheet presentation. Right now, I have one class where I'm instantiating the Dialogs from and able to reuse them throughout the app.
What would I need to do: to do the same to be able to reuse BottomSheetDialogFragments? Rather than creating a BottomSheet presentation from a screen to screen basis, is there a way to have all of them in one class and just call them when I need to from a different screen?
Adding a little bit more context. Let's say I have a CloseDialog, LogoutConfirmationDialog and I use them on multiple screens currently. I would like to do the same with the Android BottomSheet presentation modal if I was to turn these two Dialogs into a BottomSheet presentation.
You can create a custom fragment and inherit "BottomSheetDialogFragment"
class CustomBtmSheetFragment : BottomSheetDialogFragment()
{
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
{
return inflater.inflate(
R.layout.fragment_dialog,
container,
false
)
}
override fun getTheme(): Int = R.style.CustomBottomSheetDialog
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
BottomSheetDialog(requireContext(), theme)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
}
}
Related
Im using Compose via ComposeView in a Fragment backed by Graph Navigation.
Im using ModalBottomSheetLayout and need to hide it on back Press.
I have tried using BackHandler, but it is not working.
class fragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
BackHandler(true) {
Log.i("compose_check","back Pressed") // not getting triggered
}
}
}
}
}
I have Overrided onBackPressed() in the Activity. After removing that, it worked fine.
I'm trying to use Bottom Navigation in my app. Most explanations online are about using Bottom Navigation to navigate a fragment container from WITHIN the MainActivity. However, I am trying to use the Bottom Navigation from within a fragment and not an activity. Can anybody help me? This is the code I have thus far, the line in bold represents the problem saying:
"Too many arguments for public fun Fragment.findNavController(): NavController defined in androidx.navigation.fragment"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomePageBinding.inflate(inflater, container, false)
val bottomNavigationView = binding.bottomNavigation
val navController = findNavController(**R.id.homeScreenFragment**)
bottomNavigationView.setupWithNavController(navController)
return binding.root
}
}
I have a fragment that I want to display as an embedded fragment in a ViewPager and as a Bottom Sheet. I followed this https://developer.android.com/reference/android/app/DialogFragment#DialogOrEmbed and created a DialogFragment
private val mViewModel: CardPricesViewModel by viewModels()
private val binding by viewBinding(FragmentCardDetailPricesBinding::inflate)
companion object {
// This is the same value as the navArg name so that the SavedStateHandle can acess from either
const val ARG_SKU_IDS = "skuIds"
fun newInstance(skus: List<Long>?) =
CardDetailPricesFragment().apply {
arguments = Bundle().apply {
putLongArray(ARG_SKU_IDS, skus?.toLongArray())
}
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
However, when it gets inflated in a ViewPager the background dims as though it is a BottomSheetDialogFragment
However, when I manually do it with
supportFragmentManager
.beginTransaction()
.replace(binding.cardPricesFragmentContainer.id, cardDetailPricesFragment)
.commit()
It works fine. I see that the FragmentStateAdapter uses FragmentViewHolders instead of the using transactions directly (?), so I am not sure how to resolve this issue. I see that onCreateDialog() is being called, so if I call dismiss() after onViewCreated(), it works properly, but I am not sure if this a workaround
After some digging, I found the DialogFragment.setShowsDialog(boolean) method that you can use to disable the dialog being created.
I am doing the migration to view binding and I have a fragment where I use 2 different layouts depending on a variable. Basically it goes like this.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = inflater.inflate(
when (ussdType) {
UssdType.USSD_TYPE -> R.layout.fragment_transaction
else -> R.layout.fragment_balance
}, container, false
)
The problem is that if I refactor it to viewbinding I will need 2 types of viewbindings (FragmentTransactionBinding and FragmentBalanceBinding) so for example to use a button element.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
continue_button.setOnClickListener { onContinueAction(it) }
}
I don't know how to proceed since I have 2 different viewbindings
Simply create two variable that represent viewBinding
private lateinit var bindingTransaction: FragmentTransactionBinding
private lateinit var bindingBalance: FragmentBalanceBinding
and inflate one of them depend on your ussdType
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return when (ussdType) {
UssdType.USSD_TYPE -> {
bindingTransaction = FragmentTransactionBinding.inflate(inflater, container, false)
bindingTransaction.root
}
else -> {
bindingBalance = FragmentBalanceBinding.inflate(inflater, container, false)
bindingBalance.root
}
}
}
I'm also given a project which has 2 possible layouts in a fragment.
There are only 3 ways out. (If you don't have time to read, go with the 3rd option which is the best way)
One is to continue with findViewById() or Kotlin Synthetics. But both are not a good solution.
2nd way is to use two nullable viewBinding variables and use ? To do task So the one which is not null will be executed. If you want some value out of views, then you can get either of the bindings using smart null checks (Elvis operator) ex: val text = b1.tv.text ?: b2.tv.text
3rd (best way) is, create another fragment for BalanceFragment and use respective binding in it. This will also help you separate out the code so you have clear separation of concerns.
How to provide conditional Up and Back navigation when using Android's Navigation Component library?
For example, my app has a contact book. When creating a new contact, if the user presses back before filling out any info, I'd like to go back to the list of contacts. If the user filled out info, I'd like to go to the detail view of that contact.
In your fragment's onCreate, add an OnBackPressedCallback callback:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(this) {
backOrUpPressed()
}
}
Then, after you setup your toolbar with the NavController, set a NavigationOnClickListener:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Setup the view
setSupportActionBar(toolbar)
toolbar.setupWithNavController(findNavController(), AppBarConfiguration(findNavController().graph))
toolbar.setNavigationOnClickListener { backOrUpPressed() }
}
Then implement your custom Up/Back logic in backorUpPressed()