// photo in shape

Photo in shape

Any image (a photo, a logo, a map) can be wrapped onto a 3D shape. You give flutter_fiber the image bytes, it decodes them into a Fiber3DTexture, and you assign that texture to the map of a Fiber3DStandardMaterial. The shape's surface then shows the image, lit like any other surface.

There is no image-loading dependency. The package takes raw bytes from wherever you get them (app assets, a file, the network) and decodes them with Flutter's own image codec.

// demo

Demo

A world image wrapped around a Fiber3DSphere.

A logo on a Fiber3DBox. The same image appears on every face.

// how to use it

How to use it

Replace your_image.png with your own file. The three steps are the same for every shape.

1. Add the image to your app and declare it under flutter: assets: in your pubspec.yaml:

yaml
flutter:
  assets:
    - assets/images/your_image.png

2. Load and decode it. Decoding is asynchronous, so wait for ensureDecoded() before using the texture. In a State class:

dart
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_fiber/flutter_fiber.dart';

Fiber3DTexture? _texture;

@override
void initState() {
  super.initState();
  _loadTexture();
}

Future<void> _loadTexture() async {
  final data = await rootBundle.load('assets/images/your_image.png');
  final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);

  final texture = Fiber3DTexture(bytes);
  await texture.ensureDecoded(); // texture.isReady is now true

  if (!mounted) return;
  setState(() => _texture = texture);
}

3. Use it as the material's map on any shape. Here it is on a sphere. The surrounding canvas, camera and lights are set up as in Install & Quick Start:

dart
// In build(): wait until the texture is ready.
final texture = _texture;
if (texture == null) {
  return const Center(child: CircularProgressIndicator());
}

// Then, inside your Fiber3DCanvas children:
Fiber3DMesh(
  geometry: Fiber3DSphere(radius: 1.8, widthSegments: 64, heightSegments: 48),
  material: Fiber3DStandardMaterial(
    color: 0xffffff, // white = show the image untinted
    roughness: 0.85,
    map: texture,
  ),
  showEdges: false,
  onFrame: (elapsed, delta, transform) {
    transform.rotation.y = elapsed.inMicroseconds / 1e6 * 0.25; // slow spin
  },
)

// choosing a shape

Choosing a shape

Sphere (a globe or ball). The sphere wraps the image around itself, so use a wide image with a 2:1 ratio, like a flat world map (an equirectangular image). Raise widthSegments and heightSegments for a smoother surface.

Box (a logo, a label). Each of the six faces shows the whole image. Swap the geometry in step 3:

dart
Fiber3DMesh(
  geometry: Fiber3DBox(width: 1.0, height: 1.0, depth: 1.0),
  material: Fiber3DStandardMaterial(
    color: 0xffffff,
    roughness: 0.6,
    map: texture,
  ),
  showEdges: false,
)

A flat picture. Use a Fiber3DPlane with the same width-to-height ratio as the image, so it isn't stretched.

A different image on each face. One mesh holds one material, and so one image. To show a different image on each face, build the shape from separate planes, one per face, each with its own texture. The Text in shape page shows this with a six-sided die. It works the same way for images.

// tips & limits

Tips & limits

  • Use color: 0xffffff to show the image untinted. The material color multiplies the image, so any other color tints it.
  • With color management on (the default), the image is treated as sRGB, so its colors match the original file.
  • The image is lit like any surface, so a dark image stays dark. If it looks too dim, raise the ambient light intensity or use a brighter image.
  • Only Fiber3DStandardMaterial accepts textures. Images use the geometry's first UV set, linear filtering and clamp-to-edge wrapping, with no mipmaps. An image shown much smaller than its pixel size can look noisy, so resize large sources to roughly the size they will appear.
  • The decoded image is uploaded to the GPU once and is released when the canvas is disposed.