Skip to content

Embedding Images

Using images in Fluvie compositions

Images are fundamental building blocks in video compositions. This guide covers how to embed, animate, and optimize images in Fluvie.

Table of Contents


Basic Usage

Standard Flutter image widgets work in Fluvie:

Scene(
  durationInFrames: 150,
  children: [
    VPositioned.fill(
      child: Image.asset(
        'assets/background.jpg',
        fit: BoxFit.cover,
      ),
    ),
  ],
)

Image Sources

Asset Images

Load images from your Flutter assets:

// Register in pubspec.yaml first
// flutter:
//   assets:
//     - assets/images/

Image.asset(
  'assets/images/photo.jpg',
  fit: BoxFit.cover,
)

Network Images

Load images from URLs (cached during render):

Image.network(
  'https://example.com/photo.jpg',
  fit: BoxFit.cover,
  loadingBuilder: (context, child, loadingProgress) {
    if (loadingProgress == null) return child;
    return Container(color: Colors.grey);
  },
)

File Images

Load from the local filesystem:

Image.file(
  File('/path/to/image.jpg'),
  fit: BoxFit.cover,
)

Memory Images

Load from bytes in memory:

Image.memory(
  imageBytes,
  fit: BoxFit.cover,
)

Sizing and Fitting

BoxFit Options

// Cover: Fill container, may crop
Image.asset('photo.jpg', fit: BoxFit.cover)

// Contain: Fit within container, may letterbox
Image.asset('photo.jpg', fit: BoxFit.contain)

// Fill: Stretch to fill (may distort)
Image.asset('photo.jpg', fit: BoxFit.fill)

// Fixed dimensions
SizedBox(
  width: 400,
  height: 300,
  child: Image.asset('photo.jpg', fit: BoxFit.cover),
)

Full-Screen Background

Scene(
  durationInFrames: 150,
  children: [
    VPositioned.fill(
      child: Image.asset(
        'assets/background.jpg',
        fit: BoxFit.cover,
        alignment: Alignment.center,  // Control crop position
      ),
    ),
    // Other content on top...
  ],
)

Centered with Constraints

VCenter(
  child: ConstrainedBox(
    constraints: BoxConstraints(maxWidth: 600, maxHeight: 400),
    child: Image.asset(
      'assets/photo.jpg',
      fit: BoxFit.contain,
    ),
  ),
)

Animating Images

Entry Animation

AnimatedProp(
  startFrame: 30,
  duration: 45,
  animation: PropAnimation.combine([
    PropAnimation.zoomIn(start: 0.8),
    PropAnimation.fadeIn(),
  ]),
  curve: Easing.easeOutBack,
  child: Image.asset('assets/photo.jpg'),
)

Sliding Image

AnimatedProp(
  startFrame: 0,
  duration: 60,
  animation: PropAnimation.slideUp(distance: 100),
  child: Image.asset('assets/photo.jpg'),
)

Rotating Image

AnimatedProp(
  startFrame: 0,
  duration: 90,
  animation: PropAnimation.rotate(
    start: -0.1,
    end: 0.1,
  ),
  child: Image.asset('assets/photo.jpg'),
)

Continuous Animation with TimeConsumer

TimeConsumer(
  builder: (context, frame, fps) {
    final rotation = frame * 0.01;  // Slow continuous rotation
    return Transform.rotate(
      angle: rotation,
      child: Image.asset('assets/logo.png'),
    );
  },
)

Ken Burns Effect

The Ken Burns effect creates cinematic movement on still images:

Using KenBurnsImage

KenBurnsImage(
  assetPath: 'assets/landscape.jpg',
  width: 800,
  height: 600,
  startScale: 1.0,
  endScale: 1.3,
  startAlignment: Alignment.centerLeft,
  endAlignment: Alignment.centerRight,
)

Zoom In

KenBurnsImage.zoomIn(
  assetPath: 'assets/portrait.jpg',
  width: 600,
  height: 800,
  zoomAmount: 0.2,
  focus: Alignment.center,
)

