更改播放音频方式

This commit is contained in:
rang 2025-11-19 17:43:04 +08:00
parent 4a02b757bc
commit eb1955e5e6
25 changed files with 2102 additions and 1250 deletions

View file

@ -3,10 +3,8 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/image_provider.dart';
import 'package:vaani/api/library_item_provider.dart';
import 'package:vaani/constants/sizes.dart';
import 'package:vaani/features/player/providers/currently_playing_provider.dart';
import 'package:vaani/features/player/providers/session_provider.dart';
import 'package:vaani/features/player/view/widgets/player_player_pause_button.dart';
import 'package:vaani/features/player/view/widgets/player_progress_bar.dart';
import 'package:vaani/features/skip_start_end/player_skip_chapter_start_end.dart';
@ -28,32 +26,20 @@ class PlayerExpanded extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final session = ref.watch(sessionProvider).session;
if (session == null) {
return SizedBox.shrink();
}
/// all the properties that help in building the widget are calculated from the [percentageExpandedPlayer]
/// however, some properties need to start later than 0% and end before 100%
final currentBook = ref.watch(currentlyPlayingBookProvider);
if (currentBook == null) {
return const SizedBox.shrink();
}
final currentChapter = ref.watch(currentPlayingChapterProvider);
final currentBookMetadata = ref.watch(currentBookMetadataProvider);
final currentChapter = ref.watch(currentChapterProvider);
// final currentBookMetadata = ref.watch(currentBookMetadataProvider);
// max height of the player is the height of the screen
final playerMaxHeight = MediaQuery.of(context).size.height;
final availWidth = MediaQuery.of(context).size.width;
// the image width when the player is expanded
final imageSize = min(playerMaxHeight * 0.5, availWidth * 0.9);
final itemBeingPlayed =
ref.watch(libraryItemProvider(currentBook.libraryItemId));
final imageOfItemBeingPlayed = itemBeingPlayed.valueOrNull != null
? ref.watch(
coverImageProvider(itemBeingPlayed.valueOrNull!.id),
)
: null;
final imgWidget = imageOfItemBeingPlayed?.valueOrNull != null
? Image.memory(
imageOfItemBeingPlayed!.valueOrNull!,
fit: BoxFit.cover,
)
: const BookCoverSkeleton();
return Scaffold(
appBar: AppBar(
leading: IconButton(
@ -104,7 +90,7 @@ class PlayerExpanded extends HookConsumerWidget {
borderRadius: BorderRadius.circular(
AppElementSizes.borderRadiusRegular,
),
child: imgWidget,
child: BookCoverWidget(),
),
),
),
@ -133,8 +119,8 @@ class PlayerExpanded extends HookConsumerWidget {
padding: EdgeInsets.only(bottom: AppElementSizes.paddingRegular),
child: Text(
[
currentBookMetadata?.title ?? '',
currentBookMetadata?.authorName ?? '',
session.displayTitle,
session.displayAuthor,
].join(' - '),
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Theme.of(context)

View file

@ -1,13 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/image_provider.dart';
import 'package:vaani/api/library_item_provider.dart';
import 'package:vaani/constants/sizes.dart';
import 'package:vaani/features/player/providers/audiobook_player.dart';
import 'package:vaani/features/player/providers/currently_playing_provider.dart';
import 'package:vaani/features/player/providers/session_provider.dart';
import 'package:vaani/features/player/view/widgets/player_player_pause_button.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
@ -20,25 +16,11 @@ class PlayerMinimized extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentBook = ref.watch(currentlyPlayingBookProvider);
if (currentBook == null) {
return const SizedBox.shrink();
final session = ref.watch(sessionProvider).session;
if (session == null) {
return SizedBox.shrink();
}
final itemBeingPlayed =
ref.watch(libraryItemProvider(currentBook.libraryItemId));
final imageOfItemBeingPlayed = itemBeingPlayed.valueOrNull != null
? ref.watch(
coverImageProvider(itemBeingPlayed.valueOrNull!.id),
)
: null;
final imgWidget = imageOfItemBeingPlayed?.valueOrNull != null
? Image.memory(
imageOfItemBeingPlayed!.valueOrNull!,
fit: BoxFit.cover,
)
: const BookCoverSkeleton();
final bookMetaExpanded = ref.watch(currentBookMetadataProvider);
final currentChapter = ref.watch(currentPlayingChapterProvider);
final currentChapter = ref.watch(currentChapterProvider);
return PlayerMinimizedFramework(
children: [
@ -51,7 +33,7 @@ class PlayerMinimized extends HookConsumerWidget {
context.pushNamed(
Routes.libraryItem.name,
pathParameters: {
Routes.libraryItem.pathParamName!: currentBook.libraryItemId,
Routes.libraryItem.pathParamName!: session.libraryItemId,
},
);
},
@ -59,7 +41,7 @@ class PlayerMinimized extends HookConsumerWidget {
constraints: BoxConstraints(
maxWidth: playerMinimizedHeight,
),
child: imgWidget,
child: BookCoverWidget(),
),
),
),
@ -76,14 +58,14 @@ class PlayerMinimized extends HookConsumerWidget {
children: [
// AutoScrollText(
Text(
'${bookMetaExpanded?.title ?? ''} - ${currentChapter?.title ?? ''}',
'${session.displayTitle} - ${currentChapter?.title ?? ''}',
maxLines: 1, overflow: TextOverflow.ellipsis,
// velocity:
// const Velocity(pixelsPerSecond: Offset(16, 0)),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
bookMetaExpanded?.authorName ?? '',
session.displayAuthor,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
@ -127,9 +109,9 @@ class PlayerMinimizedFramework extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final player = ref.watch(audiobookPlayerProvider);
final player = ref.watch(playerProvider);
final progress =
useStream(player.positionStream, initialData: Duration.zero);
useStream(player.positionStreamInChapter, initialData: Duration.zero);
return GestureDetector(
onTap: () => context.pushNamed(Routes.player.name),
child: Container(
@ -147,7 +129,7 @@ class PlayerMinimizedFramework extends HookConsumerWidget {
// value: (progress.data ?? Duration.zero).inSeconds /
// player.book!.duration.inSeconds,
value: (progress.data ?? Duration.zero).inSeconds /
(player.duration?.inSeconds ?? 1),
(player.chapterDuration?.inSeconds ?? 1),
color: Theme.of(context).colorScheme.onPrimaryContainer,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
),

View file

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:just_audio/just_audio.dart';
import 'package:vaani/constants/sizes.dart';
import 'package:vaani/features/player/providers/audiobook_player.dart';
import 'package:vaani/features/player/providers/session_provider.dart';
class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
const AudiobookPlayerPlayPauseButton({
@ -15,18 +14,18 @@ class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
final double iconSize;
@override
Widget build(BuildContext context, WidgetRef ref) {
final player = ref.watch(audiobookPlayerProvider);
final playing = ref.watch(isPlayerPlayingProvider);
final playState = ref.watch(playStateProvider);
final player = ref.read(playerProvider.notifier);
final playPauseController = useAnimationController(
duration: const Duration(milliseconds: 200),
initialValue: 1,
);
if (playing) {
if (playState.playing) {
playPauseController.forward();
} else {
playPauseController.reverse();
}
return switch (player.processingState) {
return switch (playState.processingState) {
ProcessingState.loading || ProcessingState.buffering => const Padding(
padding: EdgeInsets.all(AppElementSizes.paddingRegular),
child: CircularProgressIndicator(),