Skip to content

Migration

Fluvie used to spread motion, effects, and layout across many widgets. The v1 surface consolidates them. There is now one motion type (Animation), one attachment (.animate([...])), and plain Flutter layout. This page maps each old name to its replacement.

Nothing in the authoring surface changed. A Video you wrote for 0.2 builds unchanged in 0.3. What changed is the shape of a project around it.

A Fluvie project is now a directory holding a composition file, an assets/ folder, and a pubspec.yaml. There is no app, no lib/main.dart, no capture harness, and no registry. fluvie render and fluvie preview take the .dart file directly and generate whatever they need, per invocation.

Nothing breaks on upgrade. resolveProjectDir still finds a project by its pubspec, and fluvie render <key> --out <file> is unchanged, so your committed harness and registry keep rendering exactly as they did. fluvie list still prints your keys. You can stop here.

Delete the machinery and render the file. For a project that looks like the 0.2 scaffold:

  1. Move the composition to the project root (or anywhere you like) and rename its builder to build:

    // lib/videos/intro.dart -> intro.dart
    Video intro() { ... } // before
    Video build() { ... } // after

    Keep the old name if you prefer, and pass --entry intro on every render and preview instead.

  2. Delete the three files you no longer own:

    Terminal window
    rm test/render/capture_harness_test.dart # the CLI generates one per render
    rm lib/main.dart # `fluvie preview` replaces the app
    rm lib/videos/compositions.dart # or wherever your registry lives

    Delete the widget test that pumped the app, too, if it only existed to prove the app booted.

  3. Render the file:

    Terminal window
    fluvie render ./lib/intro.dart --out intro.mp4
    fluvie preview ./lib/intro.dart
  4. Move your media under assets/ and drop the flutter: assets: block from your pubspec. The CLI re-derives it from the asset tree on every render and preview. It enumerates every subdirectory that holds files, which a hand-written assets/ entry does not: Flutter bundles a declared asset directory non-recursively, so assets/images/logo.png was silently missing at runtime unless you declared assets/images/ yourself.

  5. Drop the platform directories (android/, ios/, linux/, macos/, windows/, web/) if the project existed only to render. fluvie preview generates and caches its own app in ~/.cache/fluvie/preview/, outside your project.

  • The frame cache is off by default for a file target. The render digest keys on the config and the composition key, never on the composition itself, so an edited file with the same size and frame count would replay stale frames. Pass --cache to opt in. On the key path the cache is still on, and --no-cache still bypasses it.
  • fluvie init takes only --name, --dir, and --force. --path, --render / --no-render, and --yes / -y are gone. It is no longer interactive and it no longer runs flutter create, so there is nothing to say yes to.
  • fluvie preview defaults to your desktop, not the browser. A desktop preview decodes any clip through FFmpeg; a browser is limited to what WebCodecs supports, and ProRes is not on that list. Pass -d chrome for the browser.
  • renderVideo is the entry point if you host renders yourself. It replaces hand-assembling the capture shell, the media pre-pass, and the audio staging. See the rendering surface.
  • RENDER_PROJECT now means a Fluvie project, a pubspec that depends on fluvie, rather than a project containing test/render/capture_harness_test.dart. See Rendering on a server.

New in 0.3.0, not a rename: WebM/VP9 clips work, including alpha. See Images and video clips.

Version 0.2.0 renames hard: the old names are gone, not deprecated. Every change below is mechanical.

0.1.x0.2.0
Pipeline exports on package:fluvie/fluvie.dart (RenderService, RenderConfig, render, renderToSandbox, renderTemplate, sandboxes, capture services, resolver contracts, resolveAudioMix, collectors, FadeBox, FfmpegVersion, the wasm runtime)Import package:fluvie/rendering.dart. See the rendering surface
NumberFormat re-exported from the barrelImport package:intl/intl.dart yourself
FfmpegProvider, ProcessFfmpegProvider, WasmFfmpegProvider, ffmpegProviderProviderFfmpegRunner; the platform runner is selected for you through ffmpegRunnerProvider (backed by FfmpegRunnerRegistry), all on the rendering barrel
RenderService.render(provider:)RenderService.render(runner:)
Trigger.after(a) (and the JSON trigger kind "after")Trigger.whenEnds(a) / "whenEnds", the parallel twin of Trigger.whenStarts(a); old specs fail with a rename hint
Chart.bar(growIn:), Chart.line/area(drawIn:), Chart.pie/donut(sweepIn:), Chart.scatter(popIn:)reveal: on every chart: one word for “how long the built-in reveal takes”
Counter(duration:) (and the Counter spec prop "duration")Counter(reveal:) / "reveal"
Arrow(drawIn:), Shape(drawIn:), Connector(drawIn:), LowerThird(slideIn:)reveal: (annotations share the same word)
Animation.slideFade(...) (and the spec preset "slideFade")Animation.slideFadeIn(...) / "slideFadeIn"; a new slideFadeOut mirrors it
Animation.maskWipe(...)Animation.maskWipeIn(...); a new maskWipeOut mirrors it
Animation.spin(per:) (and the spec arg "per")Animation.spin(period:) / "period"
Animation.float(frequency: 0.4) (cycles per second)Animation.float(period: 2.5.seconds) (one bob per period)
Timeline(fps: 30)Timeline(): the timeline resolves at the enclosing Video’s fps, so a 60 fps video can no longer silently mistime a schedule; timeline.placements becomes placementsAt(fps)
Frame.polaroid/card/rounded/none (the decorative photo mat)PhotoFrame.*: “Frame” now always means the render clock (FrameBuilder, n.frames, RawFrame)

