flutter_miniplayer/lib/miniplayer.dart

170 lines
5.0 KiB
Dart
Raw Normal View History

2020-07-29 22:13:39 +02:00
library miniplayer;
2020-07-29 22:32:28 +02:00
import 'dart:async';
import 'package:flutter/material.dart';
typedef Widget MiniplayerBuilder(double height, double percentage);
class Miniplayer extends StatefulWidget {
2020-08-26 16:38:34 +02:00
final double minHeight, maxHeight, elevation;
2020-07-29 22:32:28 +02:00
final MiniplayerBuilder builder;
final Curve curve;
final Color backgroundColor;
2020-08-26 16:40:39 +02:00
final Duration duration;
final ValueNotifier<double> valueNotifier;
2020-07-29 22:32:28 +02:00
2020-07-30 17:01:37 +02:00
const Miniplayer({
Key key,
@required this.minHeight,
@required this.maxHeight,
@required this.builder,
this.curve = Curves.easeInQuart,
this.elevation = 0,
this.backgroundColor = const Color(0x70000000),
this.valueNotifier,
2020-08-26 16:40:39 +02:00
this.duration = const Duration(milliseconds: 300),
2020-07-30 17:01:37 +02:00
}) : super(key: key);
2020-07-29 22:32:28 +02:00
@override
_MiniplayerState createState() => _MiniplayerState();
}
class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
ValueNotifier<double> heightNotifier;
2020-07-29 22:32:28 +02:00
double _height;
double _prevHeight;
2020-07-31 09:51:40 +02:00
//Used to set the height after the animation is complete
2020-07-29 22:32:28 +02:00
double _endHeight;
bool _up;
StreamController<double> _heightController =
StreamController<double>.broadcast();
AnimationController _animationController;
Animation<double> _sizeAnimation;
@override
void initState() {
if (widget.valueNotifier == null)
heightNotifier = ValueNotifier(widget.minHeight);
else
heightNotifier = widget.valueNotifier;
2020-07-29 22:32:28 +02:00
_animationController = AnimationController(
vsync: this,
2020-08-26 16:40:39 +02:00
duration: widget.duration,
2020-07-29 22:32:28 +02:00
);
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reset();
heightNotifier.value = _endHeight;
2020-07-29 22:32:28 +02:00
_height = _endHeight;
}
});
_height = widget.minHeight;
super.initState();
}
@override
void dispose() {
_heightController.close();
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
builder: (BuildContext context, double value, Widget child) {
var _percentage = ((value - widget.minHeight)) /
(widget.maxHeight - widget.minHeight);
return Stack(
alignment: Alignment.bottomCenter,
children: [
if (_percentage > 0)
GestureDetector(
2020-08-03 15:09:25 +02:00
onTap: () => _animateToHeight(widget.minHeight),
child: Opacity(
opacity: _percentage,
child: Container(color: widget.backgroundColor),
2020-07-29 22:32:28 +02:00
),
),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: value,
child: GestureDetector(
child: Material(
elevation: widget.elevation,
child: Container(
constraints: BoxConstraints.expand(),
child: widget.builder(value, _percentage),
2020-07-30 17:01:37 +02:00
),
),
onTap: () {
bool up = _height != widget.maxHeight;
2020-08-03 15:09:25 +02:00
_animateToHeight(up ? widget.maxHeight : widget.minHeight);
},
onPanEnd: (details) async {
if (_up)
2020-08-03 15:09:25 +02:00
_animateToHeight(widget.maxHeight);
else
2020-08-03 15:09:25 +02:00
_animateToHeight(widget.minHeight);
},
onPanUpdate: (details) {
_prevHeight = _height;
//details.delta.dy < 0 -> -- = +
var h = _height -= details.delta.dy;
//Makes sure that height !> maxHeight && !< minHeight
if (h > widget.maxHeight) h = widget.maxHeight;
if (h < widget.minHeight) h = widget.minHeight;
//Makes sure that the widget wont rebuild unnecessarily
if (_prevHeight == h &&
(h == widget.minHeight || h == widget.maxHeight))
return;
_height = h;
if (_height == widget.maxHeight)
_up = true;
else if (_height == widget.minHeight)
_up = false;
else
_up = _prevHeight < _height;
heightNotifier.value = h;
},
2020-07-29 22:32:28 +02:00
),
),
),
],
);
2020-07-29 22:32:28 +02:00
},
valueListenable: heightNotifier,
2020-07-29 22:32:28 +02:00
);
}
2020-08-03 15:09:25 +02:00
void _animateToHeight(final double h) {
2020-07-29 22:32:28 +02:00
_endHeight = h;
_sizeAnimation = Tween(
begin: _height,
end: h,
).animate(
CurvedAnimation(parent: _animationController, curve: widget.curve));
_sizeAnimation.addListener(() {
2020-07-31 09:51:40 +02:00
if (!(_sizeAnimation.value > widget.maxHeight) &&
!(_sizeAnimation.value < widget.minHeight))
heightNotifier.value = _sizeAnimation.value;
2020-07-29 22:32:28 +02:00
});
_animationController.forward();
}
2020-07-29 22:13:39 +02:00
}