three_d_ui_builder

Build futuristic 3D UIs with Flutter-native widgets.
No OpenGL. No shaders. No GPU scene-graph boilerplate.
Just Flutter.

pub version license: MIT Flutter Dart


Why three_d_ui_builder?

Most Flutter 3D solutions fall into one of two traps:

Problem Example
Dead / unmaintained flutter_3d — no commits since 2021, broken on Flutter 3+
Too low-level flutter_scene — excellent GPU engine, but you must work with raw geometry and scene nodes

three_d_ui_builder sits in the gap: it gives you fully interactive 3D widgets with a widget-tree API identical to the one you already know.

// Before — plain Flutter
ElevatedButton(child: Text('Buy'), onPressed: buy);

// After — same idea, 3D depth effect included
ThreeDButton(label: Text('Buy'), depth: 8, onPressed: buy);

No vertex buffers. No GLSL. No Impeller flags. Just widgets.


Installation

Add the dependency to your pubspec.yaml:

dependencies:
  three_d_ui_builder: ^0.1.0

Then run:

flutter pub get

Import the single barrel file in any Dart file:

import 'package:three_d_ui_builder/three_d_ui_builder.dart';

Quick Start

Wrap your app (or any subtree) with ThreeDTheme to configure global defaults, then drop in any widget:

