KotlinLeaner
04/13/2023, 12:19 PMModalBottomSheetLayout
in my project. I have a Button
and onClick
I am sending Boolean
to show sheetContent
. It works once without any problem. After clicking on outside of ModalBottomSheetLayout
it closes the sheetContent
. After again clicking on Button
, sheet is not opening again. I don't understand what is the problem in here.KotlinLeaner
04/13/2023, 12:19 PMclass MainActivity : AppCompatActivity() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val viewModel by viewModels<MainActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
viewModel.enableBottomSheet.observe(this) {
binding.bottomSheetComposeView.setContent {
ModalBottomSheetSample(it)
}
}
binding.buttonComposeView.setContent {
ButtonView()
}
}
@Composable
fun ButtonView() {
Column(Modifier.fillMaxSize()) {
Button(onClick = {
viewModel.enableBottomSheet.postValue(true)
}) {
Text(text = "Open Bottom Sheet")
}
}
}
@Composable
@OptIn(ExperimentalMaterialApi::class)
fun ModalBottomSheetSample(value: Boolean) {
val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
val scope = rememberCoroutineScope()
SideEffect {
scope.launch {
if (value) {
state.show()
}
}
}
ModalBottomSheetLayout(
sheetState = state,
sheetContent = {
LazyColumn {
items(5) {
ListItem(
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = null
)
},
text = { Text("Item $it") },
)
}
}
}
) {}
}
}
MainActivityViewModel.kt
class MainActivityViewModel : ViewModel() {
val enableBottomSheet by lazy { MutableLiveData(false) }
}
KotlinLeaner
04/13/2023, 12:20 PMcompose_bom = "2023.03.00"
Chrimaeon
04/13/2023, 12:32 PMKotlinLeaner
04/13/2023, 12:36 PMstate and events when handling the "view state" of the bottom sheet
. Is there example for this?Chrimaeon
04/13/2023, 12:38 PMThe pattern where the state goes down, and events go up is called a unidirectional data flow. In this case, the state goes down fromtoHelloScreen
and events go up fromHelloContent
toHelloContent
. By following unidirectional data flow, you can decouple composables that display state in the UI from the parts of your app that store and change state.HelloScreen
Chrimaeon
04/13/2023, 12:39 PMModalBottomSheetSample
has a state of “showing” but does not react on the “event” of when it’s hidden.KotlinLeaner
04/13/2023, 12:42 PMKotlinLeaner
04/13/2023, 12:42 PM@Composable
@OptIn(ExperimentalMaterialApi::class)
fun ModalBottomSheetSample(value: Boolean) {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
val scope = rememberCoroutineScope()
SideEffect {
scope.launch {
if (value) {
sheetState.show()
}
}
}
ModalBottomSheetContent(sheetState)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ModalBottomSheetContent(sheetState: ModalBottomSheetState) {
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
LazyColumn {
items(5) {
ListItem(
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = null
)
},
text = { Text("Item $it") },
)
}
}
}
) {}
}
Chrimaeon
04/13/2023, 12:45 PMfalse
when the sheet is closed. I’d remove the code and rethink the whole approach. like you are doing it now, it will not work.KotlinLeaner
04/13/2023, 12:51 PMModalBottomSheetContent
, then I'll update the livedata value. How can we know that ModalBottomSheetContent
is closed?Chrimaeon
04/13/2023, 12:55 PMKotlinLeaner
04/13/2023, 1:00 PMLiveData
to MutableSharedFlow
. I am sending every time true
when click on the`Button`.
class MainActivityViewModel : ViewModel() {
val showBottomSheetContent by lazy { MutableSharedFlow<Boolean>() }
}
MainActivity.kt
class MainActivity : AppCompatActivity(), HideViewListener {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val viewModel by viewModels<MainActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.showBottomSheetContent
.collect { value ->
binding.bottomSheetComposeView.setContent {
ModalBottomSheetSample(value)
}
}
}
}
binding.buttonComposeView.setContent {
ButtonViewContent()
}
}
@Composable
fun ButtonViewContent() {
val scope = rememberCoroutineScope()
Column(Modifier.fillMaxSize()) {
Button(
onClick = {
scope.launch {
viewModel.showBottomSheetContent.emit(true)
}
}
) {
Text(text = "Open Bottom Sheet")
}
}
}
@Composable
@OptIn(ExperimentalMaterialApi::class)
fun ModalBottomSheetSample(value: Boolean) {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
val scope = rememberCoroutineScope()
SideEffect {
scope.launch {
if (value) {
sheetState.show()
}
}
}
ModalBottomSheetContent(sheetState)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ModalBottomSheetContent(sheetState: ModalBottomSheetState) {
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
LazyColumn {
items(5) {
ListItem(
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = null
)
},
text = { Text("Item $it") },
)
}
}
}
) {}
}
}
Now it working fine..KotlinLeaner
04/13/2023, 1:01 PMBottomSheet
open and close, without any problem. Is there any thing need to improve?Chrimaeon
04/13/2023, 1:04 PMChrimaeon
04/13/2023, 1:05 PMlifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.showBottomSheetContent
.collect { value ->
binding.bottomSheetComposeView.setContent {
ModalBottomSheetSample(value)
}
}
}
}
why do you need that? the bottom sheet is handled by its state and not the value of the shared flow.KotlinLeaner
04/13/2023, 1:11 PMlifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.showBottomSheetContent
.collect {
binding.bottomSheetComposeView.setContent {
ModalBottomSheetSample()
}
}
}
}
ModalBottomSheetSample
@Composable
@OptIn(ExperimentalMaterialApi::class)
fun ModalBottomSheetSample() {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
val scope = rememberCoroutineScope()
SideEffect {
scope.launch {
sheetState.show()
}
}
ModalBottomSheetContent(sheetState)
}