import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:video_player/video_player.dart'; import 'chewie_progress_colors.dart'; class MaterialVideoProgressBar extends StatefulWidget { MaterialVideoProgressBar( this.controller, { ChewieProgressColors colors, this.onDragEnd, this.onDragStart, this.onDragUpdate, }) : colors = colors ?? ChewieProgressColors(); final VideoPlayerController controller; final ChewieProgressColors colors; final Function() onDragStart; final Function() onDragEnd; final Function() onDragUpdate; @override _VideoProgressBarState createState() { return _VideoProgressBarState(); } } class _VideoProgressBarState extends State { _VideoProgressBarState() { listener = () { setState(() {}); }; } VoidCallback listener; bool _controllerWasPlaying = false; VideoPlayerController get controller => widget.controller; @override void initState() { super.initState(); controller.addListener(listener); } @override void deactivate() { controller.removeListener(listener); super.deactivate(); } @override Widget build(BuildContext context) { void seekToRelativePosition(Offset globalPosition) { final box = context.findRenderObject() as RenderBox; final Offset tapPos = box.globalToLocal(globalPosition); final double relative = tapPos.dx / box.size.width; final Duration position = controller.value.duration * relative; controller.seekTo(position); } return GestureDetector( child: Center( child: Container( height: MediaQuery.of(context).size.height / 2, width: MediaQuery.of(context).size.width, color: Colors.transparent, child: CustomPaint( painter: _ProgressBarPainter( controller.value, widget.colors, ), ), ), ), onHorizontalDragStart: (DragStartDetails details) { if (!controller.value.isInitialized) { return; } _controllerWasPlaying = controller.value.isPlaying; if (_controllerWasPlaying) { controller.pause(); } if (widget.onDragStart != null) { widget.onDragStart(); } }, onHorizontalDragUpdate: (DragUpdateDetails details) { if (!controller.value.isInitialized) { return; } seekToRelativePosition(details.globalPosition); if (widget.onDragUpdate != null) { widget.onDragUpdate(); } }, onHorizontalDragEnd: (DragEndDetails details) { if (_controllerWasPlaying) { controller.play(); } if (widget.onDragEnd != null) { widget.onDragEnd(); } }, onTapDown: (TapDownDetails details) { if (!controller.value.isInitialized) { return; } seekToRelativePosition(details.globalPosition); }, ); } } class _ProgressBarPainter extends CustomPainter { _ProgressBarPainter(this.value, this.colors); VideoPlayerValue value; ChewieProgressColors colors; @override bool shouldRepaint(CustomPainter painter) { return true; } @override void paint(Canvas canvas, Size size) { final height = 2.0; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(0.0, size.height / 2), Offset(size.width, size.height / 2 + height), ), Radius.circular(4.0), ), colors.backgroundPaint, ); if (!value.isInitialized) { return; } final double playedPartPercent = value.position.inMilliseconds / value.duration.inMilliseconds; final double playedPart = playedPartPercent > 1 ? size.width : playedPartPercent * size.width; for (DurationRange range in value.buffered) { final double start = range.startFraction(value.duration) * size.width; final double end = range.endFraction(value.duration) * size.width; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(start, size.height / 2), Offset(end, size.height / 2 + height), ), Radius.circular(4.0), ), colors.bufferedPaint, ); } canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(0.0, size.height / 2), Offset(playedPart, size.height / 2 + height), ), Radius.circular(4.0), ), colors.playedPaint, ); canvas.drawCircle( Offset(playedPart, size.height / 2 + height / 2), height * 3, colors.handlePaint, ); } }