A Flutter package for programmatic video generation using declarative widgets and FFmpeg. Create animated videos from Flutter widgets with frame-level control
🌐 Visit fluvie.dev | 📖 Documentation | 🎨 Examples | 🤖 MCP Server
| Project | Description |
|---|---|
| 📦 fluvie | Main Flutter package for programmatic video generation (this repo) |
| 🤖 fluvie_mcp_server | MCP server for AI-assisted Fluvie development |
| 🌐 fluvie_website | Marketing website source code |
- Declarative Video Composition - Use familiar Flutter widgets to define video content
- Frame-Perfect Rendering - Precise control over timing and animations
- Layer System - Z-indexed layers with time-based visibility and fade transitions
- Audio Support - Background music, audio tracks with trim and fade
- Cross-Platform - Desktop (Linux, macOS, Windows) and Web support
- Pluggable FFmpeg - Use native FFmpeg or WASM for web
import 'package:fluvie/fluvie.dart';
// Define your composition
final composition = VideoComposition(
fps: 30,
durationInFrames: 150, // 5 seconds
width: 1920,
height: 1080,
child: LayerStack(
children: [
// Background layer
Layer.background(
fadeInFrames: 15,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
),
),
),
),
// Animated text
Layer(
id: 'title',
startFrame: 30,
endFrame: 120,
fadeInFrames: 15,
fadeOutFrames: 15,
child: Center(
child: TimeConsumer(
builder: (context, frame, progress) {
return Opacity(
opacity: progress,
child: Text(
'Hello Fluvie!',
style: TextStyle(fontSize: 72, color: Colors.white),
),
);
},
),
),
),
],
),
);Add fluvie to your pubspec.yaml:
dependencies:
fluvie: ^0.1.0Enable Impeller when running your app:
# For all platforms
flutter run --enable-impeller
# For specific platform
flutter run -d macos --enable-impeller
flutter run -d linux --enable-impeller
flutter run -d windows --enable-impellerFor VS Code debugging, add to your .vscode/launch.json:
{
"configurations": [
{
"name": "Fluvie (Impeller)",
"request": "launch",
"type": "dart",
"args": ["--enable-impeller"]
}
]
}Note: Impeller is enabled by default on iOS (Flutter 3.10+), but requires the --enable-impeller flag on Android and all desktop platforms.
Fluvie will automatically detect if Skia is being used and display a prominent warning dialog.
Fluvie requires FFmpeg for video encoding.
Linux:
sudo apt install ffmpegmacOS:
brew install ffmpegWindows: Download from ffmpeg.org and add to PATH.
Web:
Include in your web/index.html:
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.12.6/dist/umd/ffmpeg.js"></script>
<script src="https://unpkg.com/@ffmpeg/util@0.12.1/dist/umd/util.js"></script>Configure your server with required headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-originThe root widget that defines video dimensions, frame rate, and duration:
VideoComposition(
fps: 30,
durationInFrames: 300, // 10 seconds
width: 1920,
height: 1080,
child: YourContent(),
)Time-bounded content blocks:
Sequence(
startFrame: 0,
durationInFrames: 90, // 3 seconds
child: IntroScene(),
)Access the current frame for animations:
TimeConsumer(
builder: (context, frame, progress) {
// frame: current frame number
// progress: 0.0 to 1.0 within parent sequence
return Transform.translate(
offset: Offset(progress * 100, 0),
child: MyWidget(),
);
},
)Video-specific layers with time-based visibility:
LayerStack(
children: [
Layer.background(child: Background()),
Layer(
startFrame: 30,
endFrame: 150,
fadeInFrames: 15,
fadeOutFrames: 15,
child: Content(),
),
Layer.overlay(
blendMode: BlendMode.screen,
child: Watermark(),
),
],
)Add background music or sound effects:
AudioTrack(
source: AudioSource.asset('audio/music.mp3'),
startFrame: 0,
durationInFrames: 300,
fadeInFrames: 30,
fadeOutFrames: 30,
volume: 0.8,
)final controller = RenderController();
// In your widget tree
RenderableComposition(
controller: controller,
composition: myComposition,
);
// To render
final config = controller.config;
if (config != null) {
final service = RenderService();
await service.execute(
config: config,
repaintBoundaryKey: controller.boundaryKey,
onFrameUpdate: controller.setFrame,
onComplete: (outputPath) {
print('Video saved to: $outputPath');
},
);
}final diagnostics = await FFmpegChecker.check();
if (!diagnostics.isAvailable) {
print('FFmpeg not found: ${diagnostics.errorMessage}');
print(diagnostics.installationInstructions);
}void main() {
FluvieConfig.configure(
ffmpegPath: '/opt/ffmpeg/bin/ffmpeg',
);
runApp(MyApp());
}| Platform | Status | Provider |
|---|---|---|
| Linux | Supported | ProcessFFmpegProvider |
| macOS | Supported | ProcessFFmpegProvider |
| Windows | Supported | ProcessFFmpegProvider |
| Web | Supported | WasmFFmpegProvider |
| Android | Custom | Set via FFmpegProviderRegistry |
| iOS | Custom | Set via FFmpegProviderRegistry |
For mobile platforms, implement your own provider using ffmpeg_kit_flutter:
void main() {
FFmpegProviderRegistry.setProvider(MyFFmpegKitProvider());
runApp(MyApp());
}Run the FFmpeg checker to diagnose:
final diagnostics = await FFmpegChecker.check();
print(diagnostics);Ensure your server sends the required CORS headers. See Web Setup.
Mobile platforms require a custom provider. See Mobile Setup.
Contributions are welcome! Please read our Contributing Guidelines before submitting a PR.
MIT License - see LICENSE for details.
Created by Simon Auer