Skip to content

showToolbar() and hideToolbar() methods missing listen: false causing assertion error when called from event handlers #278

@nateshmbhat

Description

@nateshmbhat

Problem

The static methods DevicePreview.showToolbar() and DevicePreview.hideToolbar() throw an assertion error when called from event handlers (e.g., button onPressed, switch onChanged) because they call Provider.of<DevicePreviewStore>(context) without the listen: false parameter.

Error Message

Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.

To fix, write:
Provider.of<DevicePreviewStore>(context, listen: false);

Failed assertion: line 308 pos 7: 'context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate'

Current Code (Buggy)

In lib/src/device_preview.dart:

Line 245 - showToolbar() method:

static void showToolbar(
  BuildContext context, {
  bool enablePreview = true,
}) {
  final store = Provider.of<DevicePreviewStore>(context); // ❌ Missing listen: false
  store.data = store.data.copyWith(
    isToolbarVisible: true,
    isEnabled: enablePreview,
  );
}

Line 257 - hideToolbar() method:

static void hideToolbar(
  BuildContext context, {
  bool disablePreview = true,
}) {
  final store = Provider.of<DevicePreviewStore>(context); // ❌ Missing listen: false
  store.data = store.data.copyWith(
    isToolbarVisible: false,
    isEnabled: !disablePreview,
  );
}

Inconsistency

Other static methods in the same file correctly use listen: false:

Line 271 - selectDevice() method (correct):

static void selectDevice(
  BuildContext context,
  DeviceIdentifier deviceIdentifier,
) {
  final store = Provider.of<DevicePreviewStore>(context, listen: false); // ✅ Correct
  store.selectDevice(deviceIdentifier);
}

Suggested Fix

Add listen: false to both methods:

static void showToolbar(
  BuildContext context, {
  bool enablePreview = true,
}) {
  final store = Provider.of<DevicePreviewStore>(context, listen: false);
  store.data = store.data.copyWith(
    isToolbarVisible: true,
    isEnabled: enablePreview,
  );
}

static void hideToolbar(
  BuildContext context, {
  bool disablePreview = true,
}) {
  final store = Provider.of<DevicePreviewStore>(context, listen: false);
  store.data = store.data.copyWith(
    isToolbarVisible: false,
    isEnabled: !disablePreview,
  );
}

Steps to Reproduce

  1. Create a button or switch in your Flutter app
  2. Call DevicePreview.showToolbar(context) or DevicePreview.hideToolbar(context) from the onPressed/onChanged handler
  3. Observe the assertion error

Example Code That Fails

Switch(
  value: isToolbarVisible,
  onChanged: (value) {
    if (value) {
      DevicePreview.showToolbar(context); // ❌ Throws assertion error
    } else {
      DevicePreview.hideToolbar(context); // ❌ Throws assertion error
    }
  },
)

Environment

  • device_preview: ^1.3.1
  • Flutter: 3.x
  • Dart: 3.x

Workaround

Currently, developers must directly access the Provider with listen: false:

final store = Provider.of<DevicePreviewStore>(context, listen: false);
store.data = store.data.copyWith(isToolbarVisible: value, isEnabled: value);

This is a simple fix that would make the API consistent with other methods and prevent runtime errors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions