model_viewer_pro 2.0.1
model_viewer_pro: ^2.0.1 copied to clipboard
Interactive 3D models in Flutter. Full runtime control over mesh visibility, colors, cameras, animations, and lighting. Built on model_viewer_plus.
model_viewer_pro #
Display and control interactive 3D models (GLB / GLTF) in Flutter with fine-grained mesh manipulation, camera control, animations, grounded environments, and custom lighting.
Built on top of model_viewer_plus,
this package adds a full Dart API for runtime scene control that the base
package does not provide.
The Customization Workflow (How it Works) #
This package provides the simplest and most efficient way to create customizable 3D models (like character avatars with interchangeable outfits) directly in Flutter.
To achieve seamless, instant customization, follow this workflow:
- Use a Single GLB File with Overlapping Meshes: Instead of loading multiple separate files, ask your 3D artist to export a single
.glbfile that contains all possible variations (e.g., all shirts, pants, and hats) layered on top of each other. - Initialize the Default State: When the model first loads (using the
onLoadcallback), immediately hide the overlapping meshes and show only the necessary default meshes. - Toggle Meshes at Runtime: As the user interacts with your app's UI, use
controller.setVisibility('MeshName', true/false)to instantly swap parts. Because all meshes are already loaded into memory, the transition is completely seamless with zero loading delay!
Features #
| 🎨 Mesh control | Discover, show/hide, and recolour meshes by name |
| 📷 Camera | Orbit, target, FOV, zoom in/out, auto-rotate, reset |
| 🌄 Environment & Skybox | Set HDR/equirectangular images from URL or Flutter assets |
| 🌍 Grounded mode | Project the skybox onto a ground plane for realistic scenes |
| 💡 Lighting | Shadow intensity/softness, exposure, background colour |
| 🎬 Animations | Play, pause, seek, get duration |
| 📸 Screenshot | Capture the current frame as a PNG data URI |
| ⚡ Performance | Batched JS calls, single render-trigger, minimal WebView round-trips |
Installation #
# pubspec.yaml
dependencies:
model_viewer_pro: ^2.0.0
Then run:
flutter pub get
Quick Start #
import 'package:model_viewer_pro/model_viewer_pro.dart';
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final _controller = ModelViewerProController();
@override
Widget build(BuildContext context) {
return ModelViewerProViewer(
src: 'assets/model.glb',
controller: _controller,
cameraControls: true,
autoRotate: true,
onLoad: (meshes) {
print('Loaded meshes: $meshes');
},
);
}
}
Don't forget to declare your model asset in pubspec.yaml:
flutter:
assets:
- assets/model.glb
ModelViewerProViewer — Widget Reference #
Required #
| Parameter | Type | Description |
|---|---|---|
src |
String |
Path or URL to the model file (.glb or .gltf). |
Controller & Callbacks #
| Parameter | Type | Default | Description |
|---|---|---|---|
controller |
ModelViewerProController? |
null |
Controller for runtime mesh, camera, and lighting operations. |
onLoad |
Function(List<String>)? |
null |
Called once the model loads with discovered mesh names. |
initialLoadingMeshes |
List<String>? |
null |
Whitelist of meshes to show instantly upon load, hiding all others to prevent visual pop-in. |
Appearance #
| Parameter | Type | Default | Description |
|---|---|---|---|
backgroundColor |
Color |
Colors.transparent |
Background colour of the viewer. |
shadowIntensity |
double? |
null (1.0 when grounded) |
Shadow darkness. 0.0 = none. |
shadowSoftness |
double? |
null |
Shadow blur. 0.0 = sharp, 1.0 = soft. |
exposure |
double? |
null |
Scene brightness. 1.0 = default. |
Environment & Skybox #
| Parameter | Type | Default | Description |
|---|---|---|---|
environmentImageUrl |
String? |
null |
URL to HDR/equirectangular image for reflections. |
skyboxImagePath |
String? |
null |
URL or asset path to equirectangular image for 360° background. |
grounded |
bool? |
null |
Projects skybox onto a ground plane. See Grounded Mode. |
skyboxHeight |
String? |
null ("1.5m" when grounded) |
Height at which the skybox terminates, e.g. "1.5m". |
Camera #
| Parameter | Type | Default | Description |
|---|---|---|---|
cameraOrbit |
String? |
null |
Initial camera position, e.g. "45deg 55deg 2m". |
cameraTarget |
String? |
null |
Look-at point, e.g. "0m 1m 0m". |
fieldOfView |
String? |
null |
FOV, e.g. "30deg". |
minFieldOfView |
String? |
null |
Min FOV clamp. |
maxFieldOfView |
String? |
null |
Max FOV clamp. |
maxCameraOrbit |
String? |
null ("auto 90deg auto" when grounded) |
Max orbit constraint. |
minCameraOrbit |
String? |
null |
Min orbit constraint. |
Interaction #
| Parameter | Type | Default | Description |
|---|---|---|---|
cameraControls |
bool |
true |
Enable camera orbit via drag. |
autoRotate |
bool |
false |
Continuous auto-rotation. |
disablePan |
bool? |
null (true when grounded) |
Disable two-finger pan / middle-mouse. |
disableZoom |
bool? |
null |
Disable pinch-zoom / scroll wheel. |
disableTap |
bool? |
null |
Disable tapping the model. |
touchAction |
String? |
null |
CSS touch-action, e.g. "pan-y". |
Model Loading #
| Parameter | Type | Default | Description |
|---|---|---|---|
ar |
bool |
false |
Show the AR button. |
autoPlay |
bool |
false |
Auto-play embedded animations. |
alt |
String? |
null |
Accessibility alt text. |
poster |
String? |
null |
Poster image URL shown while loading. |
loading |
Loading? |
null |
Loading behavior: auto, lazy, eager. |
reveal |
Reveal? |
null |
Reveal behavior: auto, interaction, manual. |
Grounded Mode #
When grounded: true is set, the skybox is projected onto a virtual ground
plane, creating a realistic floor reflection. A skybox or environment image
is required.
Automatic Defaults #
When grounded is enabled, these sensible defaults are applied unless you explicitly override them:
| Attribute | Auto value | Override with |
|---|---|---|
skybox-projection |
equirectangular |
(always applied) |
skybox-height |
"1.5m" |
skyboxHeight param |
shadow-intensity |
1.0 |
shadowIntensity param |
max-camera-orbit |
"auto 90deg auto" |
maxCameraOrbit param |
disable-pan |
true |
disablePan param |
If no skyboxImagePath is provided but environmentImageUrl is set, the
environment image is automatically used as the skybox.
Example #
ModelViewerProViewer(
src: 'assets/model.glb',
controller: controller,
grounded: true,
skyboxImagePath: 'assets/park.jpg',
// All defaults applied automatically — override any you need:
// skyboxHeight: '2m',
// maxCameraOrbit: 'auto 120deg auto',
// disablePan: false,
)
ModelViewerProController — Runtime API #
Create one instance, pass it to ModelViewerProViewer. All methods become active
after the onLoad callback fires.
Scene #
| Method | Returns | Description |
|---|---|---|
waitForSceneReady({int timeoutMs}) |
Future<bool> |
Wait for the 3D scene graph to be accessible. |
Mesh #
| Method | Returns | Description |
|---|---|---|
getAvailableMeshes() |
Future<List<String>> |
All mesh names in the loaded model. |
setVisibility(name, visible) |
Future<void> |
Show/hide a mesh and all its children. |
setExclusiveMesh(group, selectedMesh) |
Future<void> |
Show selectedMesh and hide all other meshes in the group list. |
setTextureColor(name, colorHex) |
Future<void> |
Change a mesh's base colour (e.g. "#FF0000"). |
Camera #
| Method | Returns | Description |
|---|---|---|
setCameraOrbit(theta, phi, radius) |
Future<void> |
Move camera (degrees/metres). |
setCameraTarget(x, y, z) |
Future<void> |
Set look-at point (metres). |
setFieldOfView(fov) |
Future<void> |
Set FOV (degrees). |
getCameraOrbit() |
Future<Map?> |
Get current orbit values. |
resetCamera() |
Future<void> |
Reset to default position. |
zoomIn() / zoomOut() |
Future<void> |
Zoom by ±20%. |
setAutoRotate(enabled) |
Future<void> |
Toggle auto-rotation. |
Environment & Skybox #
| Method | Returns | Description |
|---|---|---|
setEnvironmentImageFromUrl(url) |
Future<bool> |
Set reflection image from URL. |
setEnvironmentImageFromAsset(path) |
Future<bool> |
Set reflection image from Flutter asset. |
setSkyboxImageFromUrl(url) |
Future<bool> |
Set 360° background from URL. |
setSkyboxImageFromAsset(path) |
Future<bool> |
Set 360° background from Flutter asset. |
setSkyboxHeight(height) |
Future<bool> |
Set skybox termination height. |
removeSkyboxHeight() |
Future<bool> |
Remove skybox height constraint. |
Lighting #
| Method | Returns | Description |
|---|---|---|
setBackgroundColor(color) |
Future<bool> |
Set CSS background (e.g. "#000"). |
setGroundVisibility(visible) |
Future<bool> |
Show/hide ground shadow. |
setShadowIntensity(value) |
Future<bool> |
Shadow darkness (0–1). |
setShadowSoftness(value) |
Future<bool> |
Shadow blur (0–1). |
setExposure(value) |
Future<bool> |
Scene brightness multiplier. |
Interaction Controls #
| Method | Returns | Description |
|---|---|---|
setDisablePan(disabled) |
Future<bool> |
Enable/disable panning. |
setDisableZoom(disabled) |
Future<bool> |
Enable/disable zooming. |
setDisableTap(disabled) |
Future<bool> |
Enable/disable tap. |
setMaxCameraOrbit(orbit) |
Future<bool> |
Set max orbit constraint. |
setMinCameraOrbit(orbit) |
Future<bool> |
Set min orbit constraint. |
setTouchAction(action) |
Future<bool> |
Set CSS touch-action. |
Animation #
| Method | Returns | Description |
|---|---|---|
playAnimation() |
Future<void> |
Start playback. |
pauseAnimation() |
Future<void> |
Pause at current position. |
setAnimationTime(seconds) |
Future<void> |
Seek to a specific time. |
getAnimationDuration() |
Future<double?> |
Total animation duration. |
Asset Loading #
| Method | Returns | Description |
|---|---|---|
loadSkyboxFromAsset(path) |
Future<bool> |
Load skybox from Flutter asset (base64). |
loadEnvironmentFromAsset(path) |
Future<bool> |
Load environment from Flutter asset. |
Utilities #
| Method | Returns | Description |
|---|---|---|
takeScreenshot() |
Future<String?> |
Capture frame as PNG data URL. |
executeCustomJS(script) |
Future<void> |
Run arbitrary JavaScript. |
Performance Tips #
- Use URL images when possible —
skyboxImagePathandenvironmentImageUrlpointing to remote URLs are loaded natively by the WebView and are more efficient than base64-encoded assets. - Avoid unnecessary
setState— The viewer batches alldidUpdateWidgetchanges into a single JS call to keep round-trips minimal. - Wait for
onLoad— Perform all controller operations inside or after theonLoadcallback to ensure the scene is ready. - Keep models small — Compress
.glbfiles and use Draco compression. - Use
loading: Loading.lazy— Defer loading until the viewer is on screen.
Supported Platforms #
| Platform | Status |
|---|---|
| Android | ✅ Supported (6.0+) |
| iOS | ✅ Supported (11.0+) |
| Web | ✅ Supported (via <model-viewer>) |
Architecture & Limitations (Problem Solving) #
This package is optimized for zero-latency outfit swapping using a single-file architecture (sub-mesh toggling). This is highly desirable for avatars, mini-games, and simple 3D models where you want instant changes without network requests.
However, be aware of the following limitations:
- Memory Usage: The entire wardrobe (all meshes and textures, even hidden ones) is loaded into memory at once. If you have dozens of high-resolution clothing items, you may encounter Out-Of-Memory (OOM) crashes on low-end mobile devices.
- Texture Atlasing: To mitigate memory issues, instruct your 3D artists to use Texture Atlasing (combining multiple clothing textures into a single shared image map).
- Skeleton Sharing: All clothing items must share the exact same skeletal rig. Clothing that requires entirely different bone structures (e.g., a flowing cape vs. a tight shirt) might cause animation conflicts or inflate the rig complexity.
If your application requires hundreds of distinct, high-resolution clothing pieces, consider a modular loading approach (loading separate GLB files dynamically) instead of packing everything into one file.
Troubleshooting #
- Black screen or infinite loading: Ensure your
assets/model.glbis declared inpubspec.yamland spelled exactly the same (case-sensitive). WebViewPlatform.instance != nullerrors in tests: Becausemodel_viewer_prorelies on native WebViews, headless widget tests (flutter test) will fail. You must use theintegration_testpackage and run tests on a real device or emulator.- Symlink errors on Windows: If you encounter plugin symlink errors when running the example app, enable Developer Mode in your Windows system settings.
Launching & Publishing your App #
When preparing your Flutter app for release with this package:
- Model Optimization: Compress your
.glbfiles before launch. Use tools likegltf-pipelineto apply Draco compression. Themodel-viewerweb component natively supports Draco decompression. - Web Deployment: If deploying to Flutter Web, no additional configuration is needed. The package uses an iframe
<model-viewer>component which handles WebGL natively. Ensure your web server serves.glbfiles with the correct CORS headers if loading from external URLs. - Android Permissions: Native WebView components require internet access if you are loading remote URLs (for the model or skyboxes). Ensure
<uses-permission android:name="android.permission.INTERNET" />is in yourAndroidManifest.xml. - App Size: Be mindful that large 3D assets bundled locally will drastically increase your
.apk/.ipasize. For production apps, host yourglbandhdrfiles on a CDN and load them via URL.
Migrating from model_viewer_pro_controller #
Replace the import:
// Before
import 'package:model_viewer_pro_controller/model_viewer_pro_controller.dart';
// After
import 'package:model_viewer_pro/model_viewer_pro.dart';
Update your pubspec.yaml:
dependencies:
# Remove: model_viewer_pro_controller: ...
model_viewer_pro: ^1.0.0
All class names (ModelViewerProViewer, ModelViewerProController) and method
signatures remain identical — no other code changes required.
License #
See LICENSE for details.