depthlift 0.2.0
depthlift: ^0.2.0 copied to clipboard
Convert any 2D image into a live 3D parallax scene with on-device depth estimation. Inspired by iOS depth effects.
DepthLift #
Transform any 2D image into a live, interactive 3D parallax scene with on-device depth estimation. Inspired by iPhone's depth wallpaper effect.
✨ What it does #
Tilt your phone → the image comes alive in 3D
- iPhone-like gyro motion — damped, interpolated, zero jitter
- On-device depth estimation — Depth Anything v2 via TFLite / Core ML
- Real-time GPU rendering — OpenGL ES 3.0 (Android) / Metal (iOS) at 60 FPS
- 3-tier input fallback — Gyroscope → Touch drag → Idle float animation
- 4 motion presets — iOS Natural, Cinematic, Soft, Intense
- Frame export — capture PNG stills
🚀 Quick Start #
dependencies:
depthlift: ^0.2.0
import 'package:depthlift/depthlift.dart';
DepthLiftView(
image: const AssetImage('assets/photo.jpg'),
options: const DepthLiftOptions(
effect: DepthEffect.parallax,
useGyroscope: true,
),
);
That's it. The widget handles depth estimation, mesh construction, and rendering automatically.
🎬 Motion Presets #
// iPhone wallpaper feel — ultra-smooth, subtle, premium
DepthLiftOptions.fromPreset(DepthPreset.iosNatural)
// Cinematic parallax — moderate, dramatic
DepthLiftOptions.fromPreset(DepthPreset.cinematic)
// Gentle movement — good for portraits
DepthLiftOptions.fromPreset(DepthPreset.soft)
// Bold, aggressive parallax
DepthLiftOptions.fromPreset(DepthPreset.intense)
🎛️ Controller #
final ctrl = DepthLiftController();
DepthLiftView(
image: const AssetImage('assets/photo.jpg'),
controller: ctrl,
);
// Start the breathing animation
await ctrl.play();
// Switch effect on the fly
await ctrl.setEffect(DepthEffect.bokeh);
// Export a still frame
final Uint8List png = await ctrl.exportFrame();
// Listen to state changes
ctrl.stateStream.listen((state) {
// DepthLiftState.loading | .ready | .error
});
// Always dispose when done
ctrl.dispose();
🎯 iPhone-Style Gyro Engine #
The IOSStyleGyroController is the secret sauce. Unlike raw gyro mapping, it uses:
| Technique | Purpose |
|---|---|
| Ticker-based updates | Frame-synced at 60 FPS, not raw stream events |
lerpDouble smoothing |
Interpolates toward target each frame |
| Decay factor | Prevents drift, gently returns to centre |
| Low sensitivity | 0.01–0.02 range for subtle motion |
| Clamped range | Max ±0.25 offset — keeps motion premium |
The result: motion that feels floating, damped, and slightly delayed — just like iOS.
// Use it standalone if you want
final gyro = IOSStyleGyroController.iosNatural();
await gyro.init(vsync);
gyro.offset.addListener(() {
final o = gyro.offset.value;
// o.dx, o.dy = smoothed tilt values
});
gyro.dispose();
⚙️ Options Reference #
| Field | Type | Default | Description |
|---|---|---|---|
effect |
DepthEffect |
.parallax |
Active visual effect |
depthModel |
DepthModel |
.depthAnythingV2 |
Depth estimation backend |
depthScale |
double |
0.6 |
Z-axis displacement (0.0–1.0) |
parallaxFactor |
double |
0.4 |
Motion multiplier (0.0–1.0) |
meshResolution |
double |
64 |
Grid subdivisions (16–256) |
focusDepth |
double |
0.5 |
Bokeh focal plane (0.0–1.0) |
bokehIntensity |
double |
0.5 |
Blur strength (0.0–1.0) |
floatDuration |
Duration |
3s |
Float animation cycle |
useGyroscope |
bool |
true |
Enable gyroscope input |
lowPowerMode |
bool |
false |
Halves mesh, disables bokeh |
📱 Platform Setup #
Android #
Minimum SDK: 24 (Android 7.0)
android {
defaultConfig {
minSdkVersion 24
}
}
The plugin declares android.hardware.sensor.gyroscope with required="false" — works on all devices including emulators and tablets without gyroscope.
iOS #
Deployment target: 14.0
Add to Info.plist:
<key>NSMotionUsageDescription</key>
<string>Used for parallax depth effect</string>
In ios/Podfile:
platform :ios, '14.0'
🔄 Input Fallback System #
The plugin automatically detects available input and falls back gracefully:
1. Gyroscope (if hardware available + useGyroscope: true)
↓ not available
2. Touch/Mouse drag (GestureDetector)
↓ finger lifted
3. Float animation (Lissajous idle breathing)
No configuration needed — it just works on every device.
🏗️ Architecture #
┌──────────────────────────────────────────────────┐
│ Flutter (Dart) │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │DepthLiftView │──│ DepthLiftController │ │
│ └──────┬───────┘ └──────────────────────────┘ │
│ │ │
│ ┌──────▼───────────────────────────────────┐ │
│ │ DepthLiftInputManager │ │
│ │ ├─ IOSStyleGyroController (Ticker+Lerp) │ │
│ │ ├─ PointerController (touch drag) │ │
│ │ └─ FloatAnimationController (idle) │ │
│ └──────┬───────────────────────────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────────┐ │
│ │ MethodChannel (dev.depthlift/engine) │ │
│ └──────┬──────────────────────────────────┘ │
├─────────┼────────────────────────────────────────┤
│ Native │ │
│ ┌──────▼──────┐ ┌──────────────────────────┐ │
│ │ Plugin │──│ DepthRenderer │ │
│ │ (Kt/Swift) │ │ GLES 3.0 / Metal │ │
│ └─────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────┘
🧪 Testing #
flutter test
📄 License #
MIT License — see LICENSE for details.