I am learning in State in jetpack compose. I found that State holders as source of truth. So created my some data can you guys guide me if I am doing wrong here.
PairViewModel.kt
class PairViewModel : ViewModel() {
var isBluetoothEnabled = mutableStateOf(false)
private set
fun onBluetoothEnable(value: Boolean) {
isBluetoothEnabled.value = value
}
}
PairScreen.kt
class PairScreenState(context: Context, viewModel: PairViewModel) {
private val bluetoothManager: BluetoothManager = context.getSystemService(BluetoothManager::class.java)
private val bluetoothAdapter: BluetoothAdapter by lazy {
bluetoothManager.adapter
}
init {
viewModel.onBluetoothEnable(bluetoothAdapter.isEnabled)
}
fun checkBluetoothStatus(bluetoothStatus: MutableState<Boolean>): BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
when (intent.getIntExtra(
BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR
)) {
BluetoothAdapter.STATE_OFF -> {
bluetoothStatus.value = false
}
BluetoothAdapter.STATE_ON -> {
bluetoothStatus.value = true
}
}
}
}
}
}
}
@Composable
fun rememberPairScreenState(
context: Context,
viewModel: PairViewModel
) = remember {
PairScreenState(context, viewModel)
}
@Composable
fun PairContent(
context: Context = LocalContext.current,
viewModel: PairViewModel = getViewModel(),
rememberPairScreenState: PairScreenState = rememberPairScreenState(context, viewModel),
) {
AnimatedVisibility(visible = true) {
AppBarScaffold() {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
rememberPairScreenState.checkBluetoothStatus(viewModel.isBluetoothEnabled).apply {
context.registerReceiver(this, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
}
if (viewModel.isBluetoothEnabled.value) {
println(">> Enable >>>")
} else {
println(">> Disable >>>")
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun PairContentPreview() {
PairContent()
}
I am using Bluetooth as example to understand state holder in my use case. Please guide me if you find anything wrong in my code. Thanks
Ill try my best here, I get where you're coming from, having a code that it's hard to verify if its the proper way of doing "yet", regardless of how many source materials you review like in github
, sometimes references just doesn't exist yet right?
For State
hoisting/handling, its good to follow the principles coming from the community. So the way I handle State Hoisting
, is thinking of its purpose
So if its just something that needs to be local within the @Composable
remember {...}
If its something that deals with multiple logic and values, State
class
class PersonState(val personParam: Person) {
.....
}
@Composable
fun rememberPersonState(person: Person) = remember(key1= person) {
PersonState(person)
}
If its something that deals with repository, network calls, use-cases where persistence is a major part of the requirement, ViewModel
, and lifecyle
is something you have to be aware of. ViewModel
class PersonScreenViewModel {
/..RepositoryStateFlows../
/..Data structural updates../
}
So far this mindset and approach helped me a bit when deciding how would I hoist my states
.
As for your PairScreenState, consider this use-case solution coming from this post Detect if Soft Keyboard is Open or Close, where you can detect if the keyboard is open or not
I would have your BlueTooth usecase where I would implement it as a Composable
utility function and returns a State
where I can define a DisposableEffect
, though this code is not working but I think you'll get my point here.
enum class BlueTooth {
ON, OFF
}
@Composable
fun BlueToothAsState(): State<BlueTooth> {
val blueToothState = remember { mutableStateOf(BlueTooth.OFF) }
DisposableEffect(view) {
var mReceiver : BroadcastReceiver? = object : BroadcastReceiver() {
/.../
when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
BluetoothAdapter.STATE_OFF -> {
blueToothState = BlueTooth.OFF
}
BluetoothAdapter.STATE_ON -> {
blueToothState = BlueTooth.ON
}
}
}
}
onDispose {
mReceiver = null
}
}
return blueToothState
}
As for the other parts of the code, I don't think you need it here if its just always set to true
AnimatedVisibility(visible = true)