//MIT License // //Copyright (c) [2019] [Befovy] // //Permission is hereby granted, free of charge, to any person obtaining a copy //of this software and associated documentation files (the "Software"), to deal //in the Software without restriction, including without limitation the rights //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //copies of the Software, and to permit persons to whom the Software is //furnished to do so, subject to the following conditions: // //The above copyright notice and this permission notice shall be included in all //copies or substantial portions of the Software. // //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. //part of fijkplayer; import 'package:flutter/material.dart'; import 'dart:core'; import 'dart:math'; import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; /// MyFijkSlider is like Slider in Flutter SDK. /// MyFijkSlider support [cacheValue] which can be used /// to show the player's cached buffer. /// The [colors] is used to make colorful painter to draw the line and circle. class MyFijkSlider extends StatefulWidget { final double value; final double cacheValue; final ValueChanged onChanged; final ValueChanged onChangeStart; final ValueChanged onChangeEnd; final double min; final double max; final MyFijkSliderColors colors; const MyFijkSlider({ Key key, @required this.value, @required this.onChanged, this.cacheValue = 0.0, this.onChangeStart, this.onChangeEnd, this.min = 0.0, this.max = 1.0, this.colors = const MyFijkSliderColors(), }) : assert(value != null), assert(cacheValue != null), assert(min != null), assert(max != null), assert(min <= max), assert(value >= min && value <= max), super(key: key); @override State createState() { return _MyFijkSliderState(); } } class _MyFijkSliderState extends State { bool dragging = false; double dragValue; static const double margin = 2.0; @override Widget build(BuildContext context) { double v = widget.value / (widget.max - widget.min); double cv = widget.cacheValue / (widget.max - widget.min); return GestureDetector( child: Container( margin: EdgeInsets.only(left: margin, right: margin), height: double.infinity, width: double.infinity, color: Colors.transparent, child: CustomPaint( painter: _MySliderPainter(v, cv, dragging, colors: widget.colors), ), ), onHorizontalDragStart: (DragStartDetails details) { setState(() { dragging = true; }); dragValue = widget.value; if (widget.onChangeStart != null) { widget.onChangeStart(dragValue); } }, onHorizontalDragUpdate: (DragUpdateDetails details) { final box = context.findRenderObject() as RenderBox; final dx = details.localPosition.dx; dragValue = (dx - margin) / (box.size.width - 2 * margin); dragValue = max(0, min(1, dragValue)); dragValue = dragValue * (widget.max - widget.min) + widget.min; if (widget.onChanged != null) { widget.onChanged(dragValue); } }, onHorizontalDragEnd: (DragEndDetails details) { setState(() { dragging = false; }); if (widget.onChangeEnd != null) { widget.onChangeEnd(dragValue); } }, ); } } /// Colors for the MyFijkSlider class MyFijkSliderColors { const MyFijkSliderColors({ this.playedColor = const Color.fromRGBO(255, 0, 0, 0.6), this.bufferedColor = const Color.fromRGBO(50, 50, 100, 0.4), this.cursorColor = const Color.fromRGBO(255, 0, 0, 0.8), this.baselineColor = const Color.fromRGBO(200, 200, 200, 0.5), }); final Color playedColor; final Color bufferedColor; final Color cursorColor; final Color baselineColor; @override bool operator ==(Object other) => identical(this, other) || other is MyFijkSliderColors && runtimeType == other.runtimeType && hashCode == other.hashCode; @override int get hashCode => hashValues(playedColor, bufferedColor, cursorColor, baselineColor); } class _MySliderPainter extends CustomPainter { final double v; final double cv; final bool dragging; final Paint pt = Paint(); final MyFijkSliderColors colors; _MySliderPainter(this.v, this.cv, this.dragging, {this.colors = const MyFijkSliderColors()}) : assert(colors != null), assert(v != null), assert(cv != null); @override void paint(Canvas canvas, Size size) { double lineHeight = min(size.height / 2, 1); pt.color = colors.baselineColor; double radius = min(size.height / 2, 4); // draw background canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(0, size.height / 2 - lineHeight), Offset(size.width, size.height / 2 + lineHeight), ), Radius.circular(radius), ), pt, ); final double value = v * size.width; // draw played part pt.color = colors.playedColor; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(0, size.height / 2 - lineHeight), Offset(value, size.height / 2 + lineHeight), ), Radius.circular(radius), ), pt, ); // draw cached part final double cacheValue = cv * size.width; if (cacheValue > value && cacheValue > 0) { pt.color = colors.bufferedColor; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(value, size.height / 2 - lineHeight), Offset(cacheValue, size.height / 2 + lineHeight), ), Radius.circular(radius), ), pt, ); } //注意:改变拖动圆形光标,必须按Q退出App,再重新flutter run才能生效 // draw circle cursor // pt.color = colors.cursorColor; // pt.color = pt.color.withAlpha(max(0, pt.color.alpha - 50)); //radius = min(size.height / 2, dragging ? 10 : 5); pt.color = Colors.red; //radius = 10; radius = min(size.height / 2, 10); canvas.drawCircle(Offset(value, size.height / 2), radius, pt); //pt.color = colors.cursorColor; //radius = min(size.height / 2, dragging ? 6 : 3); //canvas.drawCircle(Offset(value, size.height / 2), radius, pt); } @override bool operator ==(Object other) => identical(this, other) || other is _MySliderPainter && hashCode == other.hashCode; @override int get hashCode => hashValues(v, cv, dragging, colors); @override bool shouldRepaint(_MySliderPainter oldDelegate) { return hashCode != oldDelegate.hashCode; } }