widget_record 0.0.2

  • Readme
  • Changelog
  • Example
  • Installing
  • new50

widget_record #

A widget that help you record animation

Warning: the api may change as this is a very very early version

Current known limitations:

  • output format is only apng
  • the size of the recorded widget must not change

todo list:

  • increase performances with isolates
  • improve documentations
  • improve example
  • add output format (mp4, gif, etc)

⚙️ Installation #

https://pub.dev/packages/widget_record/versions/0.0.1

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  widget_record: ^0.0.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:widget_record/widget_record.dart';
  

[0.0.1] - initial version : 24 june 2020. #

  • initial version: very basic version

[0.0.2] - improve pub scoring - better package presentation : 24 june 2020. #

  • improve pub scoring: package description was too short
  • better package presentation: readme update

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:widget_record/widget_record.dart';
import 'package:image/image.dart' as img;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Widget Recorder Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: WidgetRecorderExample(),
    );
  }
}

class WidgetRecorderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 20),
      child: Column(
        children: <Widget>[
          AnimatedCircle(),
        ],
      ),
    );
  }
}

class AnimatedCircle extends StatefulWidget {
  @override
  _AnimatedCircleState createState() => _AnimatedCircleState();
}

class _AnimatedCircleState extends State<AnimatedCircle>
    with SingleTickerProviderStateMixin {
  static const double CircleInitialDiameter = 200;
  final _colors = [Colors.red, Colors.blue, Colors.green];
  AnimationController _controller;
  WidgetRecorderController _widgetRecorderController;
  bool _recording = false;
  img.Animation _animation;
  int _colorIndex = 0;
  GlobalKey _playerKey;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));
    _controller.repeat();
  }

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

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.topCenter,
      children: <Widget>[
        Column(
          children: <Widget>[
            WidgetRecorder(
              controller: _widgetRecorderController,
              child: Stack(
                alignment: Alignment.center,
                children: <Widget>[
                  SizedBox(
                    height: CircleInitialDiameter,
                    width: CircleInitialDiameter,
                  ),
                  AnimatedBuilder(
                    animation: _controller,
                    builder: (context, child) {
                      return SizedBox(
                        height: CircleInitialDiameter * (1 - _controller.value),
                        width: CircleInitialDiameter * (1 - _controller.value),
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius:
                                BorderRadius.circular(CircleInitialDiameter),
                            color: _colors[_colorIndex % _colors.length],
                          ),
                        ),
                      );
                    },
                  ),
                ],
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FlatButton(
                  color: Colors.white,
                  onPressed: () => startRecord(),
                  child: Text('Record'),
                ),
                FlatButton(
                  color: Colors.white,
                  onPressed: () => switchColor(),
                  child: Text('Switch color'),
                )
              ],
            ),
            if (_animation != null)
              AnimationPlayer(key: _playerKey, animation: _animation),
          ],
        ),
        if (_recording)
          Container(
            height: MediaQuery.of(context).size.height -
                (MediaQuery.of(context).padding.top + 20),
            width: MediaQuery.of(context).size.width,
            color: Colors.red.withOpacity(0.1),
            child: Center(
              child: Text(
                "Recording",
                style: TextStyle(
                    color: Colors.blue, decoration: TextDecoration.none),
              ),
            ),
          ),
      ],
    );
  }

  void switchColor() => setState(() => _colorIndex++);

  void startRecord() async {
    setState(() {
      _recording = true;
    });
    _widgetRecorderController = WidgetRecorderController(
      childAnimationControler: _controller,
      fps: Fps.Fps10,
    );

    // This callback is called when the widget need a new frame
    _widgetRecorderController.addListener(notifyNewFrameReady);
    var animation = await _widgetRecorderController.captureAnimation();
    onRecordEnded(animation);
    // .then((animation) => onRecordEnded(animation));
  }

  // When recording is finished, an animation is available
  void onRecordEnded(img.Animation animation) {
    setState(() {
      _recording = false;
      _controller.repeat();
      _widgetRecorderController.removeListener(notifyNewFrameReady);
      _animation = animation;
      _playerKey = GlobalKey();
    });
  }

  // Notify the widgetRecorderController that a new frame is ready
  void notifyNewFrameReady() {
    WidgetsBinding.instance
        .addPostFrameCallback((_) => _widgetRecorderController.newFrameReady());
  }
}

class AnimationPlayer extends StatefulWidget {
  final img.Animation animation;

  const AnimationPlayer({Key key, @required this.animation}) : super(key: key);

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

class _AnimationPlayerState extends State<AnimationPlayer>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  List<List<int>> _frames;
  int _durationInMs = 0;

  @override
  void initState() {
    assert(widget.animation != null);

    _frames =
        widget.animation.frames.map((frame) => img.encodePng(frame)).toList();
    widget.animation.frames
        .forEach((frameImage) => _durationInMs += frameImage.duration);
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: _durationInMs));
    _controller.repeat();
  }

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

  @override
  Widget build(BuildContext context) => Column(
        children: <Widget>[
          Text(
            'the copy is below',
            style: TextStyle(
                color: Colors.white,
                decoration: TextDecoration.none,
                fontSize: 26),
          ),
          AnimatedBuilder(
            animation: _controller,
            builder: (context, child) => Image.memory(
              _frames[(_controller.value * widget.animation.length).toInt()],
            ),
          ),
        ],
      );
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  widget_record: ^0.0.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:widget_record/widget_record.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
5
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
50
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.13
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

because of import path [widget_record] that is in a package requiring null.

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
flutter 0.0.0
image ^2.1.13 2.1.14
Transitive dependencies
archive 2.0.13
args 1.6.0
charcode 1.1.3
collection 1.14.12 1.14.13
convert 2.1.1
crypto 2.1.5
meta 1.1.8 1.2.2
path 1.7.0
petitparser 3.0.4
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
xml 4.2.0
Dev dependencies
flutter_test