Closed peekpt closed 4 years ago
Is that it? I'm still trying to figure out this plugin...
To be completely honest with you, making diagrams is not my thing.
I can't really help you with that. But if others may want to help, I'll gladly answer all the questions you have.
We should use UML 2 for diagrams because they are well specified and universally understood.
The documentation for provider had me thinking it wasn't a well maintained or broadly adopted package, so I went looking elsewhere. Then I saw this Flutter team video saying how they use provider in house.
I'm excellent at technical documentation and may take this up, but I'm still in the stages of selecting my state mechanism. If I do draft a new README, what's the best way for us to collaborate on it? Is git really the best way for us to do this?
I switched to the new Bloc 0.20 which now has the new Provider under the wood. The combination of both worlds is almost perfect for state management
The documentation for provider had me thinking it wasn't a well maintained or broadly adopted package
Could you explain what you felt is lacking? It's not limited to the README, there's the dartdoc too.
If I do draft a new README, what's the best way for us to collaborate on it?
Make a pull request – everybody can see them and participate.
Any help is appreciated 😄
Could you explain what you felt is lacking? It's not limited to the README, there's the dartdoc too.
Yeah, I mean just the README. APIs are for reference once you have a basic understanding.
Most glaringly, it's unclear how to control whether a widget rebuilds. I'm having to play with it to figure this out. For example, the README doesn't even mention listen = false
. I found this by browsing the dartdocs.
Make a pull request – everybody can see them and participate.
Okay, will do. I'll track my running understanding of things as I dive into provider.
The Flutter team is in several places promoting provider
as the place to start, so I'm thinking we need a README that appeals to newbie flutterers, briefly explaining the problems provider
solves. This should also help more experienced folks understand that provider
isn't just training wheels, which is the impression I get reading posts about (other) BLoC solutions. (It seems to me that provider
can be thought of as a BLoC solution, particularly with the support of StreamProvider
.)
Provider and Consumer is just an efficient way to deliver objects to the widget tree. BLoC is a pattern to organize your code with events, blocs and states. So the code flows only with one direction events->bloc->states->UI (and loops)
I implemented a simple app using provider, scoped_model, bloc_provider, bloc, and Didier Boelens' version of bloc. All but bloc produced largely identical code. Where provider and scoped_model have a "model," block solutions have a "BLoC." Models and blocs both manage state, they both contain "business logic," and they both have channels for receiving or delivering async messages.
bloc and one version of Didier Boelens' bloc asynchronously receive messages (events) and asynchronously deliver messages (states). Other versions of bloc synchronously receive state changes while asynchronously deliver state messages. Provider and scoped_model work similarly to these latter bloc implementations, but instead of delivering state they deliver change notifications, allowing the widgets to subsequently get state data synchronously.
I really don't see them as being all that different from one another. They all have a business logic object and it's just a matter of the channels that connect it to the UI. (There are tradeoffs of course, but I'm not seeing them as that significant.)
But my understanding may continue to evolve as I investigate further.
You seem to fall for the common misconception that provider is an architecture (misconception caused by the IO talk). It's not.
I like to take food as an example.
State management is "Burgers". It comes in many, many different flavors and is very broad.
BLoC is the latest burger from McDonald. It's very specific, but you may not like it.
provider
is a bag the most common ingredients to make a burger, already pre-assembled but without the meat that is the state implementation.
You can combine it with a steak to make a cheeseburger. But you don't have to. You can use chicken or a vegetarian ingredient too if you wanted to.
Basically, provider + ChangeNotifier = scoped_model But you can do provider + reducer, provider + mobx store, provider + bloc,...
That's also exactly why you shouldn't expect the same level of documentation from provider than from alternatives.
An example doesn't make sense. You can't request a final product of something that is deliberately not complete.
I could make detailed examples of how to make a scoped-model architecture using provider, as it's what most peoples having watched the IO expects.
But i personally use my own steak, that is not ChangeNotifier
as I'm allergic to mutability.
I'm just not using Provider because it's already integrated in the BLoC plugging.
I'm using BLoC + BuiltValue + Dio for standard web apis. It's my secret sauce :-)
A quinta, 29/08/2019, 08:22, Remi Rousselet notifications@github.com escreveu:
That's also exactly why you shouldn't expect the same level of documentation from provider than from alternatives.
An example doesn't make sense. You can't request a final product of something that is deliberately not complete.
I could make detailed examples of how to make a scoped-model architecture using provider, as it's what most peoples having watched the IO expects.
But i personally use my own steak, that is not ChangeNotifier as I'm allergic to mutability.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/rrousselGit/provider/issues/83?email_source=notifications&email_token=AABWJMOE3ZVQWBLZPB4XTZTQG52LJA5CNFSM4HPJG42KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5NQXLI#issuecomment-526060461, or mute the thread https://github.com/notifications/unsubscribe-auth/AABWJMNH2US6QJ7PY7POQMLQG52LJANCNFSM4HPJG42A .
You seem to fall for the common misconception that provider is an architecture (misconception caused by the IO talk). It's not.
I did the experiment and came to that conclusion prior to even discovering that talk. Instead, that talk is my reason for giving provider another look.
That said, I woke up this morning realizing what my misunderstanding was.
provider
is a bag the most common ingredients to make a burger, already pre-assembled but without the meat that is the state implementation.
Gotcha. Provider is a way to lift state in an object tree.
But i personally use my own steak, that is not
ChangeNotifier
as I'm allergic to mutability.
Do you use provider to DI and mobx/streams ?
That's also exactly why you shouldn't expect the same level of documentation from provider than from alternatives.
I just want documentation to leave me understanding the tool. I still do not understand this tool and am working toward that. (My difficulty may be due to never having worked with the reactive pattern -- I don't know -- but it's not due to inexperience with software or software architecture. I'm 50 and have been a professional developer since age 15.)
An example doesn't make sense. You can't request a final product of something that is deliberately not complete.
The documentation just needs to state its scope and clearly explain its use. Last night I encountered another thing that confused me about the documentation. The docs distinguish between Provider<T>.value
and Provider<T>
by saying the former "exposes" a value, while the latter "creates and exposes" an object. Yet, I clearly could create and expose any object in either approach. By scanning through the old issues I found a case of of misuse, which you explained as Provider<T>
disposing its object, while Provider<T>.value
does not. And now I understand the difference. The difference between "exposes" and "creates and exposes" now makes sense to me -- it's good reminder language but not the best explanatory language.
I could make detailed examples of how to make a scoped-model architecture using provider, as it's what most peoples having watched the IO expects.
But i personally use my own steak, that is not
ChangeNotifier
as I'm allergic to mutability.
I just want to be able to read the docs to understand how to properly use provider
with flutter.
I'm wondering if we could just focus on helping me understand and give me a chance to revise the README so you can evaluate what I'm trying to do after I've done it. If you don't like it, I can just make it a Medium post -- assuming the information is accurate.
Gotcha. Provider is a way to lift state in an object tree.
And a data-binding. Otherwise, it'd be just like get_it
.
Do you use provider to DI and mobx/streams ?
I don't have any professional Flutter project atm, so I can't answer that.
But here's a few gists of custom providers I made: https://gist.github.com/rrousselGit/b85ff28f3ee9509a97171f5b944890b1 https://gist.github.com/rrousselGit/4910f3125e41600df3c2577e26967c91
It's basically like ChangeNotifier
, but immutable.
I just want to be able to read the docs to understand how to properly use provider with flutter.
I'm wondering if we could just focus on helping me understand and give me a chance to revise the README so you can evaluate what I'm trying to do after I've done it. If you don't like it, I can just make it a Medium post -- assuming the information is accurate.
Any help is greatly appreciated. Feel free to open a PR and ask questions.
I'm not very good at documentation, so it'll be very useful to get some external help.
I'm experimenting to see what's possible and am getting some awesomely helpful error messages. Nicely done!
I think I finally grok this package. It's actually beautifully simple! I think the README just needs to convey the overall abstraction and how it variously instantiates that abstraction.
I've hand drawn a number of UML diagrams, but I'm still refining them.
How exactly is the provider value preserved across rebuilds? I ran across the following code in InheritedProvider<T>
:
/// Mutating `value` should be avoided. Instead rebuild the widget tree
/// and replace [InheritedProvider] with one that holds the new value.
final T _value;
I know build()
does not get called on the child of provider for each change in value, but I'm wondering if something gets rebuilt anyway.
I'm asking because I'm trying to work backwards from accurate UML diagrams to simplified diagrams suitable for the README. The accurate diagrams show how provider
works to some degree, because they show how provider
classes behave in response to flutter calls that we're otherwise used to handling. I need to show state being preserved across rebuilds. I was expecting the Provider
class to delegate the value to an associated State
class, but I'm not seeing that here. (I'm also trying to avoid reverse engineering the entire code base.)
Okay, as I continue to examine the code, it's looking like a new InheritedProvider<T>
is only created upon rebuilding the Provider<T>
, and at that time the value is copied from a state object that survives rebuilds.
Yes. InheritedProvider is just an Inheritedwidget.
The state is maintained by another widget (although it's specific implementation do not matter)
The state is maintained by another widget (although it's specific implementation do not matter)
I'm working backward from knowing how things work to determining what matters and how to show it. The less I understand, the more correcting you'll need to do in the diagrams.
For example, I originally surmised that Provider
created its value in response to createState()
, but noticing that builder
takes a context, I examined the code and found that the value is actually initialized on the first build. I could have handed you a diagram showing the value being created in response to createState()
, and you would have had to correct that.
I'm finding it necessary to illustrate how this package works with respect to the normal sequence of flutter calls, so that people can see how a Provider<T>
instance (for example), fits into the widget tree. Even so, I'm also looking for ways to remove (or creatively represent) the messaging that a user of the provider
package shouldn't need to be aware of.
I don't think the diagram should talk about createState/StatefulWidget, but only the builder callback.
It's worth noting that the builder will soon be lazy loaded
Thanks for the heads up about lazy loading builder, but I think you're missing my point about needing to understand how things work to get the diagrams right. I'll stop asking questions, get what I can from the code, and let you tell me what's wrong.
The diagram does show the order of events, so I'll depict prebuilding the value for now and we can change it later.
Okay, I'm sharing the most basic diagrams so you can see my approach. I'm also working on diagrams for listenables. I've been debating whether to show the return value from build()
here, providing a Widget out to flutter (all the way on the left). I left it off for simplicity.
One diagram per comment. Too squashed otherwise.
EDIT: Something must be wrong because dispose()
doesn't have the context here.
I'm not as confident about this one. I realize that some of the messaging does not directly indicate the actual senders and receivers, but I was trying to simplify things. It still looks too complex.
I should probably hide the fact that setState
is called, but I'm not sure how else to indicate that the listenable is inducing a rebuild.
UPDATE: Listenable
doesn't have a dispose()
method as shown, so I need to delete that.
I plan to explore representing StreamProvider and FutureProvider in hopes of find a simple abstraction that works for all the asynchronous rebuilders. But I'll wait for feedback on my understanding and this graphical approach before putting any more time into it.
FYI, I'm now experimenting with doing this in collaboration diagrams without showing flutter events.
And here's a really simplified collaboration diagram for ListenableProvider<T>
. It makes more assumptions about the user's understanding, but the text could make sure there are no misunderstandings. I think this one would be easier to generalize into a diagram for introducing the provider pattern in use here.
EDIT: This might be more attractive using bubbles and arcs, but let's get the content right first.
Here's my first attempt at abstracting all providers into a common pattern.
I see a number of tweaks that might make this clearer, but let's see if I've got it right first.
In particular, I'd like to make it clearer that the value has type T.
I may also need to better accommodate initialData
.
And I think we can do away with the "request value" messages. Just show the value returning. (Or maybe not, because these message make it clear that the consumer relies on the provider for the values, and becaues the messages equally represent Provider.of<T>()
calls.)
Here's an alternative abstraction to consider:
EDIT: Oops, had to make a correction. I replaced the image.
I don't think either of these abstractions works for the case where a build()
method calls Provider.of<T>()
.
It might be cool to swap the positions of Binding<T>
and the provider's child Widget
so that the new value drops in from the top, suggestive of a drip source.
I attempted to create analogous diagrams for the Consumer<T>
and Provider.of<T>()
cases. I'm not sure I'm happy with them.
Any feedback on the direction this is taking? Or the accuracy?
EDIT: I've got the 0..*
multiplicity on the wrong connection. It should be on the provider child's connection to its descendent widgets.
@rrousselGit, I'm hoping for your feedback before proceeding. We can continue to revise the diagrams, but I'd like to make sure my understanding is correct before working on the README.
There's no difference between Consumer and Provider.of Consumer works by calling Provider.of in a new widget.
Similarly, all providers works by exposing and rebuilding an InheritedProvider (although the lazy loading branch refactored it to instead call InheritedElement.notifyClient)
There's no difference between Consumer and Provider.of Consumer works by calling Provider.of in a new widget.
Yeah, I knew that -- saw that in the code. But there is a difference in how client code (of this package) gets the value. I'm trying to show users how to use the package more than how the package works.
There are two ways to subscribe to rebuilds. We could chose to only diagram the Consumer<T>
way, but it might be better to have diagrams for the major modes of using the package.
Similarly, all providers works by exposing and rebuilding an InheritedProvider (although the lazy loading branch refactored it to instead call InheritedElement.notifyClient)
Cool. But I'm not sure how this should affect the diagrams.
The sequence diagrams I posted earlier could show the call to Provider.of<T>()
in both scenarios, clearly showing that Consumer<T>
makes the call in one scenario, while the client code must do so in the other scenario. I'm not sure how to show that in these simpler collaboration diagrams.
Maybe this is a way to show both modes of behaving as a consumer, while still informing users of the library of what they're responsible for and what they aren't.
And here's what analogous Provider<T>.value
diagrams might look like
@rrousselGit, you're quite except to correct a mistake. The vibe I'm getting is that you're either busy or not thrilled about this approach to diagramming.
I'm indeed busy. I can't take a deeper look until tomorrow
I took your earlier suggestion to think of provider
as both lifting state and providing data binding and incorporated that into the model. If I did the abstraction right, it suggests a way to explain the various kinds of providers in the README.
A provider is a widget that makes a value available to descendent widgets known as consumers, "exposing" the value to the consumers.
The value may have a data source and may change over time. The data source supplies the value and knows when the value changes.
When the value has a data source, the consumer may bind to the value via the provider. A consumer that is bound to a value rebuilds when the value changes.
Different kinds of bindings are available for the different kinds of data sources:
Provider
exposes a value without making it possible to bind to the value to listen for changes.ListenableProvider
exposes a data source that implements the Listenable
interface and binds consumers to the data source via this interface.ChangeNotifierProvider
exposes a data source that implements the ChangeNotifier
class and binds consumers to the data source via the Listenable
interface on that class.ValueListenableProvider
exposes the value of a ValueNotifier
and binds consumers to this value via the Listenable
interface on that class.FutureProvider
exposes the value of a Future
and binds consumers to the value of the Future
at completion.StreamProvider
exposes the events of a Stream
and binds consumers to the emitted event. [Revised]The wording needs some massage, but I think this would be the gist.
Notice that to say that a consumer is bound to value V is to say that the consumer receives each subsequent instance of V. Data sources and values aren't always the same, and this language makes it clear what exactly the consumer receives on change.
It may be possible to revise the diagrams to refer to publishers instead of bindings. That would be more consistent with the current documentation that refers to listeners (in the README) and subscription (in the dartdocs).
One way to do this is to replace the word "binding" in the diagrams with "pub type" and "data source" with "publisher," though I'd hope we could find a better term than "pub type."
- A
Provider
exposes a value without making it possible to bind to the value to listen for changes.
That's not true There's an example https://github.com/rrousselGit/provider#do-i-have-to-use-changenotifier-for-complex-states
- A
Provider
exposes a value without making it possible to bind to the value to listen for changes.That's not true There's an example https://github.com/rrousselGit/provider#do-i-have-to-use-changenotifier-for-complex-states
I just need to work on the wording. In your example, the provider takes no part in the binding. Clearly code elsewhere can wrap a value and implement notification.
Maybe:
Provider
exposes a value without supplying a means for binding to the value. Even so, a descendant widget can wrap access to the value to rebuild on changes to the value, as explained here.
I did this simple sketch example, I'm no expert, but I think if the dev team adds diagrams to documentation will help a lot people that are starting to learn this Plugin. There's information missing like how the values behave, how they update, etc... With all this providers and ways to reach the provided values it is start to get confusing.