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 fromtoHelloScreenand events go up fromHelloContenttoHelloContent. 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)
}