New preset mirrors in 0.2.0: scaleOut, glitchOut, slideFadeOut, and maskWipeOut complete every directional pair, so guessing the *Out twin of an *In preset now always works.

The authoring surface (Video, Scene, elements, Animation, Trigger, themes, specs, the preview runtime) stays on package:fluvie/fluvie.dart.

New in 0.2.0, not a rename: the VideoRenderer<T> contract unifies the render entry points. DesktopVideoRenderer (local FFmpeg, returns a File), OnDeviceVideoRenderer (mobile hardware encoder, returns a File), and WebVideoRenderer (ffmpeg.wasm, returns bytes) all implement it, so the same call shape renders on every platform.

Most renames are mechanical. Here is the most common one, an animated text line, before and after:

// old
AnimatedText('Hello', animation: EntryAnimation.slideFade());
const Text('Hello', style: _line).animate([Animation.slideFadeIn()]);

The deprecated_member lint flags many of the old type names in your code and offers a rename quick-fix for each name it knows. It does not cover the parameter renames or every name below, so read the rest by hand. Each section that follows maps one row of the consolidation table.

The old motion widgets (PropAnimation, EntryAnimation, AmbientAnimation, SlideIn, and a scene transition used as an animation) are now Animation.* presets, or Animation.from / Animation.to / Animation.keyframes for custom motion.

AnimatedProp becomes the .animate([...]) extension on any widget:

// old
AnimatedProp(animation: PropAnimation.fade(), child: Text('Hi'));
const Text('Hello', style: _line).animate([Animation.slideFadeIn()]);

See Animating elements for the full preset menu.

The Stagger widget and StaggerConfig are gone. Pass a stagger: on the animation that plays across the group:

// old
Stagger(config: StaggerConfig(each: 0.1.seconds), children: [...]);
const Column(children: [Text('A'), Text('B')]).animate([
Animation.fadeIn(stagger: const Stagger.each(Time.frames(6))),
]);

EffectOverlay, ParticleEffect, MaskedClip, and ParallaxLayer are gone. The pixel post-effects (grain, vignette, particles, shader, maskWipeIn, and the rest) sit in the same .animate([...]) list as everything else:

// old
EffectOverlay(effects: [ParticleEffect.confetti()], child: card);
const ColoredBox(color: Color(0xFF13131F)).animate([
Animation.grain(0.12),
Animation.vignette(0.4),
]);

See Shaders and effects.

The V-prefixed wrappers (VStack, VColumn, VRow, VCenter, VPadding, VPositioned, VSizedBox) are gone. Use the real Flutter widgets:

// old
VColumn(children: [VCenter(child: text)]);
const Column(
children: [
Text('Plain Flutter', style: _line),
Text('Row, Column, Stack, Center', style: _line),
],
);

LayerStack, Layer, and VideoTimingMixin had no public replacement; their job is now the internal TimeScope. See Layouts.

Raw image use, KenBurnsImage, PhotoCard, and PolaroidFrame collapse into one Image plus Animation.kenBurns and the PhotoFrame.* styles:

// old
PolaroidFrame(child: KenBurnsImage.network(url));
PhotoFrame.polaroid(
child: Image.network(url, fit: BoxFit.cover).animate([Animation.kenBurns()]),
);

EmbeddedVideo and VideoSequence become one Clip:

// old
EmbeddedVideo.network(url, muted: true);
Clip.network(url, audio: const ClipAudio.muted(), fit: BoxFit.cover);

See Images and video clips.

AnimatedText, FadeText, Fade, and FadeContainer become a plain Text plus .animate(). TypewriterText becomes Typewriter. CounterText and DataDrivenText become Counter. FloatingElement becomes Animation.float. See Text and typography.

AnimatedChart becomes Chart. StatCard and Collage are no longer core widgets; they are recipes you compose from the public API (the built-in templates show how). CameraFocus moves onto the scene as Scene(camera: Camera.*). See Charts and data.

Loop becomes a repeat: on the animation:

// old
Loop(child: spinner);
const Text('Spinning', style: _line).animate([
Animation.spin(repeat: const Repeat.forever()),
]);

AudioTrack, AudioSource, and BackgroundAudio become Audio.music and Audio.sfx:

// old
BackgroundAudio(AudioTrack.asset(path));
Audio.music(path, fadeOut: const Time.seconds(0.5)),

AudioReactive, BpmDetector, and FrequencyAnalyzer become Trigger.beat() plus the reactive presets (Animation.pulse(on: AudioBand.bass) and Animation.scaleY). EncodingConfig becomes Export.*:

// old
EncodingConfig(crf: 14);
const Export.mp4(quality: Quality.max);

See Audio and captions and Exporting your video.

SyncAnchor and SyncAnchorRegistry are gone from the public surface. They power Trigger and Anchor from the inside. Name a moment with an Anchor and react to it with a Trigger. See Timing and triggers.

  • Animating elements: the one motion list that most renames lead to.
  • Cheatsheet: the full v1 surface on one page.
  • FAQ: the questions a new user asks first.