Skip to content

This is an experimental package that provides a simple way to render widget to video/gif using ffmpeg (it is not the best way but by the moment is the best method)

License

Notifications You must be signed in to change notification settings

camilo1498/flutter_widget_recorder_ffmpeg

Repository files navigation

Record Widget and export as a Video/Gif

This is an experimental package that provides a simple way to render widget to video/gif using ffmpeg (it is not the best way but by the moment is the best method)

Demo

showcase gif showcase gif

Installation

This plugin it is not published in pub.dev yet

Add flutter_screen_recorder_ffmpeg to your pubspec.yaml dependencies and then import it.

flutter_screen_recorder_ffmpeg:
    git:
      path: https://github.com/camilo1498/flutter_widget_recorder_ffmpeg

How to use

  1. Android

  • Add uses-permission in AndroidManifest.xml file:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="31" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE"/>

inside the <application> section add the following line:

    android:requestLegacyExternalStorage="true"
  • Update kotlin version to 1.6.0 and classpath 'com.android.tools.build:gradle:7.0.4' in your build.gradle

  • Set the minSdkVersion to 25 in your build.gradle

    ios

Not tested yet...

Example

import 'dart:async';

import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screen_recorder_ffmpeg/screen_recorder.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Color colors = Colors.red;
  ScreenRecorderController controller = ScreenRecorderController();
  String outPath = '';
  bool _showDialog = false;

  @override
  void initState() {
    Permission.storage.request();
    super.initState();
  }

  int _timerStart = 5;
  recordWidget() async {
    controller.start();
    startTimer();
    setState(() {
      _showDialog = true;
    });
  }

  void startTimer() {
    Duration oneSec = const Duration(seconds: 1);
    Timer.periodic(
      oneSec,
      (Timer timer) async {
        if (_timerStart == 0) {
          setState(() {
            controller.stop();

            timer.cancel();
          });
          var path = await controller.export(renderType: RenderType.gif);
          if(path['success'] == true){
            setState((){
              outPath = path['outPath'];
            });
            await ImageGallerySaver.saveFile(outPath,
                name: "${DateTime.now()}").then((value) {

              if(value['isSuccess'] == true){
                debugPrint(value['filePath']);
              } else{
                debugPrint(value['errorMessage']);
              }
            })
                .whenComplete(() {
              setState(() {
                _showDialog = false;
              });
            });
          } else{
            setState(() {
              outPath = path['msg'];
              _showDialog = false;
            });
          }
        } else {
          setState(() {
            _timerStart--;
          });
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ScreenRecorder(
                  controller: controller,
                  child: Center(
                      child: AnimatedContainer(
                    height: 200,
                    width: 200,
                    duration: const Duration(milliseconds: 300),
                    color: colors,
                    child: Center(
                      child: AnimatedTextKit(
                        repeatForever: true,
                        animatedTexts: [
                          TyperAnimatedText('Demo',
                              speed: const Duration(milliseconds: 300))
                        ],
                      ),
                    ),
                  )),
                ),
                const SizedBox(
                  height: 20,
                ),
                SizedBox(
                  width: MediaQuery.of(context).size.width,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    mainAxisSize: MainAxisSize.max,
                    children: [
                      _colorPalette(
                          color: Colors.red,
                          onTap: () {
                            setState(() {
                              colors = Colors.red;
                            });
                          }),
                      _colorPalette(
                          color: Colors.green,
                          onTap: () {
                            setState(() {
                              colors = Colors.green;
                            });
                          }),
                      _colorPalette(
                          color: Colors.blue,
                          onTap: () {
                            setState(() {
                              colors = Colors.blue;
                            });
                          }),
                      _colorPalette(
                          color: Colors.purpleAccent,
                          onTap: () {
                            setState(() {
                              colors = Colors.purpleAccent;
                            });
                          }),
                      _colorPalette(
                          color: Colors.orange,
                          onTap: () {
                            setState(() {
                              colors = Colors.orange;
                            });
                          }),
                    ],
                  ),
                ),
                const SizedBox(
                  height: 50,
                ),
                ElevatedButton(
                  onPressed: () {
                    recordWidget();
                  },
                  child: const Text('Start'),
                ),
                const SizedBox(
                  height: 50,
                ),
                Center(
                  child: Text(
                    'Response => $outPath'
                  ),
                )
              ],
            ),
          ),
        ),
        if (_showDialog)
          Container(
            color: Colors.black.withOpacity(0.6),
            child: const Center(
              child: SizedBox(
                height: 30,
                width: 30,
                child: CircularProgressIndicator(
                  color: Colors.red,
                ),
              ),
            ),
          )
      ],
    );
  }

  Widget _colorPalette({required Function() onTap, required Color color}) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: GestureDetector(
        onTap: onTap,
        child: Container(
          decoration: BoxDecoration(
            color: color,
            shape: BoxShape.circle,
          ),
          height: 40,
          width: 40,
        ),
      ),
    );
  }
}

About

This is an experimental package that provides a simple way to render widget to video/gif using ffmpeg (it is not the best way but by the moment is the best method)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages