tsacdop-podcast-app-android/lib/util/audiopanel.dart

321 lines
9.6 KiB
Dart
Raw Normal View History

2020-07-28 06:35:57 +02:00
import 'dart:math' as math;
2020-02-14 07:32:21 +01:00
import 'package:flutter/material.dart';
2020-07-28 06:35:57 +02:00
import 'extension_helper.dart';
2020-07-08 21:48:41 +02:00
enum SlideDirection { up, down }
2020-02-14 07:32:21 +01:00
class AudioPanel extends StatefulWidget {
final Widget miniPanel;
final Widget expandedPanel;
2020-07-28 06:35:57 +02:00
final Widget optionPanel;
2020-07-08 21:48:41 +02:00
final double minHeight;
final double maxHeight;
2020-07-29 14:06:49 +02:00
2020-07-08 21:48:41 +02:00
AudioPanel(
{@required this.miniPanel,
@required this.expandedPanel,
2020-07-28 06:35:57 +02:00
this.optionPanel,
2020-07-29 14:06:49 +02:00
this.minHeight = 70,
2020-07-08 21:48:41 +02:00
this.maxHeight = 300,
Key key})
: super(key: key);
2020-02-14 07:32:21 +01:00
@override
2020-07-29 14:06:49 +02:00
AudioPanelState createState() => AudioPanelState();
2020-02-14 07:32:21 +01:00
}
2020-07-29 14:06:49 +02:00
class AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
2020-02-14 07:32:21 +01:00
double initSize;
double _startdy;
double _move = 0;
AnimationController _controller;
2020-07-08 21:48:41 +02:00
AnimationController _slowController;
Animation _animation;
2020-07-08 21:48:41 +02:00
SlideDirection _slideDirection;
2020-07-28 06:35:57 +02:00
double _expandHeight;
2020-02-14 07:32:21 +01:00
@override
void initState() {
2020-07-08 21:48:41 +02:00
initSize = widget.minHeight;
2020-02-14 07:32:21 +01:00
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 50))
2020-02-14 07:32:21 +01:00
..addListener(() {
if (mounted) setState(() {});
2020-02-14 07:32:21 +01:00
});
2020-07-08 21:48:41 +02:00
_slowController =
AnimationController(vsync: this, duration: Duration(milliseconds: 200))
..addListener(() {
if (mounted) setState(() {});
2020-07-08 21:48:41 +02:00
});
2020-07-26 12:20:42 +02:00
_animation =
Tween<double>(begin: 0, end: initSize).animate(_slowController);
_controller.forward();
2020-07-28 06:35:57 +02:00
_slideDirection = SlideDirection.up;
super.initState();
2020-07-28 06:35:57 +02:00
_expandHeight = 600;
2020-02-14 07:32:21 +01:00
}
@override
void dispose() {
_controller.dispose();
2020-07-08 21:48:41 +02:00
_slowController.dispose();
2020-02-14 07:32:21 +01:00
super.dispose();
}
2020-07-28 06:35:57 +02:00
double _getHeight() {
if (_animation.value >= _expandHeight) {
return _expandHeight;
} else if (_animation.value <= widget.minHeight) {
return widget.minHeight;
} else {
return _animation.value;
}
}
2020-02-14 07:32:21 +01:00
@override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
Container(
2020-07-08 21:48:41 +02:00
child: (_animation.value > widget.minHeight + 30)
? Positioned.fill(
child: GestureDetector(
2020-07-29 14:06:49 +02:00
onTap: backToMini,
child: Container(
color: Theme.of(context)
.scaffoldBackgroundColor
2020-07-28 06:35:57 +02:00
.withOpacity(0.9 *
math.min(_animation.value / widget.maxHeight, 1)),
),
2020-02-14 07:32:21 +01:00
),
)
: Center(),
),
2020-07-29 14:06:49 +02:00
Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
2020-07-26 12:20:42 +02:00
onVerticalDragStart: _start,
onVerticalDragUpdate: _update,
onVerticalDragEnd: (event) => _end(),
child: Container(
2020-07-28 06:35:57 +02:00
height: _getHeight(),
2020-07-08 21:48:41 +02:00
child: _animation.value < widget.minHeight + 30
? Container(
color: Theme.of(context).primaryColor,
child: Opacity(
2020-07-08 21:48:41 +02:00
opacity: _animation.value > widget.minHeight
? (widget.minHeight + 30 - _animation.value) / 40
: 1,
2020-07-29 14:06:49 +02:00
child: widget.miniPanel,
),
)
: Container(
decoration: BoxDecoration(
2020-07-28 06:35:57 +02:00
color: context.primaryColor,
boxShadow: [
BoxShadow(
2020-07-29 14:06:49 +02:00
offset: Offset(0, -1),
blurRadius: 1,
2020-07-29 14:06:49 +02:00
color: context.brightness == Brightness.light
? Colors.grey[400].withOpacity(0.5)
: Colors.grey[800],
),
],
),
child: SingleChildScrollView(
2020-07-08 21:48:41 +02:00
physics: const NeverScrollableScrollPhysics(),
child: Opacity(
2020-07-08 21:48:41 +02:00
opacity: _animation.value < (widget.maxHeight - 50)
? (_animation.value - widget.minHeight) /
(widget.maxHeight - widget.minHeight - 50)
: 1,
2020-07-29 14:06:49 +02:00
child: SizedBox(
2020-07-28 06:35:57 +02:00
height: math.max(widget.maxHeight,
math.min(_animation.value, _expandHeight)),
child: widget.expandedPanel,
),
),
2020-02-14 07:32:21 +01:00
),
),
),
),
2020-02-14 07:32:21 +01:00
),
]);
}
2020-07-29 14:06:49 +02:00
backToMini() {
setState(() {
2020-07-08 21:48:41 +02:00
_animation = Tween<double>(begin: initSize, end: widget.minHeight)
.animate(_slowController);
initSize = widget.minHeight;
});
2020-07-08 21:48:41 +02:00
_slowController.forward();
2020-02-14 07:32:21 +01:00
}
2020-07-29 14:06:49 +02:00
scrollToTop() {
setState(() {
_animation = Tween<double>(begin: initSize, end: _expandHeight)
.animate(_slowController);
initSize = _expandHeight;
});
_slowController.forward();
}
2020-02-14 07:32:21 +01:00
_start(DragStartDetails event) {
setState(() {
_startdy = event.localPosition.dy;
_animation =
Tween<double>(begin: initSize, end: initSize).animate(_controller);
2020-02-14 07:32:21 +01:00
});
_controller.forward();
}
_update(DragUpdateDetails event) {
setState(() {
_move = _startdy - event.localPosition.dy;
_animation = Tween<double>(begin: initSize, end: initSize + _move)
.animate(_controller);
2020-07-08 21:48:41 +02:00
_slideDirection = _move > 0 ? SlideDirection.up : SlideDirection.down;
2020-02-14 07:32:21 +01:00
});
_controller.forward();
}
2020-07-29 14:06:49 +02:00
_end() async {
2020-07-08 21:48:41 +02:00
if (_slideDirection == SlideDirection.up) {
if (_move > 20) {
2020-07-28 06:35:57 +02:00
if (_animation.value > widget.maxHeight + 20) {
setState(() {
_animation =
Tween<double>(begin: _animation.value, end: _expandHeight)
.animate(_slowController);
initSize = _expandHeight;
});
2020-07-29 14:06:49 +02:00
_slowController.forward();
2020-07-28 06:35:57 +02:00
} else {
setState(() {
_animation =
2020-07-29 14:06:49 +02:00
Tween<double>(begin: widget.maxHeight, end: widget.maxHeight)
.animate(_controller);
2020-07-28 06:35:57 +02:00
initSize = widget.maxHeight;
});
2020-07-29 14:06:49 +02:00
_controller.forward();
2020-07-28 06:35:57 +02:00
}
2020-07-08 21:48:41 +02:00
} else {
setState(() {
_animation =
Tween<double>(begin: _animation.value, end: widget.minHeight)
.animate(_controller);
initSize = widget.minHeight;
});
_controller.forward();
}
} else if (_slideDirection == SlideDirection.down) {
if (_move > -50) {
2020-07-28 06:35:57 +02:00
if (_animation.value > widget.maxHeight) {
setState(() {
_animation =
Tween<double>(begin: _animation.value, end: _expandHeight)
.animate(_slowController);
initSize = _expandHeight;
});
} else {
setState(() {
_animation =
Tween<double>(begin: _animation.value, end: widget.maxHeight)
.animate(_slowController);
initSize = widget.maxHeight;
});
}
2020-07-08 21:48:41 +02:00
_slowController.forward();
} else {
2020-07-28 06:35:57 +02:00
if (_animation.value > widget.maxHeight) {
setState(() {
_animation =
Tween<double>(begin: _animation.value, end: widget.maxHeight)
.animate(_slowController);
initSize = widget.maxHeight;
});
} else {
setState(() {
_animation =
Tween<double>(begin: _animation.value, end: widget.minHeight)
.animate(_controller);
initSize = widget.minHeight;
});
}
2020-07-08 21:48:41 +02:00
_controller.forward();
}
}
2020-07-28 06:35:57 +02:00
if (_animation.value >= _expandHeight) {
2020-02-14 07:32:21 +01:00
setState(() {
2020-07-28 06:35:57 +02:00
initSize = _expandHeight;
2020-02-14 07:32:21 +01:00
});
2020-07-08 21:48:41 +02:00
} else if (_animation.value < widget.minHeight) {
2020-02-14 07:32:21 +01:00
setState(() {
2020-07-08 21:48:41 +02:00
initSize = widget.minHeight;
2020-02-14 07:32:21 +01:00
});
}
}
}
2020-07-29 14:06:49 +02:00
class _AudioPanelRoute extends StatefulWidget {
_AudioPanelRoute({this.expandPanel, this.height, Key key}) : super(key: key);
final Widget expandPanel;
final double height;
@override
__AudioPanelRouteState createState() => __AudioPanelRouteState();
}
class __AudioPanelRouteState extends State<_AudioPanelRoute> {
@override
Widget build(BuildContext context) {
return MediaQuery.removePadding(
context: context,
removeTop: true,
child: Scaffold(
body: Stack(children: <Widget>[
Container(
child: Positioned.fill(
child: GestureDetector(
onTap: () => Navigator.pop(context),
// child:
// Container(
// color: Theme.of(context)
// .scaffoldBackgroundColor
// .withOpacity(0.8),
//
//),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: widget.height,
decoration: BoxDecoration(
color: context.primaryColor,
boxShadow: [
BoxShadow(
offset: Offset(0, -1),
blurRadius: 1,
color: context.brightness == Brightness.light
? Colors.grey[400].withOpacity(0.5)
: Colors.grey[800],
),
],
),
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: SizedBox(
height: 300,
child: widget.expandPanel,
),
),
),
),
]),
),
);
}
}