Skip to content

fix(pdfx): guard PdfViewPinch._needReLayout against setState after dispose - #648

Open
Yusuke-Shimamura wants to merge 1 commit into
ScerIO:mainfrom
Nowledge-Inc:fix/pdf-view-pinch-relayout-mounted
Open

fix(pdfx): guard PdfViewPinch._needReLayout against setState after dispose#648
Yusuke-Shimamura wants to merge 1 commit into
ScerIO:mainfrom
Nowledge-Inc:fix/pdf-view-pinch-relayout-mounted

Conversation

@Yusuke-Shimamura

Copy link
Copy Markdown

What

_PdfViewPinchState._needReLayout schedules setState through Future.delayed(Duration.zero, ...) without checking mounted. When the viewer is unmounted in the same frame that scheduled the relayout (for example, the page hosting PdfViewPinch is popped while a jumpToPage / animateToPage tick or a relayout changed the set of visible pages), the callback lands on a disposed State.

  • In debug builds this is setState() called after dispose().
  • In release builds the assert is stripped and it surfaces as Null check operator used on a null value thrown from State.setState (_element!.markNeedsBuild()), which ends up in the app's zone error handler.

Observed in production on Flutter web with pdfx 2.9.2. Decoded stack:

State.setState                                          flutter/lib/src/widgets/framework.dart:1219
_PdfViewPinchState._needReLayout.<anonymous function>   pdfx-2.9.2/lib/src/viewer/pinch/pdf_view_pinch.dart:345
Future.delayed.<anonymous function>                     dart-sdk/lib/async/future.dart:440

Fix

Check mounted inside the delayed callback, matching the other delayed callbacks in this file (the loading-state listener and the first-attach callback in _reLayout already do this).

Notes

_updateRealSizeOverlay (#598) is a related but different path (async work continuing after dispose) and is not touched here.

🤖 Generated with Claude Code

…spose

_needReLayout schedules setState via Future.delayed(Duration.zero) without
checking `mounted`. If the viewer is unmounted in the same frame that
scheduled the relayout, the callback runs on a disposed State: in debug
this is "setState() called after dispose()", in release the assert is
stripped and it surfaces as "Null check operator used on a null value"
from State.setState. Check `mounted` like the other delayed callbacks in
this file already do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant