didvan-app/lib/views/widgets/audio/player_navbar.dart

337 lines
14 KiB
Dart

import 'package:didvan/config/theme_data.dart';
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:provider/provider.dart';
import '../../../config/design_config.dart';
import '../../../constants/app_icons.dart';
import '../../../models/enums.dart';
import '../../../models/requests/radar.dart';
import '../../../routes/routes.dart';
import '../../../services/media/media.dart';
import '../../../utils/action_sheet.dart';
import '../../podcasts/podcasts_state.dart';
import '../../podcasts/studio_details/studio_details_state.dart';
import '../../podcasts/studio_details/widgets/studio_details_widget.dart';
import '../didvan/icon_button.dart';
import '../didvan/text.dart';
import '../skeleton_image.dart';
import 'audio_player_widget.dart';
import 'audio_slider.dart';
class PlayerNavBar extends StatefulWidget {
final bool inHome;
const PlayerNavBar({Key? key, required this.inHome}) : super(key: key);
@override
State<PlayerNavBar> createState() => _PlayerNavBarState();
}
class _PlayerNavBarState extends State<PlayerNavBar> {
bool _enablePlayerController(StudioDetailsState state) =>
MediaService.currentPodcast != null ||
(MediaService.audioPlayerTag?.contains('podcast') ?? false);
@override
Widget build(BuildContext context) {
return StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream,
builder: (context, isPlaying) => GestureDetector(
onTap: () => (MediaService.currentPodcast == null &&
(MediaService.audioPlayerTag ?? '')
.split('-')[1]
.isNotEmpty) ||
MediaService.currentPodcast?.description == 'radar'
? Navigator.of(context).pushNamed(
Routes.radarDetails,
arguments: {
'onMarkChanged': (id, value) {},
'onCommentsChanged': (id, value) {},
'id': MediaService.currentPodcast?.id,
'args': const RadarRequestArgs(page: 0),
'hasUnmarkConfirmation': false,
},
)
: (MediaService.audioPlayerTag ?? '').split('-')[1].isNotEmpty
? _showPlayerBottomSheet(context)
: null,
child: Consumer<StudioDetailsState>(
builder: (context, state, child) => AnimatedContainer(
height: widget.inHome
? _enablePlayerController(state)
? 72 + 32 + 32
: 72
: null,
duration: DesignConfig.lowAnimationDuration,
decoration: BoxDecoration(
color: DesignConfig.isDark
? Theme.of(context).colorScheme.focused
: Theme.of(context).colorScheme.navigation,
borderRadius: BorderRadius.vertical(
top: const Radius.circular(16),
bottom:
widget.inHome ? Radius.zero : const Radius.circular(16)),
),
alignment: widget.inHome ? Alignment.topCenter : Alignment.center,
child: Builder(builder: (context) {
if (!_enablePlayerController(state)) {
return const SizedBox();
}
if (state.appState == AppState.failed) {
Future.delayed(const Duration(seconds: 2), () {
MediaService.resetAudioPlayer();
state.update();
// _enablePlayerController(state);
});
return Padding(
padding: const EdgeInsets.only(top: 18.0),
child: DidvanText(
'اتصال اینترنت برقرار نمی‌باشد',
color: DesignConfig.isDark
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.secondCTA,
),
);
}
if (MediaService.currentPodcast == null) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 16)
.copyWith(bottom: widget.inHome ? 72 + 16 : 16),
child: Row(
children: [
DidvanIconButton(
icon: DidvanIcons.close_regular,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
gestureSize: 28,
onPressed: () async {
await MediaService.resetAudioPlayer();
},
),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.only(left: 48),
child: SpinKitThreeBounce(
size: 18,
color: DesignConfig.isDark
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.secondCTA,
),
),
),
),
],
),
);
}
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanIconButton(
icon: DidvanIcons.close_regular,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
gestureSize: 28,
onPressed: () async {
await MediaService.resetAudioPlayer();
// _enablePlayerController(state);
// state.update();
},
),
const SizedBox(width: 8),
SkeletonImage(
imageUrl: MediaService.currentPodcast!.image,
width: 32,
height: 32,
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
MediaService.currentPodcast!.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
),
AudioSlider(
disableThumb: true,
tag: MediaService.audioPlayerTag!,
),
],
),
),
const SizedBox(
width: 8,
),
// StreamBuilder<PlayingAudio?>(
// stream: MediaService.audioPlayer.onReadyToPlay,
// builder: (context, snapshot) {
// if (snapshot.data == null ||
// state.appState == AppState.busy &&
// MediaService.currentPodcast?.description !=
// 'radar') {
// return SizedBox(
// height: 18,
// width: 18,
// child: CircularProgressIndicator(
// strokeWidth: 2,
// color: DesignConfig.isDark
// ? Theme.of(context).colorScheme.title
// : Theme.of(context).colorScheme.secondCTA,
// ),
// );
// }
// return const SizedBox();
// },
// ),
(state.appState != AppState.busy &&
isPlaying.data != null ||
MediaService.currentPodcast?.description == 'radar')
? DidvanIconButton(
gestureSize: 28,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
icon: isPlaying.data!
? DidvanIcons.pause_solid
: DidvanIcons.play_solid,
onPressed: () {
if (state.args?.type == 'video') {
state.getStudioDetails(
MediaService.currentPodcast!.id,
args: state.podcastArgs,
fetchOnly: true,
);
}
MediaService.handleAudioPlayback(
audioSource: MediaService.currentPodcast!.link,
id: MediaService.currentPodcast!.id,
isVoiceMessage: false,
);
},
)
: SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: DesignConfig.isDark
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.secondCTA,
),
)
],
),
);
}),
),
),
),
);
}
void _showPlayerBottomSheet(BuildContext context) {
final sheetKey = GlobalKey<ExpandableBottomSheetState>();
bool isExpanded = false;
final detailsState = context.read<StudioDetailsState>();
if (detailsState.args?.type == 'video') {
detailsState.getStudioDetails(
MediaService.currentPodcast!.id,
args: detailsState.podcastArgs,
fetchOnly: true,
);
}
final state = context.read<PodcastsState>();
showModalBottomSheet(
constraints: BoxConstraints(
maxWidth: ActionSheetUtils(context).mediaQueryData.size.width,
),
backgroundColor: Colors.transparent,
context: context,
isScrollControlled: true,
builder: (context) => ChangeNotifierProvider<PodcastsState>.value(
value: state,
child: Consumer<StudioDetailsState>(
builder: (context, state, child) => MediaQuery(
data: ActionSheetUtils(context).mediaQueryData,
child: ExpandableBottomSheet(
key: sheetKey,
background: Align(
alignment: Alignment.bottomCenter,
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
color: Theme.of(context).colorScheme.surface,
),
),
persistentHeader: GestureDetector(
onVerticalDragUpdate: (details) {
if (details.delta.dy > 10) {
Navigator.of(context).pop();
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AudioPlayerWidget(
podcast: MediaService.currentPodcast!,
),
Container(
width: MediaQuery.of(context).size.width,
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
DidvanIconButton(
size: 32,
icon: DidvanIcons.angle_up_regular,
onPressed: () {
if (!isExpanded) {
sheetKey.currentState?.expand();
isExpanded = true;
} else {
isExpanded = false;
sheetKey.currentState?.contract();
}
},
),
const SizedBox(height: 16),
],
),
),
],
),
),
expandableContent: state.appState == AppState.busy
? Container(
height: MediaQuery.of(context).size.height / 2,
alignment: Alignment.center,
child: SpinKitSpinningLines(
color: Theme.of(context).colorScheme.primary,
),
)
: StudioDetailsWidget(
onMarkChanged: (id, value) => context
.read<PodcastsState>()
.changeMark(id, value, true),
),
),
),
),
),
);
}
}