Pan Across

KenBurnsImage.pan(
  assetPath: 'assets/panorama.jpg',
  width: 1200,
  height: 400,
  from: Alignment.centerLeft,
  to: Alignment.centerRight,
  scale: 1.2,
)

Image Grids

Using VRow and VColumn

VColumn(
  spacing: 10,
  children: [
    VRow(
      spacing: 10,
      children: [
        Expanded(child: Image.asset('photo1.jpg', fit: BoxFit.cover)),
        Expanded(child: Image.asset('photo2.jpg', fit: BoxFit.cover)),
      ],
    ),
    VRow(
      spacing: 10,
      children: [
        Expanded(child: Image.asset('photo3.jpg', fit: BoxFit.cover)),
        Expanded(child: Image.asset('photo4.jpg', fit: BoxFit.cover)),
      ],
    ),
  ],
)

With Stagger Animation

VColumn(
  spacing: 10,
  stagger: StaggerConfig.scale(delay: 10, duration: 30),
  children: [
    Image.asset('photo1.jpg'),
    Image.asset('photo2.jpg'),
    Image.asset('photo3.jpg'),
  ],
)

Clipping and Borders

Rounded Corners

ClipRRect(
  borderRadius: BorderRadius.circular(20),
  child: Image.asset(
    'assets/photo.jpg',
    fit: BoxFit.cover,
    width: 300,
    height: 200,
  ),
)

Circular Image

ClipOval(
  child: Image.asset(
    'assets/profile.jpg',
    fit: BoxFit.cover,
    width: 150,
    height: 150,
  ),
)

With Border

Container(
  decoration: BoxDecoration(
    border: Border.all(color: Colors.white, width: 4),
    borderRadius: BorderRadius.circular(20),
  ),
  child: ClipRRect(
    borderRadius: BorderRadius.circular(16),
    child: Image.asset('assets/photo.jpg', fit: BoxFit.cover),
  ),
)

Custom Shape

ClipPath(
  clipper: MyCustomClipper(),
  child: Image.asset('assets/photo.jpg'),
)

Error Handling

With Placeholder

Image.asset(
  'assets/photo.jpg',
  fit: BoxFit.cover,
  errorBuilder: (context, error, stackTrace) {
    return Container(
      color: Colors.grey[800],
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.broken_image, size: 48, color: Colors.grey),
          Text('Image not found', style: TextStyle(color: Colors.grey)),
        ],
      ),
    );
  },
)

Fallback Chain

Widget buildImage(String? path) {
  if (path == null) {
    return _buildPlaceholder();
  }

  return Image.asset(
    path,
    fit: BoxFit.cover,
    errorBuilder: (_, __, ___) => _buildPlaceholder(),
  );
}

Widget _buildPlaceholder() {
  return Container(
    color: Colors.grey[800],
    child: Icon(Icons.image, color: Colors.grey),
  );
}

Best Practices

1. Match Resolution to Output

// For 1080x1920 output, use similarly sized images
// Don't load 4K images if output is 1080p

// Good
Image.asset('assets/bg_1080.jpg')  // 1080x1920

// Wasteful
Image.asset('assets/bg_4k.jpg')    // 2160x3840

2. Use Appropriate Formats

Use Case Format Why
Photos JPEG Smaller file size
Transparency PNG Alpha channel support
Logos/icons PNG/SVG Crisp edges
Simple graphics PNG Lossless

3. Preload Critical Images

// In initState or before rendering
await precacheImage(AssetImage('assets/important.jpg'), context);

4. Organize Assets

assets/
├── images/
│   ├── backgrounds/
│   │   ├── gradient_dark.jpg
│   │   └── texture_noise.png
│   ├── photos/
│   │   ├── photo_001.jpg
│   │   └── photo_002.jpg
│   └── ui/
│       ├── logo.png
│       └── icon_play.png

5. Consider Memory

For compositions with many images, consider: - Loading images on demand - Using lower resolution previews - Clearing image cache between scenes