import 'package:flutter/material.dart';
import 'package:three_d_ui_builder/three_d_ui_builder.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ThreeDTheme(
      data: ThreeDThemeData.defaults(),
      child: MaterialApp(
        title: '3D UI Demo',
        home: const HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // ── 3D button ──────────────────────────────────────────────
          ThreeDButton(
            label: const Text('Launch'),
            depth: 8,
            faceColor: Colors.deepPurple,
            onPressed: () {},
          ),

          const SizedBox(height: 32),

          // ── Flip card ──────────────────────────────────────────────
          FlipCard3D(
            front: const Card(child: Center(child: Text('Front'))),
            back:  const Card(child: Center(child: Text('Back'))),
          ),

          const SizedBox(height: 32),

          // ── Circular list ──────────────────────────────────────────
          SizedBox(
            height: 320,
            child: CircularList3D(
              itemCount: 6,
              radius: 160,
              autoRotate: true,
              itemBuilder: (ctx, i) => Container(
                width: 80, height: 80,
                color: Colors.primaries[i * 4 % Colors.primaries.length],
                child: Center(child: Text('$i')),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Buttons

ThreeDButton

A button with a physical depth slab that presses in on tap.

ThreeDButton(
  label: const Text('Click Me'),
  depth: 8.0,
  faceColor: Colors.indigo,
  sideColor: Colors.indigo.shade900,   // optional — auto-derived if omitted
  borderRadius: 16,
  showHighlight: true,
  onPressed: () => print('Pressed!'),
)
Parameter Type Default Description
label Widget required Content rendered on the button face
depth double 6.0 Z-depth of the 3D slab in logical pixels
faceColor Color Color(0xFF5C6BC0) Top-surface colour
sideColor Color? auto-derived Side-slab colour
borderRadius double 12.0 Corner radius
showHighlight bool true Specular streak on the top edge
onPressed VoidCallback? null Tap handler; null = disabled

FloatingAction3D

A floating action button with a pulsing 3D levitation effect.

FloatingAction3D(
  icon: Icons.add,
  backgroundColor: Colors.teal,
  pulseRadius: 8,
  onPressed: () {},
)

IconButton3D

An icon that rotates around its Y-axis on press.

IconButton3D(
  icon: Icons.favorite,
  color: Colors.red,
  onPressed: () {},
)

Cards

FlipCard3D

A card that flips between front and back faces with a perspective-correct 3D animation.

final controller = FlipCard3DController();

FlipCard3D(
  controller: controller,
  front: const FrontWidget(),
  back:  const BackWidget(),
  flipDuration: const Duration(milliseconds: 600),
  flipAxis: FlipAxis.y,          // y = horizontal flip, x = vertical tumble
  curve: Curves.easeInOut,
  perspective: 0.001,
  onFlipComplete: (isFront) => print('Showing front: $isFront'),
)

// Trigger from anywhere:
controller.flip();
controller.showFront();
controller.showBack();
Parameter Type Default
front Widget required
back Widget required
flipDuration Duration 500ms
flipAxis FlipAxis FlipAxis.y
curve Curve Curves.easeInOut
initiallyFlipped bool false
perspective double 0.001

TiltCard

A card that tilts in response to pointer hover or device gyroscope.

TiltCard(
  maxTiltDegrees: 15.0,
  useGyroscope: false,    // set true for gyro-driven tilt on mobile
  child: MyCardContent(),
)

DepthCard

A card rendered as multiple parallel layers at increasing Z offsets, creating a true parallax depth illusion.

DepthCard(
  layers: [
    DepthLayer(depth: 0,  child: BackgroundWidget()),
    DepthLayer(depth: 10, child: MiddleWidget()),
    DepthLayer(depth: 20, child: ForegroundWidget()),
  ],
)

ParallaxCard

A card where the background moves slower than the foreground when tilted — classic parallax illusion.

ParallaxCard(
  background: NetworkImage('https://…/bg.jpg'),
  child: const CardContent(),
  parallaxFactor: 0.3,
)

Lists

CircularList3D

Items arranged on a ring in 3D space. Nearer items appear larger (perspective scaling) and more opaque.

CircularList3D(
  itemCount: 8,
  radius: 200,
  tiltAngle: 0.4,          // ring tilt in radians (0 = flat, π/2 = vertical)
  autoRotate: true,
  rotationSpeed: 0.25,     // full rotations per second
  rotationDirection: RotationDirection.clockwise,
  snapOnRelease: true,
  itemBuilder: (ctx, i) => ItemCard(items[i]),
)

CylinderList

A scrollable cylinder (like a slot-machine reel). Items wrap around the drum and rotate into view.

CylinderList(
  itemCount: 20,
  itemBuilder: (ctx, i) => ListTile(title: Text(items[i])),
  cylinderHeight: 300,
  itemHeight: 48,
)

CoverFlow3D

The classic iTunes-style coverflow: the selected card faces forward while neighbours fan out to the sides.

CoverFlow3D(
  itemCount: albums.length,
  itemBuilder: (ctx, i) => AlbumArt(albums[i]),
  itemWidth: 200,
  itemHeight: 200,
  onPageChanged: (index) => setState(() => _current = index),
)

SphereMenu

Menu items distributed over the surface of a sphere using Fibonacci sphere packing. Rotate the sphere by dragging.

SphereMenu(
  items: menuItems,
  radius: 160,
  itemBuilder: (ctx, item) => MenuBubble(item),
  onItemTap: (item) => navigate(item),
)

Containers

CubeContainer

An interactive 3D cube — each of the six faces holds an independent widget. Drag to rotate freely, or programmatically navigate to a face.

final cubeController = CubeController();

CubeContainer(
  size: 280,
  controller: cubeController,
  front:  const HomeScreen(),
  back:   const SettingsScreen(),
  left:   const ProfileScreen(),
  right:  const NotificationsScreen(),
  top:    const SearchScreen(),
  bottom: const HelpScreen(),
  perspective: 0.001,
  onFaceVisible: (face) => print('Showing: $face'),
)

// Navigate programmatically:
cubeController.showFace(CubeFace.back);

Panel3D

A flat panel with a realistic drop shadow and subtle 3D angle.

Panel3D(
  elevation: 12,
  tiltX: 0.05,    // radians
  tiltY: -0.03,
  material: Material3D.frostedGlass(),
  child: const PanelContent(),
)

Stack3D

A Stack where each child is offset along the Z-axis by a configurable depth value, creating a genuine layered parallax.

Stack3D(
  children: [
    Stack3DItem(depth: 0,  child: BackgroundLayer()),
    Stack3DItem(depth: 15, child: ContentLayer()),
    Stack3DItem(depth: 30, child: OverlayLayer()),
  ],
)

Text

ExtrudedText

Text with a real extrusion depth — each letter appears embossed out of the surface.

ExtrudedText(
  'LAUNCH',
  style: const TextStyle(fontSize: 48, fontWeight: FontWeight.w900),
  depth: 6,
  faceColor: Colors.white,
  sideColor: Colors.grey.shade400,
)

FloatingLabel

A label that floats in 3D space with a subtle levitation animation.

FloatingLabel(
  label: 'New',
  color: Colors.amber,
  floatHeight: 8,
  child: const ProductCard(),
)

Scene

ThreeDSceneWidget

The root widget for complex multi-object 3D scenes.

ThreeDSceneWidget(
  camera: CameraController(
    position: const Vec3(0, -100, 500),
    target:   Vec3.zero,
    fov: 60,
  ),
  lights: [
    LightSource.directional(
      direction: const Vec3(-1, -2, -1),
      color: Colors.white,
      intensity: 0.9,
    ),
    LightSource.ambient(intensity: 0.3),
  ],
  children: [
    /* ThreeDObject widgets */
  ],
)

Core Math Types

The library exposes its internal math primitives in the public API so you can build custom widgets on the same foundation.

Vec3

An immutable, value-type 3D vector.

const a = Vec3(1, 2, 3);
const b = Vec3(4, 5, 6);

final sum      = a + b;               // Vec3(5, 7, 9)
final cross    = a.cross(b);          // perpendicular vector
final dot      = a.dot(b);            // 32.0
final midpoint = a.lerp(b, 0.5);      // Vec3(2.5, 3.5, 4.5)
final unit     = a.normalized;        // unit vector in direction of a

Named constants: Vec3.zero, Vec3.one, Vec3.right, Vec3.up, Vec3.forward, Vec3.left, Vec3.down, Vec3.back


Quat

An immutable unit quaternion for 3D rotations — avoids Gimbal Lock.

// 90° around the Y-axis
final rot = Quat.axisAngle(Vec3.up, math.pi / 2);

// Smooth rotation interpolation (shortest arc, constant speed)
final mid = Quat.slerp(Quat.identity, rot, 0.5);

// Compose two rotations
final combined = rotA * rotB;

// Rotate a vector
final rotated = rot.rotate(Vec3.right);  // → Vec3.forward (approx)

Transform3D

A complete 3D transform: position + rotation + scale.

final t = Transform3D(
  position: const Vec3(50, 0, 0),
  rotation: Quat.axisAngle(Vec3.up, math.pi / 4),
  scale:    Vec3.one,
);

// Use directly in a Flutter Transform widget
Transform(
  alignment: Alignment.center,
  transform: t.toMatrix4Perspective(),
  child: MyWidget(),
);

// Interpolate between transforms
final mid = a.lerp(b, 0.5);   // uses SLERP for rotation

Animation System

Transform3DTween

Plugs into Flutter's standard animation framework:

late final AnimationController _ctrl = AnimationController(
  vsync: this,
  duration: const Duration(seconds: 1),
);

late final Animation<Transform3D> _anim = Transform3DTween(
  begin: Transform3D.identity,
  end:   Transform3D(
    position: const Vec3(0, -100, 0),
    rotation: Quat.axisAngle(Vec3.up, math.pi),
    scale:    Vec3.one,
  ),
).animate(CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut));

Spring3D

Physics-based spring animation — no keyframes, just energy:

final spring = Spring3D.bouncy;   // or Spring3D(stiffness: 300, damping: 22)

final anim = spring.animate(
  controller: _ctrl,
  from: currentTransform,
  to:   targetTransform,
);

Or use the convenience widget that auto-springs whenever targetTransform changes:

SpringTransform3D(
  targetTransform: _dragging ? dragTransform : Transform3D.identity,
  spring: Spring3D.gentle,
  child: MyCard(),
)
Preset Stiffness Damping Feel
Spring3D.snappy 500 30 Fast, tight
Spring3D.bouncy 200 10 Fun, overshoots
Spring3D.gentle 100 18 Smooth, subtle
Spring3D.stiff 800 60 Near-instant

RotationAnimation

Interpolates between two Quat values using SLERP (always shortest arc):

final anim = RotationAnimation(
  controller: _ctrl,
  from: Quat.identity,
  to:   Quat.axisAngle(Vec3.up, math.pi),
  curve: Curves.easeInOut,
);

FloatAnimation

Sinusoidal up-and-down floating effect:

FloatAnimation(
  controller: _ctrl,
  amplitude: 12.0,    // pixels
  frequency: 1.5,     // cycles per second
)

Theming

Wrap your app or subtree with ThreeDTheme to configure global 3D defaults:

ThreeDTheme(
  data: ThreeDThemeData(
    defaultDepth:    7.0,
    defaultMaterial: Material3D.glossyPlastic(Colors.indigo),
    shadowStyle:     DepthShadowStyle.soft,
    enableLighting:  true,
    lightPosition:   const Vec3(-200, -300, 500),
    perspective:     0.001,
  ),
  child: MyApp(),
)

Built-in theme presets

ThreeDThemeData.defaults()    // clean white matte
ThreeDThemeData.cyberpunk()   // dark, neon, high contrast
ThreeDThemeData.neumorphic()  // soft embossed light grey

Material3D Surface Properties

Every surface widget accepts a Material3D that drives highlight, shadow, and edge colours:

Material3D.matte(Colors.white)            // flat, no shine
Material3D.glossyPlastic(Colors.blue)     // strong highlight
Material3D.metallic(Colors.grey)          // mirror-like
Material3D.frostedGlass(tint: Colors.white)  // translucent blur
Material3D.rubber(Colors.black)           // very rough, no shine

Gesture System

Drag3DRecognizer

Converts 2D drag deltas into 3D rotation quaternions:

Drag3DRecognizer(
  sensitivity: 0.01,
  onRotate: (Quat delta) => setState(() => _rotation = _rotation * delta),
  child: My3DObject(),
)

PinchDepthRecognizer

Maps a two-finger pinch gesture to a Z-axis scale change:

PinchDepthRecognizer(
  onDepthChange: (double scale) => setState(() => _zoom = scale),
  child: SceneWidget(),
)

GyroscopeTilt

Drives a widget's tilt from the device gyroscope:

GyroscopeTilt(
  sensitivity: 0.5,
  builder: (ctx, tiltX, tiltY) => Transform(
    transform: Matrix4.identity()
      ..setEntry(3, 2, 0.001)
      ..rotateX(tiltX)
      ..rotateY(tiltY),
    alignment: Alignment.center,
    child: MyCard(),
  ),
)

Platform Support

Platform Status Notes
Android ✅ Full Impeller enabled by default (Flutter 3.24+)
iOS ✅ Full Impeller enabled by default
Web (WebGL 2) ✅ Full Chrome, Edge, Firefox
macOS 🔶 Experimental Requires --enable-impeller
Windows 🔶 Experimental Requires --enable-impeller
Linux 🔶 Experimental Requires --enable-impeller

How the Math Works

Every 3D effect in the library ultimately comes down to one key equation — the perspective projection that Flutter's Matrix4 encodes:

x_screen = (f · x_3d) / (z_3d + d)
y_screen = (f · y_3d) / (z_3d + d)

In code this is achieved by:

Matrix4.identity()..setEntry(3, 2, 0.001)
//                              ↑ perspective coefficient = 1/d ≈ 1/1000

Rotation uses unit quaternions instead of Euler angles to avoid Gimbal Lock. The SLERP formula:

slerp(q₁, q₂, t) = q₁·sin((1−t)θ)/sin(θ) + q₂·sin(tθ)/sin(θ)

guarantees constant angular speed along the great arc on the unit 4-sphere.

Depth-based perspective scaling in CircularList3D:

scale(z) = s_min + (s_max − s_min) · (z/r + 1) / 2

where r is the ring radius, s_min = 0.55, s_max = 1.0.


Running the Example

cd example
flutter run

The example app contains five demo screens:

Screen Widgets shown
DemoButtons ThreeDButton, FloatingAction3D, IconButton3D
DemoCards FlipCard3D, TiltCard, DepthCard, ParallaxCard
DemoCircularList CircularList3D, CylinderList, CoverFlow3D
DemoCubeScene CubeContainer, Panel3D, Stack3D
DemoSphereMenu SphereMenu, ThreeDSceneWidget

Running Tests

flutter test

The test suite covers:

  • Vec3 arithmetic, geometry, equality
  • Quat construction, rotation, SLERP, multiplication, decomposition
  • Transform3D identity, lerp, copyWith, Transform3DTween
  • FlipCard3D rendering, controller API, initiallyFlipped
  • ThreeDButton rendering, interaction, theming
  • CircularList3D item count, drag, auto-rotate, snap
  • RotationAnimation value at t=0/0.5/1, curve application
  • Spring3D presets, animate(), SpringTransform3D widget

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Write tests for new behaviour
  4. Run flutter test — all tests must pass
  5. Run flutter analyze — zero warnings
  6. Open a pull request with a clear description

Please follow the Dart style guide and the existing code conventions.


Roadmap

Version Planned features
0.2.0 SphereMenu full Fibonacci packing, ExtrudedText with RTL/Arabic support
0.3.0 Deep flutter_scene integration — glTF avatars inside widgets
0.4.0 Shader-based glass, neon glow, hologram effects
1.0.0 Stable API, all platforms fully supported, theme packs (Glassmorphism, Cyberpunk, Minimal 3D)

Changelog

See CHANGELOG.md for the full version history.


License

MIT © 2026 — Free to use in commercial and open-source projects.

See LICENSE for the full text.

Libraries

three_d_ui_builder
three_d_ui_builder