s0nerik / context_plus

Convenient BuildContext-based value propagation and observing. Seamlessly integrates with Flutter's built-in observability primitives.
MIT License
32 stars 4 forks source link

AsyncSnapshot #8

Closed lukepighetti closed 2 months ago

lukepighetti commented 5 months ago

I totally get the reasoning behind changing the API to use AsyncSnapshot but part of the beauty of context_watch was the simplicity of the API, would have loved to see .watch stay as it was, and then add a .watchSnapshot with the AsyncSnapshot return type

s0nerik commented 5 months ago

Hey Luke, thanks for the feedback. I understand your frustration.

My intention for context_watch was to replace the *Builder widgets for reactive data with something that doesn't introduce nesting, but provides all the same data these builders provide, in a unified fashion.

The very first implementation, even though slightly easier to write, was not very useful for async primitives:

Returning AsyncSnapshot from the .watch call resolves all that and provides the same values, which respective *Builder widgets provide:

The beauty of it is that it allows to have a very easy-to-remember API for every possible observable type:

Only two extra methods for every observable type, returning everything you might want to know about the state of the observable.

All potential alternatives I know of (including your proposal) blow that number up, effectively increasing the complexity ("which method should I use in these circumstances?") and making the learning curve steeper, which I want to avoid.

Speaking of the migration path, these should be equivalent:

context_watch v1 context_watch v3
final data = ValueStream.watch(context); final data = ValueStream.watch(context).requireData;
lukepighetti commented 4 months ago

ended up with this extension which I now cart around to all my projects. the purpose is to make it easy to get the underlying data without error handling (as it was previously, I know very few people who actually make use of the whole AsyncSnapshot API). null means nothing available yet. perhaps this could be useful to you

extension ValueStreamX<T> on ValueStream<T> {
  T watchValue(BuildContext context) => watch(context).data ?? value;
}
s0nerik commented 4 months ago

@lukepighetti, looks like the watchValue extension you use can be simplified to the watch(context).data! or watch(context).requireData since context_watch handles a special case of ValueStream's initial value being already present under the hood and returns the data in that case in the same frame.

Regarding the package API design decision, I don't want to get rid of the AsyncSnapshot as it allows for keeping the API surface really tiny and easy to remember and use. I love the fact that there are only two additional methods on everything observable: .watch() (for observing the whole thing) and .watchOnly() (for observing a part of the thing) and don't plan on adding any new methods to the base packages.

However, I think it would be fairly easy to introduce another "integration" package for rxdart specifically, with the .watchValue() and .watchValueOrNull() extensions, which would just always return the respective property values of the ValueStream. Please let me know if that could be of any value to you.