This commit is contained in:
rang 2026-01-05 17:29:24 +08:00
parent 178f3fbdb1
commit 634ffaed8c
27 changed files with 648 additions and 1012 deletions

View file

@ -62,17 +62,17 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
// add a shadow to the image elevation hovering effect
child: PlayerExpandedImage(imageSize),
),
_buildControls(imageSize),
SizedBox(
width: imageSize,
child: Padding(
padding: EdgeInsets.only(
left: AppElementSizes.paddingRegular,
right: AppElementSizes.paddingRegular,
),
child: const AudiobookChapterProgressBar(),
),
),
// _buildControls(imageSize),
// SizedBox(
// width: imageSize,
// child: Padding(
// padding: EdgeInsets.only(
// left: AppElementSizes.paddingRegular,
// right: AppElementSizes.paddingRegular,
// ),
// child: const AudiobookChapterProgressBar(),
// ),
// ),
_buildSettings(imageSize),
],
),
@ -113,7 +113,37 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
),
),
),
Hero(tag: 'player_hero', child: const PlayerMinimizedControls()),
SizedBox(
height: playerMinimizedHeight,
child: _buildBottom(),
),
],
);
}
Widget _buildBottom() {
return Row(
children: [
SizedBox(
width: 180,
child: Row(
children: [
const AudiobookPlayerSeekChapterButton(isForward: false),
// play/pause button
const AudiobookPlayerPlayPauseButton(),
const AudiobookPlayerSeekChapterButton(isForward: true),
],
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(
left: AppElementSizes.paddingRegular,
right: AppElementSizes.paddingRegular,
),
child: const AudiobookChapterProgressBar(),
),
),
],
);
}
@ -128,7 +158,7 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
const AudiobookPlayerSeekChapterButton(isForward: false),
// buttonSkipBackwards
const AudiobookPlayerSeekButton(isForward: false),
AudiobookPlayerPlayPauseButton(),
const AudiobookPlayerPlayPauseButton(),
// // buttonSkipForwards
const AudiobookPlayerSeekButton(isForward: true),
// // next chapter
@ -144,6 +174,8 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const AudiobookPlayerSeekButton(isForward: false),
const AudiobookPlayerSeekButton(isForward: true),
// speed control
const PlayerSpeedAdjustButton(),
const Spacer(),

View file

