double_tap_player_view

pub package

Created to handle fast forward/rewind behavior by double tap and horizontal drag like a video player.
This plugin is inspired to DoubleTapPlayerView.

Sample

sample_screenshot

Supported platforms

  • Flutter Android
  • Flutter iOS
  • Flutter web
  • Flutter desktop

Installation

Add double_tap_player_view: ^1.0.3 to your pubspec.yaml dependencies. And import it:

import 'package:double_tap_player_view/double_tap_player_view.dart';

How to use

This plugin depends riverpod.
you must wrap root widget with ProviderScope to use.

void main() {
 runApp(ProviderScope(child: MyApp()));
}

Simply create a doubleTapPlayerView widget, and pass the required params:

DoubleTapPlayerView(
  doubleTapConfig: DoubleTapConfig.create(),
  swipeConfig: SwipeConfig.create(overlayBuilder: (SwipeData data) => Text(data.toString())),
  child: VideoPlayer(_controller),
),

Params

DoubleTapPlayerView(
  doubleTapConfig: DoubleTapConfig.create(
    ignoreCenterWidth: 128, // width of the zone which the double tap event is not fired in center
    iconLeft: Icon(
      Icons.fast_rewind,
      color: Colors.white,
      size: 40,
    ),
    iconRight: Icon(
      Icons.fast_rewind,
      color: Colors.white,
      size: 40,
    ),
    curveBank: 40, // height of the oval curve bank
    ovalColor: Colors.white30,
    rippleColor: Colors.white30,
    rippleExpansionTime: const Duration(milliseconds: 400),
    expansionHoldingTime: const Duration(milliseconds: 200),
    backDropAnimDuration: const Duration(milliseconds: 400),
    fadeTime: const Duration(milliseconds: 100),
    backDrop: Colors.black26,
    // if provide [customWidgetBuilder], [ovalColor], [rippleColor], [labelBuilder], [labelStyle] must be null
    customWidgetBuilder: null, // type: Widget Function(Lr lr, int tapCount)
    labelBuilder: null, // type: `Widget Function(Lr lr, int tapCount)`
    labelStyle: const TextStyle(color: Colors.white),
    onDoubleTap: null, // type: `void Function(Lr lr)`
  ),
  swipeConfig: SwipeConfig.create(
    // type: `Widget Function(SwipeData data)` SwipeData has dx when drag starts and current dragging dx.
    @required  overlayBuilder: _overlay,
    onSwipeStart: (double dx) {}
    onSwipeCancel: (){},
    onSwipeUpdate: (SwipeData data){},
    onSwipeEnd: (SwipeData data) {},
    backDrop: Colors.black45,
  ),
  child: Container(),
);

example

this example shows the sample screenshot.

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with AfterLayoutMixin {
  VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(
        'http://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4')
      ..initialize().then((_) => setState(() {}));
  }

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

  @override
  Widget build(BuildContext context) => MaterialApp(
          home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: AspectRatio(
            aspectRatio: 16 / 9,
            child: DoubleTapPlayerView(
              doubleTapConfig: DoubleTapConfig.create(),
              swipeConfig: SwipeConfig.create(overlayBuilder: _overlay),
              child: VideoPlayer(_controller),
            ),
          ),
        ),
      ));

  @override
  void afterFirstLayout(BuildContext context) => _controller.play();

  Widget _overlay(SwipeData data) {
    final dxDiff = (data.currentDx - data.startDx).toInt();
    Duration diffDuration = Duration(seconds: dxDiff);
    final prefix = diffDuration.isNegative ? '-' : '+';
    final positionText = '${prefix}${diffDuration.printDuration()}';
    final aimedDuration = diffDuration + Duration(minutes: 5);
    final diffText = aimedDuration.printDuration();

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            positionText,
            style: const TextStyle(
              fontSize: 30,
              color: Colors.white,
            ),
          ),
          SizedBox(height: 4),
          Text(
            diffText,
            style: const TextStyle(
              fontSize: 20,
              color: Colors.white,
            ),
          ),
        ],
      ),
    );
  }
}

extension on Duration {
  /// ref: https://stackoverflow.com/a/54775297/8183034
  String printDuration() {
    String twoDigits(int n) => n.toString().padLeft(2, "0");
    String twoDigitMinutes = twoDigits(inMinutes.abs().remainder(60));
    String twoDigitSeconds = twoDigits(inSeconds.abs().remainder(60));
    return "$twoDigitMinutes:$twoDigitSeconds";
  }
}