Provider: [REQ] Documented diagrams

Created on 23 May 2019  Ā·  150Comments  Ā·  Source: rrousselGit/provider

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.

Provider

good first issue help wanted

Most helpful comment

_was able to take a deeper look at everything_
Thanks for your hard work!

To be honest, I don't understand any of these graphs.
I'm no expert on the topic, so I don't know what the issue is. But what's certain is that they are too complex for what provider is.

It's ultimately nothing but a setState on the top of InheritedProvider.

For example, we could say that StreamProvider used this way:

StreamProvider<T>.value(
  value: yourStream,
  child: child,
);

is similar to:

StreamBuilder<T>(
  stream: yourStream,
  builder: (_, snapshot) {
    return InheritedProvider<T>(
      value: snapshot.data,
     child: child,
    );
  }
)

I'm pretty sure that for most Flutter devs, this comparison is easier to understand than the current iteration of StreamProvider's graph

All 150 comments

Is that it? I'm still trying to figure out this plugin...

Provider

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.

provider_value

One diagram per comment. Too squashed otherwise.

provider

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.

listenableprovider

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.

listenableprovider_collab

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.

abstract1

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:

abstract2

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.

abstract3 consumer

abstract3 provider_of

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.

abstract4 consumer

abstract4 custom

And here's what analogous Provider<T>.value diagrams might look like

value1 consumer

value2 custom

@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:

  • A Provider exposes a value without making it possible to bind to the value to listen for changes.
  • A ListenableProvider exposes a data source that implements the Listenable interface and binds consumers to the data source via this interface.
  • A ChangeNotifierProvider exposes a data source that implements the ChangeNotifier class and binds consumers to the data source via the Listenable interface on that class.
  • A ValueListenableProvider exposes the value of a ValueNotifier and binds consumers to this value via the Listenable interface on that class.
  • A FutureProvider exposes the value of a Future and binds consumers to the value of the Future at completion.
  • A 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:

  • A 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 find this UML graphs so confusing.

simple

Provider is the BurgerShop which has the employees (build) that will be building the Burgers (Object) also has thrash cans (dispose)