@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/constants/sizes.dart';
import 'package:vaani/features/player/providers/abs_provider.dart';
import 'package:vaani/features/player/view/widgets/audiobook_player_seek_chapter_button.dart';
import 'package:vaani/features/player/view/widgets/player_player_pause_button.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/shared/extensions/chapter.dart';
@ -22,6 +23,9 @@ class PlayerMinimized extends HookConsumerWidget {
if (currentBook == null) {
return SizedBox.shrink();
}
final size = MediaQuery.of(context).size;
//
final isVertical = size.height > size.width;
return GestureDetector(
child: Container(
height: playerMinimizedHeight,
@ -29,8 +33,10 @@ class PlayerMinimized extends HookConsumerWidget {
child: Stack(
alignment: Alignment.topCenter,
children: [
Hero(tag: 'player_hero', child: const PlayerMinimizedControls()),
PlayerMinimizedProgress(),
isVertical
? const PlayerMinimizedControls()
: const PlayerMinimizedControlsDesktop(),
const PlayerMinimizedProgress(),
],
),
),
@ -130,10 +136,7 @@ class PlayerMinimizedControls extends HookConsumerWidget {
),
// play/pause button
Padding(
padding: const EdgeInsets.only(right: 8),
child: AudiobookPlayerPlayPauseButton(),
),
const AudiobookPlayerPlayPauseButton(),
],
),
);
@ -162,3 +165,99 @@ class PlayerMinimizedProgress extends HookConsumerWidget {
);
}
}
class PlayerMinimizedControlsDesktop extends HookConsumerWidget {
const PlayerMinimizedControlsDesktop({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentBook = ref.watch(currentBookProvider);
final currentChapter = ref.watch(currentChapterProvider);
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (GoRouterState.of(context).topRoute?.name != Routes.player.name) {
context.pushNamed(Routes.player.name);
} else {
context.pop();
}
},
child: Row(
children: [
SizedBox(
width: 180,
child: Row(
children: [
const AudiobookPlayerSeekChapterButton(isForward: false),
// play/pause button
const AudiobookPlayerPlayPauseButton(),
const AudiobookPlayerSeekChapterButton(isForward: true),
],
),
),
// image
Padding(
padding: EdgeInsets.all(AppElementSizes.paddingSmall),
child: GestureDetector(
onTap: () {
// navigate to item page
if (currentBook != null) {
context.pushNamed(
Routes.libraryItem.name,
pathParameters: {
Routes.libraryItem.pathParamName!:
currentBook.libraryItemId,
},
);
}
},
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: playerMinimizedHeight,
),
child: BookCoverWidget(),
),
),
),
// author and title of the book
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: AppElementSizes.paddingRegular,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
// AutoScrollText(
Text(
'${currentBook?.metadata.title ?? ''} - ${currentChapter?.title ?? ''}',
maxLines: 1, overflow: TextOverflow.ellipsis,
// velocity:
// const Velocity(pixelsPerSecond: Offset(16, 0)),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
currentBook?.metadata.asBookMetadataExpanded.authorName ??
'',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.7),
),
),
],
),
),
),
],
),
);
}
}

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:just_audio/just_audio.dart';
import 'package:vaani/features/player/providers/abs_provider.dart'
hide PlayerState;
import 'package:vaani/features/player/core/abs_audio_player.dart';
class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
const AudiobookPlayerPlayPauseButton({
@ -21,12 +21,12 @@ class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
);
}
Widget _getIcon(AbsPlayerState playerState, BuildContext context) {
Widget _getIcon(PlayerState playerState, BuildContext context) {
if (playerState.playing) {
return Icon(size: iconSize, Icons.pause);
} else {
switch (playerState.processingState) {
case AbsProcessingState.loading || AbsProcessingState.buffering:
case ProcessingState.loading || ProcessingState.buffering:
return CircularProgressIndicator();
default:
return Icon(size: iconSize, Icons.play_arrow);
@ -34,13 +34,13 @@ class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
}
}
void _actionButtonPressed(AbsPlayerState playerState, WidgetRef ref) async {
void _actionButtonPressed(PlayerState playerState, WidgetRef ref) async {
final player = ref.read(absPlayerProvider);
if (playerState.playing) {
await player.pause();
} else {
switch (playerState.processingState) {
case AbsProcessingState.completed:
case ProcessingState.completed:
await player.seekInBook(const Duration(seconds: 0));
await player.play();
default:

View file

@ -12,6 +12,7 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final book = ref.watch(currentBookProvider);
final player = ref.watch(absPlayerProvider);
final currentChapter = ref.watch(currentChapterProvider);
final position = useStream(
@ -36,7 +37,7 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
final progress =
currentChapterProgress ?? position.data ?? const Duration(seconds: 0);
final total = currentChapter == null
? player.book?.duration ?? const Duration(seconds: 0)
? book?.duration ?? const Duration(seconds: 0)
: currentChapter.end - currentChapter.start;
return ProgressBar(
progress: progress,
@ -65,6 +66,7 @@ class AudiobookProgressBar extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final book = ref.watch(currentBookProvider);
final player = ref.read(absPlayerProvider);
final position = useStream(
player.positionInBookStream,
@ -75,7 +77,7 @@ class AudiobookProgressBar extends HookConsumerWidget {
height: AppElementSizes.barHeightLarge,
child: LinearProgressIndicator(
value: (position.data ?? const Duration(seconds: 0)).inSeconds /
(player.book?.duration ?? const Duration(seconds: 0)).inSeconds,
(book?.duration ?? const Duration(seconds: 0)).inSeconds,
borderRadius: BorderRadiusGeometry.all(Radius.circular(10)),
),
);