Bloc: Are you confident the BlocProvider will work as intended?

Created on 30 Nov 2018  ·  8Comments  ·  Source: felangel/bloc

Looking at the BlocProvider code I found that you are using the BuildContext.ancestorWidgetOfExactType method instead of
BuildContext.inheritFromWidgetOfExactType
From what I could gather this will prevent Widgets that use the BlocProvider from rebuilding when the Bloc is swapped for another one.
I have already created a version that derives BlocProvider from StatelessWidget because I don't need the addditional overhead that comes with using InheritedWidget.
I haven't made a pull request so far as I have no time to make the required tests to support my claim. Let me know if you are interested.

enhancement

All 8 comments

Hi @Soundbytes to clarify, the use case you're describing is when a BlocProvider's bloc has changed, the widget subtree should rebuild correct?

Also, can you please elaborate a bit on what you mean by not needing additional overhead from InheritedWidget?

It would also be super useful if you can include an example of your particular use case.
Thanks!

Yep that is the use case I was describing. Note that it is not what I want to have and I also don't think anybody will need it. Instead I am referring to it because that is what InheritedWidget was made for.
As I said I don't think that it will work at all with the BlocProvider because it requires to use a particular BuildContext method that makes some provisions behind the scenes to enable the behavior
inheritFromWidgetOfExactType
Since it won't work anyway with the method used in your code
ancestorWidgetOfExactType I'd propose to remove the inherited widget. I don't think it will be missed.

Read this brilliant article for more information on the overhead introduced by InheritedWidget in comparison to a plain StatelessWidget: Reactive Programming — Streams — BLoC
(scroll down to the Why not using an InheritedWidget? paragraph)

Here is the modified code. It seems to work fine. I am afraid I had no time to write tests. At least your tests did all pass. (and it did not break my app)

class BlocProvider<T extends Bloc<dynamic, dynamic>> extends StatelessWidget {
  const BlocProvider({
    Key key,
    @required this.bloc,
    @required this.child,
  })  : assert(bloc != null),
        super(key: key);

  /// The Bloc which is to be made available throughout the subtree
  final T bloc;

  final Widget child;

  /// Method that allows widgets to access the bloc as long as their `BuildContext`
  /// contains a `BlocProvider` instance.
  static B of<B extends Bloc<dynamic, dynamic>>(BuildContext context) {
    Type typeOf<B>() => B;
    BlocProvider<B> provider =
        context.ancestorWidgetOfExactType(typeOf<BlocProvider<B>>());
    return provider.bloc;
  }

  @override
  Widget build(BuildContext context) => child;
}

Oops, pressed the wrong button.

@Soundbytes so I completely agree that I the current implementation should have been using inheritFromWidgetOfExactType instead of ancestorWidgetOfExactType; however, I'm not quite sure I understand why the StatelessWidget implementation will perform better than the InheritedWidget implementation with inheritFromWidgetOfExactType.

Take the case where we have a really deep widget tree and we are calling BlocProvider.of(context):

Scenario A. If the implementation is using StatelessWidget and ancestorWidgetOfExactType, this operation will take O(n).
Scenario B. If the implementation is using InheritedWidget and inheritFromWidgetOfExactType, this operation will take O(1).

I understand that in scenario B, the widget will now be rebuilt more often but I'm not confident that A is definitively more performant than B. Thoughts?

I did a quick performance test and it seems like using StatelessWidget does perform better so I made the change in #21.

Thanks for bringing this up! 💯

May I ask about some more informations about those tests? I'm really interested in this discussion

Could you please provide more information on your performance testing? Thanks!

sure I’ll publish the performance tests I ran in a couple of hours.

The basic setup was an app with a deep widget tree and a BlocProvider at the root. Along the way down the sub tree I made every widget request for the bloc via the BuildContext and then looked at TimelineSummary for various performance statistics.

I did several trials with each of the three setups:
InheritedWidget using ancestorWidgetOfExactType

InheritedWidget using inheritFromWidgetOfExactType

StatelessWidget using ancestorWidgetOfExactType

performance test setup + results can be found here

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nhwilly picture nhwilly  ·  3Comments

wheel1992 picture wheel1992  ·  3Comments

craiglabenz picture craiglabenz  ·  3Comments

krusek picture krusek  ·  3Comments

timtraversy picture timtraversy  ·  3Comments