Consumer (us) have a way (
to eat the Burguer (build) ,we organize our table (widget)

An alternative I was experimenting with lately, is to use flutter_web to make a dynamic graph, where we could see what life-cycles got called and which widgets have access to which value

@peekpt, did you mean to close an issue that I'm actively working on and am seeking help with, and for which Remi expressed a desire for assistance?

An alternative I was experimenting with lately, is to use flutter_web to make a dynamic graph, where we could see what life-cycles got called and which widgets have access to which value

Can you point me to an example of this elsewhere, so I might play with an alternate approach?

I realize only software engineers are going to understand UML, though it's hard for me to see how most of it wouldn't be intelligible to non-engineers.

In any case, we can formally express what we want in UML, and once we agree on the information that needs to be communicated, later translate that to a more intuitive diagram.

Perhaps @peekpt just meant to unsubscribe. šŸ˜‰

@jtlapp No I'm sorry it was a mistake I'm so tired that I press the wrong button.

@peekpt, @bradyt, it's easy for me to imagine getting this onslaught of notifications and thinking, "Get me out of here!" :-)

Glancing through my design patterns books right now, they ALL use UML. It seems that anyone who is familiar with design patterns is going to have some familiarity with UML.

But I am open to creating diagrams that look less formal and more inviting.

I'm still confused, I'm not used to UML and I know how Provider works, the thing is for young comers this could be a bit a pain to figure out.

I think you should separate like this:

How to inject the value.
How to access the value.
How to make the all value classes ex: ChangeNotifier

Separately. If you are going to interconnect all that will be confusing

Agreed.

Especially so that there's only one way to do these:

  • How to inject the value => InheritedProvider
  • How to access the value => Provider.of

Anything else is just built around these two.

Okay, I'm looking to understand and describe the architecture. The how-tos are important, but I retain information by having a framework on which to hang the pieces.

I set out to solve the problem of not understanding how to use this package after reading the README. I had to read several other tutorial articles and examine the code before it finally clicked. This isn't a problem I expected to have with such a popular package.

Maybe that's just how my mind works. I encountered a few other things that might be important to add to the README as how-tos (e.g. the utility of ValueListenerProvider), but I was otherwise mainly looking to add a clear description of the architecture and explain things in terms of that.

If you're just looking for more how-tos, I'm not the person.

You still can do diagrams like I said separately

You still can do diagrams like I said separately

I'm not sure how to separate them other than as I've already done.

Here is injection.

Here is access.

I think you're looking for a different kind of diagram.

Yes, you are still mixing

We already know that consumer request and provider exposes.

A domingo, 1/09/2019, 02:02, Joe Lapp notifications@github.com escreveu:

You still can do diagrams like I said separately

I'm not sure how to separate them other than as I've already done.

Here is injection
https://github.com/rrousselGit/provider/issues/83#issuecomment-526833084
.

Here is access
https://github.com/rrousselGit/provider/issues/83#issuecomment-526831748
.

I think you're looking for a different kind of diagram.

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/rrousselGit/provider/issues/83?email_source=notifications&email_token=AABWJMJJ45RGYVQQYX2TFJLQHMICTA5CNFSM4HPJG42KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5TYBCI#issuecomment-526876809,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AABWJMK2JCFIXEYOJ4BBSJ3QHMICTANCNFSM4HPJG42A
.

Yes, you are still mixing

We already know that consumer request and provider exposes.

How do you separate them? I can drop the explicit request for the value from the diagram for the Provider<T> diagram that uses Consumer<T>, leaving just the incoming value data as I've done in other diagrams. But custom consumers are "injected" by explicitly asking the provider for the value.

In the case of ListenerProvider<T>, I already posted a version that doesn't show the underlying Consumer<T> request. It isn't all that different, but I was uncomfortable with it for not showing how changing the value induces a rebuild.

If you're worried about the apparent complexity of these diagrams, I assure you, this is an extremely simple architecture! Most architectures cannot be so succinctly represented! I think the generic binding diagram is beautiful and shows the brilliance of this pattern.

I have a guess about what the problem may be. Maybe you're expecting the diagram to mirror the code? I can do that! I'll revise to show you how that might look.

@jtlapp if you're planning on taking up the tedious task of creating class diagrams note that there is a package to generate those: https://pub.dev/packages/dcdg

I don't say to mirror code but to mirror the workflow as simple and direct possible, that's the intent. Abstraction.

@jtlapp if you're planning on taking up the tedious task of creating class diagrams note that there is a package to generate those: https://pub.dev/packages/dcdg

That's pretty cool, but that's the last thing I'd want to do. I'd rather look at code. The purpose of the diagrams is to convey how the package works in a glance. The UML I created does that for me. Now I'm trying to figure out how to do it for people who don't read UML.

Here's an attempt at displaying provider by mirroring the code. What would you like to do differently?

code provider

Hey, the ListenableProvider actually looks simpler in this diagram. By imitating the code, the reader already understands control flow, so I don't have to depict that. I only have to depict data flow. But I'm not sure how well this diagram succeeds.

code listenable1

EDIT: Here's an alternative approach.

code listenable2

I realize these actually depict ListenableProvider<T>.value.

I mean like this:

I mean like this:

I think you have to know what all those pieces are before you can begin to make sense of that diagram. It's a puzzle to me. I'm staring at it trying to figure out how to interpret it.

Here's an attempt at StreamProvider<T>. I'm trying to figure out how to generalize all these async value providers in a single diagram.

code stream1

I realize this actually depicts StreamProvider<T>.value.

_was able to take a deeper look at everything_
Thanks for your hard work!

To be honest, I don't understand any of these graphs.
I'm no expert on the topic, so I don't know what the issue is. But what's certain is that they are too complex for what provider is.

It's ultimately nothing but a setState on the top of InheritedProvider.

For example, we could say that StreamProvider used this way:

StreamProvider<T>.value(
  value: yourStream,
  child: child,
);

is similar to:

StreamBuilder<T>(
  stream: yourStream,
  builder: (_, snapshot) {
    return InheritedProvider<T>(
      value: snapshot.data,
     child: child,
    );
  }
)

I'm pretty sure that for most Flutter devs, this comparison is easier to understand than the current iteration of StreamProvider's graph

I'm pretty sure that for most Flutter devs, this comparison is easier to understand than the current iteration of StreamProvider's graph

So I'm off looking up how InheritedProvider works so I can understand your analogy.

I'm new to Flutter. I quickly discovered that I needed a state management tool. So I have a catch-22: become familiar with Flutter so I can pick a state management tool, but pick a state management tool so I can do things with Flutter.

I'm trying to appeal to Flutter newbies like myself.

I've stared at it for a while and don't know what to make of the analogy. The StreamBuilder rebuilds with each stream event, while the StreamProvider doesn't. Assuming there is a consumer below the StreamProvider, that consumer would rebuild, and the there is no difference between the two approaches except that the second rebuilds from the top with each new event.

I'm not sure where to take that.

while the StreamProvider doesn't

It does.
In fact, the current implementation of StreamProvider uses StreamBuilder internally. https://github.com/rrousselGit/provider/blob/master/packages/provider/lib/src/async_provider.dart#L144

while the StreamProvider doesn't

It does.
In fact, the current implementation of StreamProvider uses StreamBuilder internally.

Oh! So StreamProvider is carrying forward a child that isn't rebuilt?

Somehow, yes. It's "magical" thanks to how the inner of Flutter works.
Basically, if the instance of a widget did not change, build is not called again.

So while StreamProvider internally rebuilds, the child parameter is still the same, and therefore child does not rebuild.

In using the provider package I found myself thinking that providers are not rebuilt with each change of value. That thinking suited me well for the simple experiments I was doing. Would you like the reader to think of providers as being rebuilt? If so, how does that help the reader?

Or is there some other takeaway I need to get here?

With provider, it is not Consumer that listens to an object, but the provider.
What Consumer listen is changes to InheritedProvider.

That's pretty important. It means that, for most use-cases, there's only one listener to an object and it persists for the whole life of the app.

On one hand:

  • it's a huge simplification of the listening process (consumers can listen to anything without having to call addListener/listen/...).
  • it gives a pretty significant scalability boost to ChangeNotifier. That class doesn't scale with many listeners, as stated by the documentation

On the other hand, it means that the listener is still there even if the class is not used at a given time (which can have some impacts with streams for example).

With provider, it is not Consumer that listens to an object, but the provider.
What Consumer listen is changes to InheritedProvider.

That's pretty important. It means that, for most use-cases, there's only one listener to an object and it persists for the whole life of the app.

This is pretty awesome! I actually considered that possibility when creating the diagrams. I scanned through the code to find an answer, but apparently I came to the wrong conclusion. I saw listener.addListener(() => setState()) for ListenerProvider and assumed that meant each consumer subscribed.

Okay, then my UML diagrams are all wrong in this regard. And my code-like diagrams aren't wrong but mistakenly suggest that the consumers themselves subscribe. It sounds like you'd rather not create that impression? Maybe so that people recognize the benefits they're getting and not suffering the O(n) of ChangeNotifier?

On the other hand, it means that the listener is still there even if the class is not used at a given time (which can have some impacts with streams for example).

So when does the provider unsubscribe from the listener? I assume at least by the time the provider widget gets disposed?

I'll take a break to digest this and see what alternative representations come to mind. Thank you for explaining how things work!

Maybe so that people recognize the benefits they're getting and not suffering the O(n) of ChangeNotifier?

Yes, and raise awareness that doings things like HTTP polling through a StreamProvider above MaterialApp may cause some unneeded requests.

So when does the provider unsubscribe from the listener? I assume at least by the time the provider widget gets disposed?

When the provider is removed from the widget tree, yes.

I have an hanging PR on Flutter repo that would allow removing the listeners when there's no more consumers listening to an InheritedProvider.
But the Flutter team is skeptical sadly.

Okay, I'm going back to the drawing board. Thank you for your patience with me!

Offtopic :
you really must try the new BLoC flutter_bloc, it's very comprehensive, it
uses provider and you don't need worry in using different types of
providers.

Also built_value for immutability

>

you really must try the new BLoC flutter_bloc, it's very comprehensive,

I have. I created a little app that required some state management within the bloc. The resulting code was nearly identical to my implementation of the same app for provider and simpler than my implementation in any other state management package except for scoped_model.

The reason I came back to provider was first that it's less opinionated about how I receive messages from the notifier. I can use accessors on the notifier. Under flutter_bloc I had to subscribe to each data item individually; or if not that, create a new state object having all prior data plus the change and emit that. flutter_bloc can't emit the notifier itself.

Second, the flutter_bloc solution seems so heavy weight. Under provider I can choose my implementation and don't have to both message the notifier and receive message from the notifier via separate streams. I also don't have to translate each service call to an event.

I was anticipating my apps having a lot of unnecessarily complexity down the road, particularly upon learning that BLoC came out of the Angular work. I found Angular excessively complicated and am not fond of how they think about architecture.

Finally, Google seems to be making a push for provider, despite being the ones to introduce the BLoC approach. I figured they're seeing something I'm not.

it uses provider and you don't need worry in using different types of

I should look into that more. The two examples flutter_bloc solutions I investigated in the package provided horrible implementations. For example, the to-do list example rebuilt the entire list every time a to-do changed. My general sense was that efficiency was not a concern.

Thanks for the recommendation!

The advantage I see in provider it's the possibility of rebuilding a list
item instead of all the list, 100% agreement on that point.

I have app news app on the market that has infinite scroll and has to
update the entire list everytime it grows and it works flawlessly I can
scroll for minutes, no worries whatsoever.

All this is possible because you have a list builder that lazy loads and
unloads the items of the list, so you don't need to be worried about this
kind management.

A domingo, 1/09/2019, 17:31, Joe Lapp notifications@github.com escreveu:

you really must try the new BLoC flutter_bloc, it's very comprehensive,

I have. I created a little app that required some state management within
the bloc. The resulting code was nearly identical to my implementation of
the same app for provider and simpler than my implementation in any other
state management package except for scoped_model.

The reason I came back to provider was first that it's less opinionated
about how I receive messages from the notifier. I can use accessors on the
notifier. Under flutter_bloc I had to subscribe to each data item
individually; or if not that, create a new state object having all prior
data plus the change and emit that. flutter_bloc can't emit the notifier
itself.

Second, the flutter_bloc solution seems so heavy weight. Under provider I
can choose my implementation and don't have to both message the notifier
and receive message from the notifier via separate streams. I also don't
have to translate each service call to an event.

I was anticipating my apps having a lot of unnecessarily complexity down
the road, particularly upon learning that BLoC came out of the Angular
work. I found Angular excessively complicated and am not fond of how they
think about architecture.

Finally, Google seems to be making a push for provider, despite being the
ones to introduce the BLoC approach. I figured they're seeing something I'm
not.

it uses provider and you don't need worry in using different types of

I should look into that more. The two examples flutter_bloc solutions I
investigated in the package provided horrible implementations. For example,
the to-do list example rebuilt the entire list every time a to-do changed.
My general sense was that efficiency was not a concern.

Thanks for the recommendation!

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/rrousselGit/provider/issues/83?email_source=notifications&email_token=AABWJMLMZDZNHFKO2BUUSM3QHPU7ZA5CNFSM4HPJG42KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5UF7UA#issuecomment-526933968,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AABWJMPFH7SIQJWCOES5EDDQHPU7ZANCNFSM4HPJG42A
.

I think I've corrected the UML here for ListenableProvider. It's considerably more complicated. I needed to do UML first to accurately capture my understanding so I can think about other ways to represent this. Okay, now to see if I can find a simpler representation...

uml listenable1

Well, here's a first stab at a code-like version of ListenableProvider<T>. I'm struggling here.

code listenable3b

@peekpt, I wish I understood what you were trying to do, because I need a kick in a new direction. Were you drawing a dependency tree?

Here's an approach that suggests the provider is the subscriber. It requires explanatory text, though. I'm not sure how to do this with a Listenable though, because the Listenable is the value.

code stream2

This is about as abstract as I can make it. It leaves a lot unsaid.

pure provider2

Or maybe this, if you like:

pure provider2c

Or maybe the rebuild circle should go on each consumer, so it doesn't look like the whole thing is getting rebuilt?

For rebuilds, we'll likely need an animated diagram that looks like a widget tree

I don't think UML can explain clearly rebuilds. It's more focused on relationships

I don't seem to be getting any traction here. Probably time to make this a side project.

Here's my latest UML that generically attempts to capture everything:

uml general1

EDIT: Revised to show value at notification.

Here I've also made the consumer generic:

uml generic2

EDIT: Revised to show value at notification.

I think to get any further I'm going to need help from people who both do diagrams and are familiar with provider. So I'm thinking that I should post an article to Medium.com showing my understanding in terms of these diagrams and asking for corrections or better approaches.

Good idea!
Post the link here when it's done 😊

In fsct, it may be interesting to add the article in the Readme, until provider gets a proper website.

Remi, you've helped correct some of my misunderstandings, and I've incorporated those corrections into the diagrams, so I'd like to acknowledge your help in the article. At the same time, you express discomfort with the diagrams, so I don't want to create the impression that you approve of the approach. How would you like me to handle that? I could just reference this discussion, but I'm not sure we want everyone joining in here (or do we?).

Wait it provides a stream or a streamcontroller?

Remi, you've helped correct some of my misunderstandings, and I've incorporated those corrections into the diagrams, so I'd like to acknowledge your help in the article. At the same time, you express discomfort with the diagrams, so I don't want to create the impression that you approve of the approach. How would you like me to handle that? I could just reference this discussion, but I'm not sure we want everyone joining in here (or do we?).

I find them difficult to understand, but as long as they are "correct", then there's nothing wrong with showing them.
So I'll gladly accept to add a link in the readme to redirect to your article šŸ˜„

On the other hand, adding them directly inside the readme (not as a link) may make peoples "fear" provider a bit if they don't understand these graphs

If anyone could possible do an animated diagram like the ones in RxDart, easy to understand.

Yeah that's what I mentionned earlier.

RxDart's graph does not really fit provider, but something animated using flutter_web is definitely a good idea

I was thinking of something that looks like a widget tree

It can be a presentation converted to gif, or a video with voice over

With flutter_web I really think a playground is ideal.

Something like that

On the other hand, adding them directly inside the readme (not as a link) may make peoples "fear" provider a bit if they don't understand these graphs

Excellent point. If they don't understand the diagram outright, they might move on.

I see three possible levels of abstraction for a GIF animation, where value is an instance of data that provider makes available to consumers:

  • Show the value as a symbol (e.g. a circle or hexagon) moving through a generic diagram.
  • Choose a value type (e.g. number of string) and show it moving through a generic diagram.
  • Choose a value type (e.g. number of string) and show it moving through a diagram that ultimately renders the value within a consumer. For example, the consumer might build a Text widget that shows the latest value rendered within the text.

Would something like this work? This is the most abstract way to do it.

sequence1

That model mainly works for streams and futures. I'd probably want a different one for listenables.

IMO animated diagrams should be a codelab using flutter web.

Because provider has other "gotcha" that can't be represented using such diagram, like providers being scoped to a widget tree

IMO animated diagrams should be a codelab using flutter web.

Can you embed that in the documentation? In the README?

No. But the readme is starting to get pretty big already.

I'm thinking of hosting an actual website generated from markdown files, which would support iframes

Okay, so I'm thinking the README at least needs a clear description of the architecture, but that description would benefit from a diagram or two, even if oversimplified. The text can explain the missing details.

It's usually best to communicate a new idea incrementally, anyway. Besides, when I'm trying to decide which packages to investigate, I want a high level description to help me decide whether to proceed, before I invest time trying to learn the system through something interactive.

Here's an attempt to illustrate a listenable provider.

seq listenable1

Or if you want to be clear that the listenabel has only one subscriber and not one for each consumer:

pure listenable3

EDIT: Replaced with a drawing more suggestive of the consumers having access to the listenable.

Do you have a better name for this diagram? I also thought of "provider pattern," but I don't plan to speak of the pattern generically, only with respect to the provider package.

provider_architecture

(Revised). The text refers to this as "the provider architecture at a conceptual level."

What about "provider's data flow" ?

What about "provider's data flow" ?

It shows that, but it also shows control flow and structure, at least at a very high level.

I mainly don't want readers to think that I'm diagramming the design of your implementation. This is instead a way to think about the package for purposes of using it.

Calling it the "provider pattern" might properly convey that notion.

I've been struggling with the text and the diagrams around the word "value." This term is a parameter name for state data and data sources. Among data sources, it is both the injected data (listeners) and a data source for the injected data (futures and streams). I think I have to relegate it to being a parameter name and nothing more.

Is "data" the intended way to refer to state in provider? I'm having trouble using that term too because this is a mass noun in normal English. "Datum" is the singular, but nobody likes reading that word.

So I'm inclined to refer to "state" and map that term to and from the terms "value" and "data" (e.g. "initialData").

Unless someone can recommend a better way?

Mapping things out, it seems that my error is in always thinking that the data is state. The data is just some value. The only ambiguity is in the name of the provider value parameter. Sometimes it's the data itself, sometimes it's a data source.

I think I'll proceed with the "value" language but distinguish the value from the provider value parameter, which isn't always the value delivered to the consumer.

Provider<T>
    provider value: T (data)
    consumer value: T (data)

ListenableProvider<T>
    provider value: T implements Listenable (data)
    consumer value: T implements Listenable (data)

ChangeNotifierProvider<T>
    provider value: T with ChangeNotifier (data)
    consumer value: T with ChangeNotifier (data)

ValueNotifierProvider<T>
    provider value: ValueNotifier<T> (data source)
    consumer value: T (data)

StreamProvider<T>
    provider value: Stream<T> (data source)
    consumer value: T (data)

StreamProvider<T>.controller
    provider value: StreamController<T> (data source)
    consumer value: T (data)

FutureProvider<T>
    provider value: Future<T> (data source)
    consumer value: T (data)

Nope, it's not working. I'm having to use the word "state" instead of "value." The provider maintains the state for passing on to the consumers. If I show providers maintaining something called "value" being passed on to consumers, and the API sometimes called a data source (e.g. a future or stream) a value, then the diagrams suggest that the future or stream gets passed to the consumer as the value parameter of builder(). The diagrams are supposed to help clarify, not confuse, so I think I have to avoid using the word "value" except in code samples.

I'm about halfway done with the article. Writing forces me to achieve clarity and my diagrams have been evolving accordingly. I can see why you might have had misgivings with my initial diagrams! Here's my latest "architecture" diagram:

uml provider_architecture 150pct

Nice!

What are the numbers referring to? Like 1, 8: build, what are these 1, 8?

Nice!

Thanks! It's starting to look pretty, anyway.

What are the numbers referring to? Like 1, 8: build, what are these 1, 8?

Sequence of events, numbered 1 through 11 here.1, 8: build means that the first and eighth events are builds.

There really are two sequences here, 1 through 4 and 5 through 11. I'd rather not present 5 as necessarily following 4, but I haven't found a better way to put it all in one diagram. The only alternative I came up with was to number them either 1A-5A and 1B-7B or 1A-1E and 2A-2G, but those approaches made the diagram quite a bit harder to read, even using lowercase letters.

The article discusses these sequences separately and should clarify the diagram.

FYI, I've already changed 2, 9: request T state to 2, 9: request T, optionally subscribe.

Nice!

Also, it's only nice looking because provider is elegantly designed. This is your architecture. Powerfully flexible, and yet so simple -- and why I've chosen to embrace it.

Okay Remi, I've finally got drafts to share with you. I'd be grateful for any feedback you can offer, particularly letting me know anything I've got wrong.

I don't state why I wrote this series of articles until the conclusion:

"I wrote this series of articles primarily to help myself come to a better understanding of the provider package and how it works with Flutter. While I'm not new to Android development, I am new to Flutter and reactive programming. This series documents the pieces I felt I was missing. I put a little extra effort into it hoping to help other Flutter newbies."

I made a change. I moved discussion of dependency injection from the first article to a new section in the last article called "Dependency Injection and Data Binding."

I tried to avoid describing things as "binding" because the docs don't do that, but now that I'm done, I'm thinking the language of the article could have been a bit simpler had I referred to injecting values, some of which are bindable. As you see from my original UML above, I actually started in that direction. I moved away from it because I didn't want to be seen as offering a new take on what you did. But the 'state' language doesn't work for constant dependencies.

Maybe all I need to do is change "state" to "value" and "state source" to "value source."

Or maybe I could say that a provider provides "values" and when constructed with a "state source" the value is a "state." Then I can refer to injecting unchanging values and changing state. This should be a pretty simple fix, although extensive.

I'm gearing up for revising everything using "value," either "state source" or "data source," and saying that the value is a "state" when using a state source (or data source). There will be more to do than just simple word replacement, as I'll need to emphasize the use of non-state values early on.

I'm not keen on calling it "injection" and "injecting" throughout the article. I'd rather say "shares", "provides", or "exposes to," and leave discussion of dependency injection for the end.

For the record, I've decided to go with "state source" rather than "data source" because I'm more comfortable calling a model the former. I'm now changing the emphasis in the articles from states to values, making state one use case for values.

Okay, I've finished revising the articles to refer to "values" as much as possible. A value is a "state value" or "state" when supplied by a state source. This was a bit challenging to do while keeping everything clear. I had to reword quite a few sentences.

Most of the edits are minor, but I did add two paragraphs to the "Providing Values" section in the first article explaining what values are and why provider "provides" them.

I've posted them as three new articles. I'll keep the prior articles around a little while longer.

As usual, I'm eager for your feedback. Once I'm happy with them, I'll submit them to a Medium publications, probably Flutter Community.

Thanks!

I'm preparing for a talk in Greece tomorrow, so I'm a bit busy. I'll take a look this week-end

I'm preparing for a talk in Greece tomorrow, so I'm a bit busy. I'll take a look this week-end

Blow them away with your talk! I'll wait 'til next week to publish, then. Thanks so much!

Hey @rrousselGit, how did your talk go? Are you still planning to get to my articles this weekend?

@rrousselGit, thank you for all your notes so far. I believe I've accommodated all of them.

@jtlapp what a great series of articles !!! Your explanations are so well structured and clear, really really impressive.

Here are some modest notes I took while reading your articles :

  • Article 1 : in section "Architecture for Retrieving Values", maybe it would be useful for beginners to mention again 'Consumer' (and the fact that Consumer is part of the provider package). Provider.of is really what does the job, but beginners are more exposed to examples that uses 'Consumer'. It is just a suggestion : as you wrote in your third articles, your series is not meant to be a tutorial, so referring only to the fundation (Provider.of) is understandable.

  • Article 2 : parts of the conclusion confuse me :

"The provider subscribes to the state source to receive state change notices. Some state sources provide the new state value along with the notice. Futures and streams are examples of such state sources. Other state sources, such as ValueListenable (including ChangeNotifier), only provide the notice. "
-> (I really like the disctinction between 'state source' and 'state value' :+1: )
You wrote 'ValueListenable (including ChangeNotifier)', ChangeNotifier is not a ValueListenable, did you mean 'Listenable' here ?

"In this latter case, the provider retrieves the state value from the state source, except when the state source is itself the value provided to dependents."
-> If "in this latter case" refers to ValueListenable and ChangeNotifier, i'm puzzled with the word "except" because with ChangeNotifier the value provided will be mostly the state source, no ?

"When using a ChangeNotifier, or when using a ValueListenable with a ValueListenableProvider, the state source is the value provided to dependents. The dependents themselves read state directly from the state source, or they directly access the state source to change its state."
-> I have yet to use ValueListenableProvider ;-), but I understood that it provides the value from the ValueListenable. Maybe you meant 'Listenable' and 'ListenableProvider' ?

