Glint

Glint

A pure-Dart 3D engine for Flutter, built on Flutter GPU.

Website · pub.dev · API docs · See it running · FAQ

Glint renders real glTF/GLB models — textures, multi-material meshes, PBR lighting, HDRI reflections, animations — as ordinary Flutter widgets. No platform views, no WebViews, no native code. Overlay Flutter UI on your scene, restyle materials with setState, pin widgets to points on a model, and run a real-time game loop, all from Dart.

Scene3D(
  scene: ProductShowroom(),          // declarative scene description
  onModelTap: (hit) => restyle(hit), // CPU-accurate tap picking
  labels: [
    Label3D(                          // a real Flutter widget, pinned to
      anchor: Vector3(.96, 1.34, 0),  // a point on the model's surface
      child: PriceTag(),
    ),
  ],
)

Watch Duck Dash run at 60 fps on iPhone → An endless runner built entirely on the engine below — imported models, animation, fog, a chase camera, and a Flutter HUD.

What's inside

Capability Detail
glTF 2.0 loading Binary .glb from assets or network; scene hierarchy, node transforms, 16/32-bit indices, normals (parsed or generated)
Materials Per-primitive material batches: base-color textures + factors, metallic, roughness; embedded PNG/JPEG textures (auto-capped at 1K for mobile memory)
Lighting GGX metallic-roughness PBR, directional key light, HDRI image-based lighting (.hdr decoder + irradiance/specular prefiltering built in), distance fog
Animation glTF node animation clips: translation/rotation/scale channels, LINEAR and STEP sampling, quaternion slerp, looping
Interaction Orbit/pan/zoom gestures, scroll-aware mode for scrollable pages, tap picking via CPU raycasting (Möller–Trumbore)
Widgets in 3D Label3D: project any widget onto a model anchor with fade/hide occlusion policies
Game loop GlintGameView: ticker-driven frames, unlimited model instances with per-node transforms, free look-at camera, translucent blob shadows, frustum culling
Diagnostics Live FPS / frame time / draw call / triangle overlay; typed, actionable errors for every asset failure

Status: v0.1, feature-complete for the first release and validated at 60 fps with ~160 draw calls per frame on a physical iPhone and on macOS desktop. Like Flutter GPU itself, Glint is early — expect rough edges, and see Outside v0.1 for what isn't here yet.

Getting started

1. Install

flutter pub add glint_engine

2. Enable Flutter GPU (one-time, per app)

Glint renders through Flutter GPU, which ships behind a flag. Bake it into your app so no run flags are ever needed:

iOS and macOS — add to ios/Runner/Info.plist and macos/Runner/Info.plist:

<key>FLTEnableFlutterGPU</key>
<true/>
<!-- macOS only, Impeller is already the default on iOS: -->
<key>FLTEnableImpeller</key>
<true/>

Android — add inside <application> in android/app/src/main/AndroidManifest.xml:

<meta-data
    android:name="io.flutter.embedding.android.EnableFlutterGPU"
    android:value="true" />

(For a quick trial without editing manifests: flutter run --enable-impeller --enable-flutter-gpu.)

3. Render a model

import 'package:glint_engine/glint_engine.dart';

GlintGpuFirstLight(
  model: Model.asset('assets/product.glb'),           // or Model.network(url)
  environmentAsset: 'assets/studio.hdr',              // optional HDRI
  fallback: const Text('Flutter GPU unavailable'),    // shown when disabled
)

4. Or describe a scene declaratively

class Showroom extends Scene {
  const Showroom({this.finish});
  final Material3D? finish;

  @override
  List<Light3D> get lights => const [
    DirectionalLight(direction: Vector3(.55, -1, -.65), intensity: .87),
    EnvironmentLight(asset: 'assets/studio.hdr'),
  ];

  @override
  List<Node3D> get children => [
    Node3D(
      model: const Model.asset('assets/product.glb'),
      material: finish, // null keeps the model's own material
    ),
  ];
}

// Restyling is just state:
Scene3D(scene: Showroom(finish: selected))

5. Build a game

