Skip to main content

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

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_sceneFilament (thermion)model_viewer_plusUnity
CompositionPainted by FlutterTextureWebViewPlatform view
Visual qualityBasic materialsHigh, physically basedGood, <model-viewer> defaultsWhatever you build
Flutter interopDirect, same Dart objectsThrough the package APIJavaScript bridgeMessage channel
Binary costSmallModerateSmall (uses system WebView)Large
Build complexityBuild hook for assetsNative dependencyNoneHigh
MaturityPreviewStable renderer, evolving bindingStableStable but heavy
Good forInteractive product viewsPresentation-quality modelsCatalogue previews and ARGames 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-size before 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.