Keep up the good work !

@jtlapp what a great series of articles !!! Your explanations are so well structured and clear, really really impressive.

Thank you so much!

Here are some modest notes I took while reading your articles :

  • Article 1 : in section "Architecture for Retrieving Values", maybe it would be useful for beginners to mention again 'Consumer' (and the fact that Consumer is part of the provider package). Provider.of is really what does the job, but beginners are more exposed to examples that uses 'Consumer'. It is just a suggestion : as you wrote in your third articles, your series is not meant to be a tutorial, so referring only to the fundation (Provider.of) is understandable.

Good idea. I'll look at moving the first mention of Consumer up to that section.

  • Article 2 : parts of the conclusion confuse me :

"The provider subscribes to the state source to receive state change notices. Some state sources provide the new state value along with the notice. Futures and streams are examples of such state sources. Other state sources, such as ValueListenable (including ChangeNotifier), only provide the notice. "
-> (I really like the disctinction between 'state source' and 'state value' šŸ‘ )
You wrote 'ValueListenable (including ChangeNotifier)', ChangeNotifier is not a ValueListenable, did you mean 'Listenable' here ?

Oops, that should have been "'ValueListenable (including ValueNotifier)'". Good catch!

"In this latter case, the provider retrieves the state value from the state source, except when the state source is itself the value provided to dependents."

