Embedded 3D renderers
Handing 3D to an engine that is not Flutter: Filament, a WebView-based viewer, Unity, and AR frameworks. Each buys capability with integration cost, so the question is always what you give up at the boundary.
Contents
- The boundary problem
- Filament via thermion
- model_viewer_plus
- Simple model widgets
- Unity
- Augmented reality
- Comparison
- Evaluating a package
The boundary problem
Every option here runs a renderer Flutter does not control. Three things get harder as a result:
- Composition. The 3D output arrives either as a GPU texture (cheap, and composites like a normal layer) or as a platform view (expensive, and can disturb scrolling and overlays). Prefer texture-based packages.
- State. The renderer has its own state. Keeping "the selected part" or "the camera angle" in sync with Flutter means messages crossing a channel, with latency and serialisation.
- Lifecycle. The engine must be paused when the app is backgrounded and disposed when the route is popped, or it keeps consuming GPU and memory. See Lifecycle.
If none of those matter to your feature, an embedded renderer is the fastest route to a good-looking result. If all of them do, revisit flutter_scene.
Filament via thermion
Filament is Google's physically based
renderer. thermion wraps it for Flutter and renders into a texture.
Strengths:
- Mature, well-documented renderer with real physically based materials, image based lighting from an environment map, shadows, and post-processing.
- glTF and GLB loading with skinning and animation.
- Texture-based composition, so Flutter widgets can be layered over it.
Costs:
- Adds a native renderer to the binary; measure the size impact before committing.
- Support and API maturity vary per platform — check the package's own matrix for Android, iOS, macOS, Windows, and web.
- The scene lives on the native side, so interactions require calls across the boundary.
Shape of the integration:
// Illustrative — follow the package README for the version you pin
final viewer = await ThermionViewer.create();
await viewer.loadSkybox('assets/env/studio_skybox.ktx');
await viewer.loadIbl('assets/env/studio_ibl.ktx');
final entity = await viewer.loadGlb('assets/models/product.glb');
await viewer.setCameraPosition(0, 1.4, 4);
// In build()
ThermionWidget(viewer: viewer);
Bring an environment map. Filament's quality advantage comes largely from image-based lighting, and a physically based model lit by a single directional light looks worse than the same model in a simpler renderer.
model_viewer_plus
model_viewer_plus embeds Google's <model-viewer> web component in a WebView.
It is the shortest path from "we have a GLB" to "it is on screen".
flutter pub add model_viewer_plus
ModelViewer(
src: 'assets/models/product.glb',
alt: 'A 3D model of the product',
ar: true,
autoRotate: true,
cameraControls: true,
disableZoom: false,
backgroundColor: Colors.transparent,
)
Strengths:
- Orbit controls, auto-rotate, animation playback, and AR entry points work with no code.
- On Android it hands off to Scene Viewer and on iOS to AR Quick Look, so "view in your room" is close to free.
- The asset is a plain GLB — no build-time conversion.
Costs:
- A WebView is heavy: extra memory, slower first paint, and rendering that is not part of Flutter's frame pipeline.
- Interaction with Flutter goes through JavaScript. Reading the camera angle or reacting to a tap on a part means bridging, and it will not feel immediate.
- Theming and text rendering inside the viewer are not your app's.
- iOS AR Quick Look expects USDZ for some flows, so you may need a second asset format.
Good for a catalogue detail page. Poor for anything where the 3D view is the interface rather than an illustration.
Simple model widgets
Packages such as flutter_3d_controller sit between the two: a widget that
loads a GLB or OBJ, plays its animations, and exposes a controller for camera
and animation state. They are worth a look when you need slightly more control
than model_viewer_plus without adopting a full engine. Check what they use
underneath — several are WebView-backed, which means they inherit those costs.
Unity
flutter_unity_widget embeds a Unity player and exchanges messages with it.
Use it when the 3D content already exists in Unity, or when you need physics, particles, a full animation system, and an editor workflow that artists already know.
Costs are substantial and should be agreed before starting:
- Two runtimes in one process; binary size grows by tens of megabytes.
- The build requires exporting a Unity project as a library and wiring it into the Android and iOS builds. CI setup is genuinely difficult.
- Communication is string or JSON messages between Dart and C#.
- Platform view composition, with the scrolling and overlay caveats above.
- Upgrades must line up across Flutter, Unity, and the plugin.
Augmented reality
For content anchored in the camera feed, use the platform AR frameworks through a plugin — ARCore on Android, ARKit on iOS. Typical capabilities: plane detection, hit testing against detected surfaces, anchors, and light estimation.
Practical requirements:
- Camera permission, with a clear rationale string on both platforms.
- Device support checks — not every device supports ARCore, and older iPhones lack the sensors for some features. Provide a non-AR fallback.
- Minimum platform versions and, on Android, the ARCore services dependency.
- Testing on hardware; simulators cannot do this at all.
If AR is only "preview this model in your room", model_viewer_plus gets you
there through the system AR viewers without writing AR code.
Comparison
| flutter_scene | Filament (thermion) | model_viewer_plus | Unity | |
|---|---|---|---|---|
| Composition | Painted by Flutter | Texture | WebView | Platform view |
| Visual quality | Basic materials | High, physically based | Good, <model-viewer> defaults | Whatever you build |
| Flutter interop | Direct, same Dart objects | Through the package API | JavaScript bridge | Message channel |
| Binary cost | Small | Moderate | Small (uses system WebView) | Large |
| Build complexity | Build hook for assets | Native dependency | None | High |
| Maturity | Preview | Stable renderer, evolving binding | Stable | Stable but heavy |
| Good for | Interactive product views | Presentation-quality models | Catalogue previews and AR | Games and rich scenes |
Evaluating a package
Before adopting any of these, check the things that decide whether it survives in your project:
- Last release date and commit activity; how quickly it followed the last two Flutter releases.
- The platform support matrix, verified by running the example on your actual targets — not read from the README.
- Whether it composites via texture or platform view.
- Binary size delta, measured with
flutter build apk --analyze-sizebefore and after. - Whether it exposes disposal and pause APIs, and whether the example uses them.
- Licence, for both the package and the renderer it wraps.
Build the smallest possible spike — load one model, rotate it, overlay one widget, background and foreground the app — and measure. That takes an afternoon and prevents a rewrite.
Next: 3D performance.