// text in shape

Text in shape

Fiber3DTextTexture.render draws a string (letters or numbers) into a texture. Assign that texture to Fiber3DStandardMaterial.map and the text appears on the shape's surface.

One mesh shows one text. A mesh takes exactly one material, and a material holds one texture, so a single mesh can't show a different text on each face. A Fiber3DBox shows the same texture on all six faces. To put different text on each face, build the shape out of separate flat Fiber3DPlane meshes, one per face, each with its own texture. The dice example below does this with six planes.

// demo

Demo

A die made of six Fiber3DPlane faces, each showing a different number rendered with Fiber3DTextTexture.

// how to use it

How to use it

One text on one shape. Render the texture (it is asynchronous, so wait for it), then use it as the map:

dart
final texture = await Fiber3DTextTexture.render(
  text: 'Hello',
  fontSize: 64,
  color: const Color(0xFFFFFFFF),
  backgroundColor: const Color(0xFF3366FF),
);

Fiber3DMesh(
  geometry: Fiber3DPlane(width: 2, height: 1),
  material: Fiber3DStandardMaterial(color: 0xffffff, map: texture),
  showEdges: false,
)

Different text on each face. Each face is its own Fiber3DPlane (a flat square that faces +Z by default). The code renders six textures, then places six planes half a cube-width out from the center, each rotated to face outward. Wrapping them in a Fiber3DGroup lets the whole die spin as one object. A complete scene:

dart
import 'dart:math';

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

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

class DiceShapeDemo extends StatefulWidget {
  const DiceShapeDemo({super.key});

  @override
  State<DiceShapeDemo> createState() => _DiceShapeDemoState();
}

/// One cube face: position (half-extent already applied), a rotation
/// about x and y, and the text to show on it.
class _FaceSpec {
  final double x, y, z;
  final double rotX, rotY;
  final String label;
  const _FaceSpec(this.x, this.y, this.z, this.rotX, this.rotY, this.label);
}

class _DiceShapeDemoState extends State<DiceShapeDemo> {
  static const _half = 0.9; // half of a 1.8-unit cube

  static final List<_FaceSpec> _faces = [
    _FaceSpec(0, 0, _half, 0, 0, '1'), // +Z (Fiber3DPlane's default facing)
    _FaceSpec(_half, 0, 0, 0, pi / 2, '2'), // +X
    _FaceSpec(0, _half, 0, -pi / 2, 0, '3'), // +Y
    _FaceSpec(0, -_half, 0, pi / 2, 0, '4'), // -Y
    _FaceSpec(-_half, 0, 0, 0, -pi / 2, '5'), // -X
    _FaceSpec(0, 0, -_half, 0, pi, '6'), // -Z
  ];

  List<Fiber3DTexture>? _textures;

  @override
  void initState() {
    super.initState();
    // Render one texture per face, then rebuild once all are ready.
    Future.wait(
      _faces.map(
        (f) => Fiber3DTextTexture.render(
          text: f.label,
          fontSize: 140,
          color: const Color(0xFF1A1A1A),
          backgroundColor: const Color(0xFFF5F5F5),
        ),
      ),
    ).then((textures) {
      if (!mounted) return;
      setState(() => _textures = textures);
    });
  }

  @override
  Widget build(BuildContext context) {
    final textures = _textures;

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: const Text('flutter_fiber: text-in-shape (dice)')),
        body: Fiber3DCanvas(
          backgroundColor: 0x303130,
          camera: Fiber3DCamera(
            position: const Fiber3DVector3(0, 0, 5),
            target: const Fiber3DVector3.zero(),
          ),
          orbitEnabled: true,
          lights: const [
            Fiber3DAmbientLight(color: 0xffffff, intensity: 0.6),
            Fiber3DPointLight(
              color: 0xffffff,
              intensity: 3.0,
              position: Fiber3DVector3(3, 4, 5),
            ),
          ],
          children: textures == null
              ? const []
              : [
                  // The group spins all six faces together as one die.
                  Fiber3DGroup(
                    onFrame: (elapsed, delta, transform) {
                      final t = delta.inMicroseconds / 1e6;
                      transform.rotation.y += t;
                      transform.rotation.x += t * 0.4;
                    },
                    children: [
                      for (var i = 0; i < _faces.length; i++)
                        Fiber3DMesh(
                          geometry: Fiber3DPlane(width: 1.8, height: 1.8),
                          material: Fiber3DStandardMaterial(
                            map: textures[i],
                            roughness: 0.6,
                            metalness: 0.0,
                          ),
                          showEdges: false,
                          hitRadius: 1.0,
                          onFrame: (elapsed, delta, transform) {
                            final f = _faces[i];
                            transform.position.set(f.x, f.y, f.z);
                            transform.rotation.x = f.rotX;
                            transform.rotation.y = f.rotY;
                            transform.rotation.z = 0.0;
                          },
                        ),
                    ],
                  ),
                ],
        ),
      ),
    );
  }
}

// parameters

Parameters

All parameters of Fiber3DTextTexture.render:

text

default required

The string to draw.

fontSize

default 48

Logical font size.

color

default white

Text color (a dart:ui Color).

backgroundColor

default transparent

Fill behind the text.

fontFamily

default null

Optional font family.

fontWeight

default FontWeight.normal

Weight of the text.

textAlign

default TextAlign.center

Alignment of the text.

padding

default 16

Logical padding around the text.

pixelRatio

default 2.0

Rasterization scale. Higher = sharper.

// tips & limits

Tips & limits

  • Render the textures before showing the mesh. In the dice example the canvas has no children until all six textures are ready.
  • The texture's size follows the text plus padding. Size the surface you map it on with a matching aspect ratio (for example a Fiber3DPlane) to avoid stretching.
  • Only Fiber3DStandardMaterial accepts textures.
  • Use color: 0xffffff (the default) on the material to show the texture untinted.