dotLottie Flutter Player Examples

Practical examples of using the dotLottie Flutter player, including playback control, state machines, theming, and slots.

dotLottie Flutter Player Examples

Basic Animation with Controls

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

class AnimationControlsPage extends StatefulWidget {
  @override
  State<AnimationControlsPage> createState() => _AnimationControlsPageState();
}

class _AnimationControlsPageState extends State<AnimationControlsPage> {
  DotLottieViewController? _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('dotLottie Controls')),
      body: Column(
        children: [
          DotLottieView(
            source: 'https://lottie.host/example/animation.lottie',
            sourceType: 'url',
            loop: true,
            onViewCreated: (c) => setState(() => _controller = c),
            onLoad: () => print('Loaded'),
            onComplete: () => print('Complete'),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: const Icon(Icons.play_arrow),
                onPressed: () => _controller?.play(),
              ),
              IconButton(
                icon: const Icon(Icons.pause),
                onPressed: () => _controller?.pause(),
              ),
              IconButton(
                icon: const Icon(Icons.stop),
                onPressed: () => _controller?.stop(),
              ),
            ],
          ),
          ElevatedButton(
            onPressed: () => _controller?.setSpeed(2.0),
            child: const Text('2x Speed'),
          ),
          ElevatedButton(
            onPressed: () => _controller?.setMode('bounce'),
            child: const Text('Bounce Mode'),
          ),
        ],
      ),
    );
  }
}

Controlling Fit and Alignment

Use the fit prop to control how the animation is inscribed into its bounds, just like Flutter's Image widget. Changing fit at runtime pushes the new layout to the native player without recreating the view.

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

class FitExamplePage extends StatefulWidget {
  @override
  State<FitExamplePage> createState() => _FitExamplePageState();
}

class _FitExamplePageState extends State<FitExamplePage> {
  DotLottieViewController? _controller;
  BoxFit _fit = BoxFit.contain;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Fit Example')),
      body: Column(
        children: [
          // A non-square container makes the BoxFit differences obvious.
          Container(
            width: 320,
            height: 180,
            decoration: BoxDecoration(
              color: Colors.grey[200],
              border: Border.all(color: Colors.blueAccent),
            ),
            child: DotLottieView(
              source: 'assets/animation.lottie',
              sourceType: 'asset',
              autoplay: true,
              loop: true,
              fit: _fit,
              onViewCreated: (c) => _controller = c,
            ),
          ),
          const SizedBox(height: 16),
          Wrap(
            spacing: 8,
            children: [
              BoxFit.contain,
              BoxFit.cover,
              BoxFit.fill,
              BoxFit.fitWidth,
              BoxFit.fitHeight,
              BoxFit.none,
            ].map((fit) {
              return FilterChip(
                label: Text(fit.name),
                selected: _fit == fit,
                onSelected: (_) => setState(() => _fit = fit),
              );
            }).toList(),
          ),
          const SizedBox(height: 16),
          // Use setLayout to change alignment without changing the fit prop.
          ElevatedButton(
            onPressed: () => _controller?.setLayout(
              BoxFit.contain,
              alignment: Alignment.topLeft,
            ),
            child: const Text('Align top-left (contain)'),
          ),
          ElevatedButton(
            onPressed: () => _controller?.setLayout(
              BoxFit.contain,
              alignment: Alignment.center,
            ),
            child: const Text('Reset to center (contain)'),
          ),
        ],
      ),
    );
  }
}

You can also call setLayout() directly on the controller to change both the fit mode and the alignment at any time:

// Pin the animation to the bottom-right corner.
await _controller?.setLayout(
  BoxFit.contain,
  alignment: Alignment.bottomRight,
);

// Fill the container completely.
await _controller?.setLayout(BoxFit.fill);

Applying a Theme

DotLottieView(
  source: 'assets/themed_animation.lottie',
  sourceType: 'asset',
  autoplay: true,
  loop: true,
  themeId: 'dark-mode',
  onViewCreated: (controller) async {
    // Switch themes after 3 seconds
    await Future.delayed(const Duration(seconds: 3));
    await controller.setTheme('light-mode');
  },
)

Playing a Segment

DotLottieView(
  source: 'assets/animation.lottie',
  sourceType: 'asset',
  autoplay: true,
  loop: true,
  segment: [10, 60],  // Play frames 10 through 60
)

Playing a Named Marker

DotLottieView(
  source: 'assets/animation.lottie',
  sourceType: 'asset',
  autoplay: true,
  loop: false,
  marker: 'intro',
  onComplete: () => print('Intro marker finished'),
)

Updating Slots Dynamically

DotLottieView(
  source: 'assets/branded_animation.lottie',
  sourceType: 'asset',
  autoplay: true,
  loop: true,
  onViewCreated: (controller) async {
    // Update brand color and headline text via JSON string
    await controller.setSlots(
      '{"primaryColor": [1.0, 0.0, 0.0, 1.0], "headlineText": {"t": "Hello Flutter!"}}'
    );
  },
)

Using a State Machine

class InteractiveAnimation extends StatefulWidget {
  @override
  State<InteractiveAnimation> createState() => _InteractiveAnimationState();
}

class _InteractiveAnimationState extends State<InteractiveAnimation> {
  DotLottieViewController? _controller;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        DotLottieView(
          source: 'assets/interactive.lottie',
          sourceType: 'asset',
          stateMachineId: 'buttonFSM',
          onViewCreated: (c) => setState(() => _controller = c),
          stateMachineOnStateEntered: (state) {
            print('Entered state: $state');
          },
          stateMachineOnTransition: (from, to) {
            print('Transition: $from -> $to');
          },
        ),
        ElevatedButton(
          onPressed: () async {
            await _controller?.stateMachineFire('click');
          },
          child: const Text('Trigger Click'),
        ),
        ElevatedButton(
          onPressed: () async {
            await _controller?.stateMachineSetBooleanInput('isActive', true);
          },
          child: const Text('Set isActive = true'),
        ),
      ],
    );
  }
}

Multi-Animation File

DotLottieView(
  source: 'assets/multi.lottie',
  sourceType: 'asset',
  animationId: 'scene-2',
  autoplay: true,
  loop: true,
  onViewCreated: (controller) async {
    // Switch to another animation after 5 seconds
    await Future.delayed(const Duration(seconds: 5));
    await controller.loadAnimation('scene-3');
  },
)
Last updated: August 13, 2026 at 9:17 AMEdit this page