[UNRESOLVED] I have a general question about updat...
# compose
l
[UNRESOLVED] I have a general question about updating state. Lets say the UI provides a
LazyColumn
composable which gets its items from a flow. As terminal operator I'm using
toList(myStateList)
and store new items in a
SnapshotStateList
. The producer of the flow might be a
BroadcastReceiver
or anything else that keeps the flow alive. Further, the UI provides a sheet for filtering the list. When I apply a filter that reduces the items of the list, should I remove the affected items from the current list (which are currently visible) or should I clear the list and restart the collection? What is the better practice here?
w
IMHO, assuming your filter is on local data, you may have a source Flow that has all the data and then applying filters is just applying transformations on that source flow, if no filter applied, source flow consumed directly. something like that, especially if the items were processed to be presentable, you don't wanna redo that. And for UI, it should receive a List<Item> and not care about any of the above.
l
That's true, but did you consider that only new items are affected from the filter and not the ones that are currently visible? My question is about what is the best practice for updating the currently visible items. I'm aware of 3 options: 1. Do nothing and keep abandoned items in list 2. Remove filtered items from list after filter is applied 3. Clear List and refetch items
w
I think something is missing here. The way I see it is: when you use flows, data "flows" from the source to UI through transformations and possibly combinations of other flows to end up with some derived data that UI consumes (e.g via .stateIn(), .collectAs...) aka reactive and that's the point to save you the trouble of synchronizing data sources with consumers/UIs (by clearing lists or re-fetching stuff etc). And this is supposed to be the functional way of doing these type of things. (the options you listed above are more OOP and defeat the purpose of flows if I'm understanding you correctly). TLDR: after applying a transformation on your source flow, a new list of items will be produced and will be delivered to UIs automatically without any further effort by you. Now you can optimize that new list generated by using a persistent immutable list to save memory and CPU. Hope this helped and clarified my POV.
l
I got you. I should mention that the flow items come from a
BroadcastReceiver
, that says, this
a new list of items will be produced
is not possible since all items have to be discovered every time again
It is not a fixed list of items which I collect from the flow, instead every item has to discovered again when I trigger the BroadcastReceiver. It's about Bluetooth devices which I have to scan first and if it is discovered it gets emitted or not.
w
I didn't say fixed list. This is exactly the use case for using flows: you have a stream of inputs (e.g. via scanning) everytime you scan you emit that data to your source flow and the rest should handle itself since flows get setup like a pipeline. if a filter applied, the data won't make to UI (skip) else it will appear.
l
True story. But when I'm not scanning and I apply the filter, there is no new list produced and the old list is abandoned with wrong state.
Only after scanning I get updated items (not a new list, only items)
w
Not at all you can have another flow that receives filtering input and combine both flows and generate your final flow that is consumed by UI since "you pressing buttons on the screen to apply filters" is also a "flow"/"stream" of inputs.
l
Hmm I guess this would work. But I'm not sure how to setup it up properly. Let me show you my use case class logic:
Copy code
override fun execute(
        requestBuilder: () -> ObserveDiscoveredDevicesRequest,
    ): Flow<DiscoveredDeviceResponse> =
        with(requestBuilder()) {
            filterService.subscribeToState().flatMapLatest { filter ->
                discoveryService.subscribeToState()
                    .filter { device ->
                        if (filter.name.isNotBlank()) {
                            device.name.startsWith(filter.name, ignoreCase = true)
                        } else {
                            true
                        }
                    }
                    .map { DiscoveredDeviceResponse.from(it) }
                    .flowOn(dispatcher)
            }
        }
Wait. Even when I
have another flow that receives filtering input and combine both flows and generate your final flow
the device discovery flow wouldn't emit anything as long as I'm not scanning. 🤔
After applying the filter the state is abandoned until I scan, unless I would have some repeat or cache logic for the device discovery flow
w
Ok what I would recommend is: if your original code is working and you have a deadline, keep what you're familiar with for now and in a hobby project/spare time make sure to look further into
Flows
,
StateFlows
and
MutableStateFlows
to learn more about their usability since they can be very tricky especially if one is not familiar with reactive programming. Hope this helped.
l
ok thanks. Maybe you can write pseudo code how you would implement it, so I can get an idea