-> If "in this latter case" refers to ValueListenable and ChangeNotifier, i'm puzzled with the word "except" because with ChangeNotifier the value provided will be mostly the state source, no ?

Yeah, I think you're puzzled because it was ChangeNotifier that was incorrect -- it should have been ValueNotifier.

"When using a ChangeNotifier, or when using a ValueListenable with a ValueListenableProvider, the state source is the value provided to dependents. The dependents themselves read state directly from the state source, or they directly access the state source to change its state."
-> I have yet to use ValueListenableProvider ;-), but I understood that it provides the value from the ValueListenable. Maybe you meant 'Listenable' and 'ListenableProvider' ?

Another good catch. Yes, I meant Listenable with ListenableProvider. Looks like I was tying myself in knots!

Thank you for taking the time to review my articles!

-> If "in this latter case" refers to ValueListenable and ChangeNotifier, i'm puzzled with the word "except" because with ChangeNotifier the value provided will be mostly the state source, no ?

Oh, this exception is confusing after making the correction. I don't even need it, because the next paragraph says everything. Deleted. Thanks so much!

@bleroux, may I thank you for your feedback by name at the end of the article? If so, I'll need your name!

I haven't gotten any more feedback in a while, so I assume it's ready to go.

Thank you everyone for your help! Here are my achnowledgements. Let me know if you'd like something different:

I am grateful to Remi Rousselet, the author of the provider package, for patiently answering my many questions and for enduring my failed early efforts to produce diagrams capturing the provider architecture. I also thank Remi Rousselet, Bruno Leroux, and Brady Trainor for their feedback on earlier drafts of this series. Any issues that remain are entirely my responsibility.

Okay, I'm all set to submit to the Flutter Community publication on Medium. I'll go ahead and submit this evening if I don't get any more feedback.

@jtlapp Is there a link you can post here?

The articles are now all published in Flutter Community on Medium.com.

(Updated links for final publication on Medium)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

FaizanKamal7 picture FaizanKamal7  Ā·  6Comments

rrousselGit picture rrousselGit  Ā·  3Comments

MaxTenco picture MaxTenco  Ā·  5Comments

BazinC picture BazinC  Ā·  6Comments

prasadsunny1 picture prasadsunny1  Ā·  6Comments