android-jetpack-composekotlin-flowproto-datastore

Proto DataStore Recompose On Updates


I have managed to set up Proto DataStore in my Jetpack Compose project. My Proto DataStore is defined by .proto file that has a list of objects.

I have successfully been able to observe values from the Proto DataStore:

 var myProtoObject: MyProtoObjectType?
 viewModel.myFlow.observe(LocalLifecycleOwner.current) { obj ->
                myProtoObject = obj
            }

Part of my UI has a lazyColumn that renders the objects from the Proto DataStore. Usually, when working with a lazyColumn, you can define a mutable state variable for the lists' items (or collectAsState/observeAsState).

The problem is, that while the flow works properly and gives updates, it does not cause recomposition.

LazyColumn(modifier = Modifier
           .fillMaxSize()
           .padding(paddingValues)) {
               item {
                      Row(
                          modifier = Modifier.fillMaxSize(),
                          horizontalArrangement = Arrangement.Center
                        ) {
                           //Content here
                        }
                   }
                   myProtoObject?.itemsList?.let {
                       items(it) { item ->
                                // Content here
                        }
                      }
            }

How would one trigger recomposition when updates are received from the flow?

EDIT

The .proto file looks like this:

syntax = "proto3";

option java_package = "com.example.example2";
option java_multiple_files = true;

message ListItem{
  string itemId = 1;
  string itemDescription = 2;
}

message ListItems{
  repeated ListItem items = 1;
}

I also tried doing the following, but it did not work:

var obj: List<ListItem> = remember {
   mutableListOf()
}

Solution

  • Compose requires a state for recomposition. Here, you are using var myProtoObject: MyProtoObjectType? which is not a state, just normal variable. Therefore, compose does not recognize the need to recompose when this object changes.

    You can achieve your goal with codes at below. Additionally, keep in mind that the flow is reset in situations such as screen rotations. You might also want to consider using StateFlow instead. You can use it in the same way as collectAsState.

    First way:

    val myProtoObject by viewModel.myFlow.collectAsState(initial = null)
    

    Second way:

    var itemsList:  List<YourItem> by  remember { mutableStateOf(listOf()) }
    
    viewModel.myFlow.observe(LocalLifecycleOwner.current) { obj ->
        itemsList = obj.itemsList
    }
    
    LazyColumn(modifier = Modifier
        .fillMaxSize()
        .padding(paddingValues)) {
        item {
            Row(
                modifier = Modifier.fillMaxSize(),
                horizontalArrangement = Arrangement.Center
            ) {
                //Content here
            }
        }
    
        items(itemsList) {
            // Content here
        }
    }