Skip to content

[BUG] [genui] Modal captures stale Surface state in showModalBottomSheet, crashing on rebuild if trigger Surface is unmounted #2828

Description

@Jungliana
  • I have searched the existing issues to make sure this bug has not already been reported.

Describe the Bug

The basic catalog's Modal component (packages/genui/lib/src/catalog/basic_catalog_widgets/modal.dart) opens a bottom sheet using:

widgetBuilder: (itemContext) {
  final modalData = _ModalData.fromMap(itemContext.data as JsonMap);
  return ElevatedButton(
    onPressed: () {
      showModalBottomSheet<void>(
        context: itemContext.buildContext,
        builder: (context) {
          return itemContext.buildChild(modalData.content);
        },
      );
    },
    child: itemContext.buildChild(modalData.trigger),
  );
},

Notice that both itemContext.buildContext and itemContext.buildChild closure over the _SurfaceState that built the trigger widget.

In Flutter, modal bottom sheets are pushed onto the root Navigator / Overlay and have an independent route lifecycle. If the underlying Surface widget is unmounted while the sheet is still open (for example: user navigates to another tab or view, parent widget tree reparents/rebuilds, or host app hides the surface during an asynchronous agent turn), Flutter clears the State (state._element = null).

When any subsequent rebuild occurs inside the open bottom sheet, buildChild calls _buildWidget in surface.dart:

surfaceId: widget.surfaceContext.surfaceId,

Accessing widget on an unmounted State throws:

TypeError: Null check operator used on a null value

_buildWidget catches this exception and returns FallbackWidget, causing the bottom sheet's content to visibly turn into a red "An error occurred / Null check operator used on a null value" error card before dismissing.

Steps to Reproduce

  1. Render a Surface containing a Modal component.
  2. Tap the modal trigger to open the modal bottom sheet.
  3. While the bottom sheet is displayed, trigger a state change in the host app that unmounts or replaces the parent Surface widget.
  4. Interact with or dismiss the modal sheet, or trigger a rebuild inside the sheet.
  5. See crash / FallbackWidget displaying Null check operator used on a null value.

Expected Behavior

The modal sheet content should be able to rebuild safely even if the trigger widget's _SurfaceState has been unmounted. Either:

  1. The modal builder should mount an independent surface or decoupled child renderer tied to the route rather than holding a direct closure on the unmounted _SurfaceState.
  2. _buildWidget and CatalogItemContext should guard against unmounted state (if (!mounted) return const SizedBox.shrink();) instead of asserting on widget.surfaceContext.

Environment Details

  • SDK/Package Name & Version: package:genui (packages/genui/lib/src/catalog/basic_catalog_widgets/modal.dart, packages/genui/lib/src/widgets/surface.dart)
  • Protocol Version: v0.9.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    • Status
      Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions