fix(flet-charts): prevent unbounded browser memory growth in MatplotlibChart#6473
Merged
FeodorFitsner merged 2 commits intorelease/v0.85.0from May 7, 2026
Merged
fix(flet-charts): prevent unbounded browser memory growth in MatplotlibChart#6473FeodorFitsner merged 2 commits intorelease/v0.85.0from
FeodorFitsner merged 2 commits intorelease/v0.85.0from
Conversation
…ibChart Replace the generic Canvas+capture flow with a dedicated MatplotlibChartCanvas widget that composites matplotlib diff frames in CPU memory. Holds at most one ui.Image at a time, avoiding the Picture.toImage allocations that accumulate on Flutter web (CanvasKit/WASM) and aren't reclaimed by the GC. Also includes: - Thread-safe TimerFletAsyncio so FuncAnimation works while we render in asyncio.to_thread on native runtimes - Lock to serialize render thread with figure.savefig (download) - Coalesce stale binary frames and draw requests during pan/zoom - Defensive Uint8List copy before instantiateImageCodec to fix Safari "EncodingError: Loading error." on async PNG decode - Dispose ui.Image resources and add gapless playback in flet.canvas.Canvas - Pyodide fallback that renders inline (no real threads)
Deploying flet-examples with
|
| Latest commit: |
204e353
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://efbdb9cd.flet-examples.pages.dev |
| Branch Preview URL: | https://web-memory-crash.flet-examples.pages.dev |
Deploying flet-website-v2 with
|
| Latest commit: |
204e353
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://fd08f26a.flet-website-v2.pages.dev |
| Branch Preview URL: | https://web-memory-crash.flet-website-v2.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
flet.canvas.Canvas+capture()flow used byMatplotlibChartwith a dedicatedMatplotlibChartCanvaswidget that composites matplotlib diff frames in CPU memory. Holds at most oneui.Imageat a time, avoiding thePicture.toImageallocations that accumulate on Flutter web (CanvasKit/WASM) and aren't reclaimed by the JS GC.What's in the PR
New widget
MatplotlibChartCanvas(Python control + Flutter widget) withapply_full(bytes)/apply_diff(bytes)/clear()invoke methods. Decodes PNGs, keeps aUint8Listbackbuffer, composites diff frames where alpha != 0, and shows the result via a singleui.Imagerebuilt withdecodeImageFromPixels.Backend / chart wiring
MatplotlibChartswitches fromfc.Canvasshapes +capture()to the new widget. Rubberband (zoom selection) becomes a positionedContaineroverlay in aStack.TimerFletAsynciosubclass captures the main asyncio loop at construction soFuncAnimationkeeps working when_timer_start()is invoked from a worker thread (used by our threaded render path).asyncio.to_threadon native runtimes so the asyncio loop stays free for pointer events. Pyodide / WASM falls back to inline rendering with aggressive yields, sinceto_threadruns synchronously there.threading.Lockserializes worker-thread renders against main-threadfigure.savefigto prevent theprint_figuremanager=Nonerace that otherwise crashes Download.drawJSON requests so pan/zoom doesn't visibly play back buffered motion.flet core (
flet.canvas.Canvas) image disposal_capturedImage/ shape_image/ intermediatePictureandCodecafter replacement (post-frame for safety).drawImage: keep the cached image visible while new bytes decode, instead of going blank for a frame.Safari fix
Uint8List.fromList(bytes)copy beforeinstantiateImageCodec. Safari's WASM runtime can free buffers across async boundaries and otherwise throwsEncodingError: Loading error.Test plan
examples/extensions/charts/matplotlib_chart/animatein Edge — memory stays flat, animation smooth.EncodingError, memory flat.examples/extensions/charts/matplotlib_chart/toolbar— pan/zoom smooth, Download saves the file.Summary by Sourcery
Replace MatplotlibChart’s generic Canvas-based rendering with a dedicated image-stream canvas to stop unbounded browser memory growth and keep interactions responsive across platforms.
New Features:
Bug Fixes:
Enhancements: