Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions lib/router.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:app/models/models.dart';
import 'package:app/ui/screens/screens.dart';
import 'package:app/ui/widgets/now_playing_page_route.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -60,17 +61,8 @@ class AppRouter {
}

Future<void> openNowPlayingScreen(BuildContext context) async {
await showModalBottomSheet(
context: context,
isScrollControlled: true,
useRootNavigator: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
child: const NowPlayingScreen(),
);
},
await Navigator.of(context, rootNavigator: true).push(
NowPlayingPageRoute(builder: (_) => const NowPlayingScreen()),
);
}

Expand Down
51 changes: 50 additions & 1 deletion lib/ui/screens/now_playing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
PlaybackState? _state;
Playable? _playable;
late PlayableProvider _playableProvider;
var _dragOffset = 0.0;

@override
void initState() {
Expand All @@ -52,6 +53,24 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
super.dispose();
}

void _onVerticalDragUpdate(DragUpdateDetails details) {
if (details.delta.dy < 0 && _dragOffset <= 0) return;
setState(() {
_dragOffset = (_dragOffset + details.delta.dy).clamp(0.0, double.infinity);
});
}

void _onVerticalDragEnd(DragEndDetails details) {
final screenHeight = MediaQuery.of(context).size.height;
final velocity = details.primaryVelocity ?? 0;

if (_dragOffset > screenHeight * 0.2 || velocity > 800) {
Navigator.of(context).pop();
} else {
setState(() => _dragOffset = 0);
}
}

@override
Widget build(BuildContext context) {
final playable = _playable;
Expand Down Expand Up @@ -110,7 +129,22 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
],
);

return Stack(
return GestureDetector(
onVerticalDragUpdate: _onVerticalDragUpdate,
onVerticalDragEnd: _onVerticalDragEnd,
child: AnimatedContainer(
duration: _dragOffset == 0
? const Duration(milliseconds: 200)
: Duration.zero,
curve: Curves.easeOut,
transform: Matrix4.translationValues(0, _dragOffset, 0),
child: ClipRRect(
borderRadius: Theme.of(context).platform == TargetPlatform.iOS
? const BorderRadius.vertical(top: Radius.circular(38.5))
: BorderRadius.zero,
child: Material(
type: MaterialType.transparency,
child: Stack(
children: <Widget>[
const GradientDecoratedContainer(),
frostBackground,
Expand All @@ -125,6 +159,17 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
// Drag handle
Center(
child: Container(
width: 36,
height: 5,
decoration: BoxDecoration(
color: Colors.white30,
borderRadius: BorderRadius.circular(2.5),
),
),
),
thumbnail,
infoPane,
const AudioControls(),
Expand Down Expand Up @@ -166,6 +211,10 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
),
),
],
),
),
),
),
);
}
}
9 changes: 6 additions & 3 deletions lib/ui/widgets/mini_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class _MiniPlayerProgressBarState extends State<MiniPlayerProgressBar>

subscribe(audioHandler.mediaItem.listen((mediaItem) {
if (mediaItem != null && mediaItem.duration != null) {
setState(() => _duration = mediaItem.duration ?? Duration.zero);
setState(() => _duration = mediaItem.duration!);
}
}));
}
Expand All @@ -239,15 +239,18 @@ class _MiniPlayerProgressBarState extends State<MiniPlayerProgressBar>

@override
Widget build(BuildContext context) {
if (_duration == Duration.zero) return SizedBox.shrink();
if (_duration.inMilliseconds == 0) return SizedBox.shrink();

final progress = (_position.inMilliseconds / _duration.inMilliseconds)
.clamp(0.0, 1.0);

return Container(
width: double.infinity,
alignment: Alignment.centerLeft,
height: 1.0,
color: Colors.white12,
child: FractionallySizedBox(
widthFactor: _position.inSeconds / _duration.inSeconds,
widthFactor: progress,
child: Container(color: Colors.white),
),
);
Expand Down
57 changes: 57 additions & 0 deletions lib/ui/widgets/now_playing_page_route.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';

class NowPlayingPageRoute<T> extends PageRoute<T> {
final WidgetBuilder builder;

NowPlayingPageRoute({required this.builder})
: super(fullscreenDialog: true);

@override
Color? get barrierColor => null;

@override
String? get barrierLabel => null;

@override
bool get opaque => false;

@override
bool get maintainState => true;

@override
Duration get transitionDuration => const Duration(milliseconds: 400);

@override
Duration get reverseTransitionDuration => const Duration(milliseconds: 350);

@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return builder(context);
}

@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
final offsetAnimation = Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
));

return SlideTransition(
position: offsetAnimation,
child: child,
);
}
}
Loading