Scenes and transitions
A video is a list of scenes. A transition is how one scene becomes the next.
You pick the blend and Fluvie computes every frame of it. Set a default blend
on the Video and it applies to every boundary:
transition: Transition.crossFade(0.5.seconds), // overlap is on by defaultThat one line dissolves every scene into the next over half a second. Lesson
04 builds three scenes on it: a gradient title, a stats scene, and an outro.
Override the default on any boundary with Scene.enter or Scene.exit.
The four transition kinds
Section titled “The four transition kinds”Every transition shares Time and Ease, and each names its own shape:
Transition.cut(),Transition.crossFade(0.5.seconds),Transition.wipe(0.4.seconds, direction: Edge.right),Transition.zoom(0.6.seconds, into: Alignment.center),Transition.slide(0.4.seconds, from: Edge.right),| Kind | What you see |
|---|---|
cut | a hard cut: the next scene starts, no blend |
crossFade | a dissolve: the incoming scene fades in over the outgoing |
wipe | a travelling reveal toward direction (Edge.right reveals left to right) |
zoom | the outgoing scene scales up and fades while the incoming settles in |
slide | a push: the incoming slides in from from, the outgoing slides off |
Overlap and your video’s length
Section titled “Overlap and your video’s length”overlap decides what a transition does to the total length. It is the one
choice that changes how long your video runs.
overlap: trueshares the window. The incoming scene starts early, so the two scenes play at the same time during the blend and the total shortens by the transition’s duration.crossFadeturns it on by default.overlap: falsekeeps every scene at its full length. Each scene plays all of its frames, then the incoming one blends in over the outgoing scene’s final frame, and the total stays the same.
Lesson 04 uses both. The default crossFade overlaps, so its 15 frames come off the total. The outro’s wipe runs sequentially, so it leaves the length alone:
Scene.centered( duration: 3.seconds, background: Background.color(_ink), enter: Transition.wipe(0.4.seconds, overlap: false), child: const Text('See you next year', style: _outro).animate([ Animation.blurIn(), Animation.fadeOut(), ]),),Three 3 second scenes sum to 270 frames. The overlapping crossFade takes the
total to 255; the sequential wipe keeps it there. You never count the frames,
but video.totalFrames reports the result.
Per-scene enter and exit
Section titled “Per-scene enter and exit”A boundary sits between two scenes, so two scenes can have an opinion about it. Fluvie resolves the conflict by precedence:
- the incoming scene’s
enter - the outgoing scene’s
exit - the video default
transition - a hard cut, if nothing governs
The incoming scene wins. An explicit Transition.cut() on enter or exit
is a real choice that forces a hard cut over a non-cut video default; null
means “no opinion” and falls through to the next candidate.
Shared elements: a logo that morphs
Section titled “Shared elements: a logo that morphs”Give the same Anchor to an element in two adjacent scenes and Fluvie tweens
its position, size, and opacity across the boundary. Declare the anchor once:
final logo = Anchor('logo');Then wrap the element in a SharedElement with that anchor in each scene. The
title scene shows the brand block large and centred:
Scene( duration: 3.seconds, background: Background.gradient(const [Color(0xFF1D2671), Color(0xFFC33764)]), children: [ SharedElement( anchor: logo, child: const Box(color: _brand, size: Size(0.4, 0.4)), ), const Text('Year in review', style: _title).animate([ Animation.slideFade(at: Trigger.sceneEnd), ]), ],),The next scene shows the same block small in the corner. During the blend, an overlay paints the element travelling from the first rect to the second.
The rule is the same Anchor instance in both adjacent scenes. Equality is
identity, so two Anchor('logo') never pair. A shared element must appear in
exactly two scenes that touch; anything else raises a typed error that names
the anchor. Most elements take a shared: parameter that wraps them in a
SharedElement for you; for a plain widget, wrap it yourself.
Camera basics
Section titled “Camera basics”A Camera is a scene-wide zoom or pan. It is a property of the scene, not a
wrapper widget, and it applies outside every element’s own animation. The stats
scene pushes its camera in while a shared logo sits in the corner:
Scene( duration: 3.seconds, background: Background.color(_ink), camera: const Camera.push(zoom: 1.25), children: [ Align( alignment: Alignment.topLeft, child: SharedElement( anchor: logo, child: const Box(color: _brand, size: Size(0.12, 0.12)), ), ), const Center(child: Text('48,230', style: _stat)), Positioned( bottom: 220, child: const Text('minutes listened', style: _caption).animate([ Animation.fadeIn(delay: 1.seconds), ]), ), ],),The four moves are Camera.still(), Camera.push(...), Camera.pull(...),
and Camera.pan(from:, to:). Each eases over its over window (the whole
scene by default) and then holds. Because the shared element reads its rect off
the live scene, the morph follows the camera automatically.
Where to next
Section titled “Where to next”- Timing and triggers: anchors,
Trigger.after, and.showwindows, the vocabulary the scenes above use. - Backgrounds and gradients: the gradient and solid fills behind each scene.
- Cheatsheet: the whole shipped surface on one page.