ai_video_editor 1.0.0 copy "ai_video_editor: ^1.0.0" to clipboard
ai_video_editor: ^1.0.0 copied to clipboard

CapCut-style video editor for Flutter with free on-device AI: voice AI Edit, AI Shorts, captions, noise cancellation, cut-out, transitions and FFmpeg export.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:ai_video_editor/video_editor.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const VideoEditorExampleApp());
}

/// CapCut-style entry point: projects home with one-tap editor launch.
class VideoEditorExampleApp extends StatelessWidget {
  const VideoEditorExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Video Editor',
      theme: VideoEditorTheme.dark(),
      home: const VideoEditorExamplePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class VideoEditorExamplePage extends StatelessWidget {
  const VideoEditorExamplePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: ListView(
          padding: const EdgeInsets.all(VideoEditorSpacing.lg),
          children: [
            const Text('Video Editor', style: VideoEditorTextStyles.screenTitle),
            const SizedBox(height: VideoEditorSpacing.sm),
            const Text(
              'CapCut-style editing with free, on-device AI: auto captions, '
              'person cut-out, beat sync, AI music and voiceover.',
              style: VideoEditorTextStyles.bodySecondary,
            ),
            const SizedBox(height: VideoEditorSpacing.xl),
            GradientButton(
              label: 'New project',
              icon: Icons.add,
              onPressed: () => _open(context, const VideoEditorScreen()),
            ),
            const SizedBox(height: VideoEditorSpacing.md),
            _Tile(
              icon: Icons.folder_open,
              title: 'My projects',
              subtitle: 'Reopen saved drafts',
              onTap: () => _open(context, const ProjectsScreen()),
            ),
            _Tile(
              icon: Icons.tune,
              title: 'Editor with external controller',
              subtitle: 'Host app owns the controller',
              onTap: () => _open(context, const VideoEditorScreenWithController()),
            ),
          ],
        ),
      ),
    );
  }

  void _open(BuildContext context, Widget screen) {
    Navigator.of(context).push(MaterialPageRoute<void>(builder: (_) => screen));
  }
}

class _Tile extends StatelessWidget {
  const _Tile({
    required this.icon,
    required this.title,
    required this.subtitle,
    required this.onTap,
  });

  final IconData icon;
  final String title;
  final String subtitle;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    return Card(
      color: VideoEditorColors.surface,
      margin: const EdgeInsets.only(bottom: VideoEditorSpacing.sm),
      child: ListTile(
        leading: Icon(icon, color: VideoEditorColors.primary),
        title: Text(title),
        subtitle: Text(subtitle, style: VideoEditorTextStyles.caption),
        trailing: const Icon(Icons.chevron_right),
        onTap: onTap,
      ),
    );
  }
}

/// Demonstrates injecting an externally owned controller.
class VideoEditorScreenWithController extends StatefulWidget {
  const VideoEditorScreenWithController({super.key});

  @override
  State<VideoEditorScreenWithController> createState() =>
      _VideoEditorScreenWithControllerState();
}

class _VideoEditorScreenWithControllerState
    extends State<VideoEditorScreenWithController> {
  late final VideoEditorController _controller = VideoEditorController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return VideoEditorScreen(existingController: _controller);
  }
}