GlintGameView(
  models: const {
    'hero': Model.asset('assets/hero.glb'),
    'crate': Model.asset('assets/crate.glb'),   // animated GLBs just work
  },
  fogColor: sky, fogDistance: 95,
  onFrame: (dt) {
    world.step(dt);                              // your simulation
    return GlintGameFrame(
      camera: GlintGameCamera(position: eye, target: focus),
      instances: [
        for (final thing in world.things)
          GlintGameInstance(
            model: thing.kind,
            transform: thing.transform,
            animationTime: world.clock,          // loops the model's clip
          ),
      ],
    );
  },
)

The example app ships a complete endless runner (Duck Dash) built this way.

Examples

example/ is a launcher with four demos, each a compact reference:

  • Duck Dash — endless runner: imported multi-material models, animation playback, fog, blob shadows, swipe input, Flutter HUD.
  • Product configurator — a scrollable product page with scroll-aware orbiting, live material swatches, tap-to-restyle, anchored labels.
  • Model viewer — the minimal one-widget integration.
  • Anchored labelsLabel3D occlusion policies in isolation.
git clone https://github.com/kiddo4/glint.git
cd glint/example
flutter run   # manifests already carry the GPU flags

Gesture routing in scrollable pages

Inside a ListView, pass gestureMode: GlintGestureMode.scrollAware: one-finger horizontal drags orbit, one-finger vertical drags keep scrolling the page, two-finger and trackpad pinches orbit, pan, and zoom.

FAQ

What platforms does this package support?

Platform Status
iOS 🟢 Validated — 60 fps on device
macOS 🟢 Validated
Android 🟡 Physical device only — see below
Web / Windows / Linux ⚪ Not yet

Glint follows wherever Flutter GPU runs. On iOS, Impeller is already Flutter's default renderer. On macOS it's opt-in (the manifest key above enables it). On Android, physical devices with Vulkan work well; emulators do not — most lack a real Vulkan driver, so Impeller falls back to its OpenGLES backend, which Flutter GPU does not support yet, and the app aborts natively rather than failing gracefully. There is no Dart-level way to detect this today, so: test on a physical Android device. Web, Windows, and Linux aren't supported because Flutter GPU isn't available there yet.

Isn't this a reinvention of flutter_scene?

Not the same bet. flutter_scene is a general-purpose scene graph; Glint is a product-experience engine built widget-first: scenes are declarative Flutter state (Scene3D, restyled with setState), Flutter widgets anchor to points on a model with real occlusion, gestures cooperate with scrollable pages, and it drives a real-time game loop with a Flutter HUD over it. Owning the whole pipeline — glTF parser, PBR + IBL shaders, picking, animation — is deliberate: every millisecond of the frame is understood, not inherited.

Why do I have to touch platform manifests at all?

Flutter GPU ships behind a flag while it's in preview, and Info.plist/ AndroidManifest have no cross-package way to merge settings into a host app (unlike Android, where a plugin's manifest entries merge automatically — something worth doing for Glint once it's a proper Flutter plugin). Until then it's a one-time, two-line setup per app, documented above.

Troubleshooting

  • "Flutter GPU requires the Impeller rendering backend" — the manifest keys from step 2 are missing, or you ran without the flags.
  • Native crash on Android: render_pass_gles.cc ... Check failed — see the platform FAQ above; run on a physical Vulkan-capable device.
  • "Missing GLB magic header" — the file isn't a binary glTF (.glb). If it lives in Git LFS, run git lfs pull; a 134-byte "file" is an LFS pointer, not a model.
  • Model renders but looks wrong — Glint v0.1 reads base-color textures and factors; normal/ORM maps, Draco compression, and skeletal skinning are not supported yet.
  • Blurry textures on imported assets — embedded textures are decoded at a maximum of 1024px per side to protect mobile memory.

Outside v0.1

Skeletal skinning, morph targets, point/spot lights, shadow mapping, KTX, custom shaders, physics, audio, Web/Windows/Linux. Open an issue if one of these is blocking you — real projects are what decide what comes next.

License

MIT. The Duck model is the Khronos glTF sample asset; see assets/models/ATTRIBUTION.md.

glint.kiddobuild.dev · GitHub · pub.dev

Libraries

glint_engine