Skip to content

Quick Start

If all you need is a quick and easy route to rendering a single 3D model in your Flutter application, start with ViewerWidget.

This is a simplified, Flutter-only wrapper around the underlying 3D rendering API with sane defaults for most people.

ViewerWidget handles all the setup and configuration of the underlying Thermion API, including:

  • Loading 3D models (glTF assets)
  • Configuring skyboxes and image-based lighting
  • Setting up camera positions and manipulators
  • Managing the rendering lifecycle

Follow the steps listed in Getting Started to configure your Flutter installation and project.

If you’re running Windows, delete the examples/flutter/quickstart/assets symlink and copy the assets folder from examples/assets to examples/flutter/quickstart/assets.

import 'package:flutter/material.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
import 'path_to_your_viewer_widget.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ViewerWidget(
assetPath: 'assets/my_model.glb',
initialCameraPosition: Vector3(0, 0, 5),
manipulatorType: ManipulatorType.ORBIT,
),
),
);
}
}
Property Type Default Description
initial Widget Red decorated box Widget to display while the viewer is loading
initialCameraPosition Vector3 Vector3(0, 0, 5) The starting position for the camera (looking towards origin)
showFpsCounter bool false Whether to show an FPS counter overlay
assetPath String? null Path to the glTF asset to load
skyboxPath String? null Path to a KTX skybox image
iblPath String? null Path to a KTX image for image-based lighting
directLightType LightType? null Type of direct light to add to the scene
transformToUnitCube bool true If true, rescales the model to fit within a 1x1x1 cube
postProcessing bool true Enables ACES tone mapping and basic anti-aliasing
background Color? null Background color (not visible when skybox is provided)
destroyEngineOnUnload bool false If true, disposes the engine when widget is disposed
manipulatorType ManipulatorType ORBIT Type of camera control to use
onViewerAvailable Future Function(ThermionViewer)? null Callback when viewer is ready

ViewerWidget supports three different camera manipulation modes:

  • ManipulatorType.NONE: No camera controls, static view
  • ManipulatorType.ORBIT: Orbit controls (pinch to zoom, swipe to rotate)
  • ManipulatorType.FREE_FLIGHT: Free flight controls for unrestricted movement

Example:

ViewerWidget(
assetPath: 'assets/model.glb',
manipulatorType: ManipulatorType.FREE_FLIGHT,
)

You can set up lighting in multiple ways:

ViewerWidget(
assetPath: 'assets/model.glb',
iblPath: 'assets/environment.ktx',
)
ViewerWidget(
assetPath: 'assets/model.glb',
directLightType: LightType.SUN,
)

You can get access to the underlying ThermionViewer object for more advanced control:

ViewerWidget(
assetPath: 'assets/model.glb',
onViewerAvailable: (viewer) async {
// Now you can use the viewer directly
final camera = await viewer.getActiveCamera();
await camera.lookAt(Vector3(0, 1, 5));
// Add custom lights, manipulate materials, etc.
},
)

The manipulatorType is the only property that can be changed after the widget is created:

class _MyWidgetState extends State<MyWidget> {
ManipulatorType _manipulatorType = ManipulatorType.ORBIT;
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ViewerWidget(
assetPath: 'assets/model.glb',
manipulatorType: _manipulatorType,
),
),
Row(
children: [
ElevatedButton(
onPressed: () {
setState(() {
_manipulatorType = ManipulatorType.ORBIT;
});
},
child: Text('Orbit'),
),
ElevatedButton(
onPressed: () {
setState(() {
_manipulatorType = ManipulatorType.FREE_FLIGHT;
});
},
child: Text('Free Flight'),
),
],
),
],
);
}
}
  • Only the manipulatorType property can be changed at runtime. For any other property changes, create a new widget.
  • The widget requires that you have the correct environment setup for Thermion (Flutter master channel with native assets enabled).

Here’s a complete example showing how to use ViewerWidget with multiple configuration options:

import 'package:flutter/material.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
import 'package:vector_math/vector_math_64.dart';
class ModelViewer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('3D Model Viewer')),
body: ViewerWidget(
assetPath: 'assets/robot.glb',
skyboxPath: 'assets/studio_skybox.ktx',
iblPath: 'assets/studio_ibl.ktx',
initialCameraPosition: Vector3(0, 1.5, 3),
manipulatorType: ManipulatorType.ORBIT,
showFpsCounter: true,
background: Colors.grey,
postProcessing: true,
transformToUnitCube: true,
onViewerAvailable: (viewer) async {
// You can perform additional setup here
print('Viewer is ready!');
},
),
);
}
}