ookami-kb / storybook_flutter

A storybook for Flutter widgets.
https://pub.dev/packages/storybook_flutter
MIT License
284 stars 63 forks source link

"Could not find the correct Provider..." when the DevTools is open #121

Open bramp opened 12 months ago

bramp commented 12 months ago

I spent a hour or two trying to debug this, but sadly I am no closer :(

When the Flutter DevTools Performance Tools are open, and I am running the storybook in Profile mode (e.g flutter run --profile), I get the following exception when I click on any Story:

I/flutter ( 4503): Error: Could not find the correct Provider<DeviceFrameDataNotifier> above this SingleChildBuilder Widget
I/flutter ( 4503):
I/flutter ( 4503): This happens because you used a `BuildContext` that does not include the provider
I/flutter ( 4503): of your choice. There are a few common scenarios:
I/flutter ( 4503):
I/flutter ( 4503): - You added a new provider in your `main.dart` and performed a hot-reload.
I/flutter ( 4503):   To fix, perform a hot-restart.
I/flutter ( 4503):
I/flutter ( 4503): - The provider you are trying to read is in a different route.
I/flutter ( 4503):
I/flutter ( 4503):   Providers are "scoped". So if you insert of provider inside a route, then
I/flutter ( 4503):   other routes will not be able to access that provider.
I/flutter ( 4503):
I/flutter ( 4503): - You used a `BuildContext` that is an ancestor of the provider you are trying to read.
I/flutter ( 4503):
I/flutter ( 4503):   Make sure that SingleChildBuilder is under your MultiProvider/Provider<DeviceFrameDataNotifier>.
I/flutter ( 4503):   This usually happens when you are creating a provider and trying to read it immediately.
I/flutter ( 4503):
I/flutter ( 4503):   For example, instead of:
I/flutter ( 4503):
I/flutter ( 4503):   ```
I/flutter ( 4503):   Widget build(BuildContext context) {
I/flutter ( 4503):     return Provider<Example>(
I/flutter ( 4503):       create: (_) => Example(),
I/flutter ( 4503):       // Will throw a ProviderNotFoundError, because `context` is associated
I/flutter ( 4503):       // to the widget that is the parent of `Provider<Example>`
I/flutter ( 4503):       child: Text(context.watch<Example>().toString()),
I/flutter ( 4503):     );
I/flutter ( 4503):   }
I/flutter ( 4503):   ```
I/flutter ( 4503):
I/flutter ( 4503):   consider using `builder` like so:
I/flutter ( 4503):
I/flutter ( 4503):   ```
I/flutter ( 4503):   Widget build(BuildContext context) {
I/flutter ( 4503):     return Provider<Example>(
I/flutter ( 4503):       create: (_) => Example(),
I/flutter ( 4503):       // we use `builder` to obtain a new `BuildContext` that has access to the provider
I/flutter ( 4503):       builder: (context, child) {
I/flutter ( 4503):         // No longer throws
I/flutter ( 4503):         return Text(context.watch<Example>().toString());
I/flutter ( 4503):       }
I/flutter ( 4503):     );
I/flutter ( 4503):   }
I/flutter ( 4503):   ```
I/flutter ( 4503):
I/flutter ( 4503): If none of these solutions work, consider asking for help on StackOverflow:
I/flutter ( 4503): https://stackoverflow.com/questions/tagged/flutter
I/flutter ( 4503):
I/flutter ( 4503): #0      Provider._inheritedElementOf (package:provider/src/provider.dart:343)
I/flutter ( 4503): #1      Provider.of (package:provider/src/provider.dart:293)
I/flutter ( 4503): #2      WatchContext.watch (package:provider/src/provider.dart:693)
I/flutter ( 4503): #3      _buildStoryWrapper (package:storybook_flutter/src/plugins/device_frame.dart:28)
I/flutter ( 4503): #4      SingleChildBuilder.buildWithChild (package:nested/nested.dart:361)
I/flutter ( 4503): #5      SingleChildStatelessElement.build (package:nested/nested.dart:277)
I/flutter ( 4503): #6      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5297)
I/flutter ( 4503): #7      Element.rebuild (package:flutter/src/widgets/framework.dart:5016)
I/flutter ( 4503): #8      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5279)
I/flutter ( 4503): #9      ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273)
I/flutter ( 4503): #10     SingleChildWidgetElementMixin.mount (package:nested/nested.dart:222)
I/flutter ( 4503): #11     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182)
I/flutter ( 4503): #12     Element.updateChild (package:flutter/src/widgets/framework.dart:3707)
I/flutter ( 4503): #13     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5322)
I/flutter ( 4503): #14     Element.rebuild (package:flutter/src/widgets/framework.dart:5016)
I/flutter ( 4503): #15     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5279)
I/flutter ( 4503): #16     ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273)
I/flutter ( 4503): #17     _NestedHookElement.mount (package:nested/nested.dart:187)
I/flutter ( 4503): #18     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182)
I/flutter ( 4503): #19     Element.updateChild (package:flutter/src/widgets/framework.dart:3707)
I/flutter ( 4503): #20     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5322)
I/flutter ( 4503): #21     Element.rebuild (package:flutter/src/widgets/framework.dart:5016)
I/flutter ( 4503): #22     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5279)
I/flutter ( 4503): #23     ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273)
I/flutter ( 4503): #24     SingleChildWidgetElementMixin.mount (package:nested/nested.dart:222)
I/flutter ( 4503): #25     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182)
I/flutter ( 4503): #26     Element.updateChild (package:flutter/src/widgets/framework.dart:3707)
I/flutter ( 4503): #27     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5322)
I/flutter ( 4503): #28     Element.rebuild (package:flutter/src/widgets/framework.dart:5016)
I/flutter ( 4503): #29     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5279)
I/flutter ( 4503): #30     ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273)
I/flutter ( 4503): #31     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182)
I/flutter ( 4503): #32     Element.updateChild (package:flutter/src/widgets/framework.dart:3701)
I/flutter ( 4503): #33     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5322)
I/flutter ( 4503): #34     Element.rebuild (package:flutter/src/widgets/framework.dart:5016)
I/flutter ( 4503): #35     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2779)
I/flutter ( 4503): #36     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:916)
I/flutter ( 4503): #37     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:360)
I/flutter ( 4503): #38     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1297)
I/flutter ( 4503): #39     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1227)
I/flutter ( 4503): #40     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1085)
I/flutter ( 4503): #41     _invoke (dart:ui/hooks.dart:170)
I/flutter ( 4503): #42     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:401)
I/flutter ( 4503): #43     _drawFrame (dart:ui/hooks.dart:140)

When I switch back to a debug build, or close the dev tools, the issue goes away.

Steps to repeat:

flutter run --profile
# It will print something like
# The Flutter DevTools debugger and profiler on sdk gphone64 arm64 is available at: http://127.0.0.1:9101?uri=...

Visit the URL in your browser. Now click on a story, and the exception is thrown. The same occurs if using the DevTools via say VSCode. If you don't open the dev tools, then everything seems fine.

bramp commented 12 months ago

I did notice, when I'm in profile, the main runApp widget get's built twice, for example:

void main() {
  runApp(const _StoryBookApp());
}

class _StoryBookApp extends StatelessWidget {
  Widget build(BuildContext context) {
    print("build");
    return Storybook(
      ...
    )
  }
}

When I do MediaQuery.of(context), the first context has a size of Zero, whereas the 2nd time it looks correct. I don't see this pattern when the DevTools are closed. Changing my code to:

  Widget build(BuildContext context) {
    if (MediaQuery.of(context).size.isEmpty) {
      return const Placeholder();
    }
    return Storybook(...);
  }

Seems to actually fix my problem! So ok, this makes me think, somewhere in Storybook something is being cached, and triggering this mess.