D1APP-99 studio progress

This commit is contained in:
MohammadTaha Basiri 2022-03-17 15:35:15 +03:30
parent 5278238e19
commit 628ebbe271
12 changed files with 248 additions and 166 deletions

View File

@ -64,6 +64,9 @@ class RouteGenerator {
ChangeNotifierProvider<StudioState>(
create: (context) => StudioState(),
),
ChangeNotifierProvider<StudioDetailsState>(
create: (context) => StudioDetailsState(),
),
],
child: const Home(),
),
@ -103,8 +106,8 @@ class RouteGenerator {
);
case Routes.studioDetails:
return _createRoute(
ChangeNotifierProvider<StudioDetailsState>(
create: (context) => StudioDetailsState(),
ChangeNotifierProvider<StudioDetailsState>.value(
value: (settings.arguments as Map<String, dynamic>)['state'],
child: StudioDetails(
pageData: settings.arguments as Map<String, dynamic>,
),

View File

@ -1,3 +1,5 @@
import 'package:didvan/models/requests/studio.dart';
import 'package:didvan/models/studio_details_data.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:flutter/foundation.dart';
@ -7,8 +9,8 @@ import 'package:just_audio/just_audio.dart';
class MediaService {
static final AudioPlayer audioPlayer = AudioPlayer();
static String? audioPlayerTag;
static String? audioPlayerTitle;
static String? audioPlayerCover;
static StudioDetailsData? currentPodcast;
static StudioRequestArgs? podcastPlaylistArgs;
static void init() {
audioPlayer.positionStream.listen((event) {
@ -21,6 +23,7 @@ class MediaService {
static Future<void> handleAudioPlayback({
required dynamic audioSource,
bool isVoiceMessage = true,
}) async {
bool isNetworkAudio = audioSource.runtimeType == String;
String tag;
@ -40,9 +43,11 @@ class MediaService {
audioPlayerTag = tag;
if (isNetworkAudio) {
await audioPlayer.setUrl(
RequestHelper.baseUrl +
isVoiceMessage
? (RequestHelper.baseUrl +
audioSource +
'?accessToken=${RequestService.token}',
'?accessToken=${RequestService.token}')
: audioSource,
);
} else {
if (kIsWeb) {
@ -58,6 +63,8 @@ class MediaService {
static Future<void> resetAudioPlayer() async {
audioPlayerTag = null;
currentPodcast = null;
podcastPlaylistArgs = null;
MediaService.audioPlayer.stop();
}

View File

@ -79,7 +79,7 @@ class ActionSheetUtils {
isScrollControlled: true,
context: context,
builder: (context) => Container(
padding: const EdgeInsets.all(20),
padding: data.hasPadding ? const EdgeInsets.all(20) : EdgeInsets.zero,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
@ -91,6 +91,7 @@ class ActionSheetUtils {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Center(
child: Container(
height: 3,
@ -99,6 +100,7 @@ class ActionSheetUtils {
),
),
const SizedBox(height: 8),
if (data.title != null)
Row(
children: [
if (data.titleIcon != null)
@ -109,10 +111,10 @@ class ActionSheetUtils {
),
if (data.titleIcon != null) const SizedBox(width: 8),
DidvanText(
data.title,
data.title!,
style: Theme.of(context).textTheme.subtitle1,
color:
data.titleColor ?? Theme.of(context).colorScheme.title,
color: data.titleColor ??
Theme.of(context).colorScheme.title,
)
],
),
@ -169,6 +171,7 @@ class ActionSheetUtils {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (data.title != null)
Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -185,7 +188,7 @@ class ActionSheetUtils {
),
Expanded(
child: DidvanText(
data.title,
data.title!,
style: Theme.of(context).textTheme.headline3,
color: data.titleColor,
fontWeight: FontWeight.bold,

View File

@ -1,7 +1,7 @@
import 'dart:io';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/home/widgets/audio_slider.dart';
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
import 'package:didvan/views/home/widgets/player_controller_button.dart';
import 'package:flutter/material.dart';

View File

@ -1,8 +1,8 @@
import 'package:didvan/models/tag.dart';
import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/views/home/hashtag/hashtag_state.dart';
import 'package:didvan/views/home/widgets/news_overview.dart';
import 'package:didvan/views/home/widgets/radar_overview.dart';
import 'package:didvan/views/home/widgets/overview/news.dart';
import 'package:didvan/views/home/widgets/overview/radar.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart';

View File

@ -6,9 +6,9 @@ import 'package:didvan/views/home/studio/studio_state.dart';
import 'package:didvan/views/home/studio/widgets/slider.dart';
import 'package:didvan/views/home/studio/widgets/tab_bar.dart';
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
import 'package:didvan/views/home/widgets/podcast_overview.dart';
import 'package:didvan/views/home/widgets/overview/podcast.dart';
import 'package:didvan/views/home/widgets/overview/video.dart';
import 'package:didvan/views/home/widgets/search_field.dart';
import 'package:didvan/views/home/widgets/video_overview.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/radial_button.dart';
@ -25,7 +25,7 @@ class Studio extends StatefulWidget {
}
class _StudioState extends State<Studio> {
final FocusNode _focusNode = FocusNode();
final _focusNode = FocusNode();
@override
void initState() {
@ -119,8 +119,6 @@ class _StudioState extends State<Studio> {
: PodcastOverview(
podcast: state.studios[index],
onMarkChanged: state.changeMark,
hasUnmarkConfirmation: false,
onCommentsChanged: state.onCommentsChanged,
studioRequestArgs: StudioRequestArgs(
page: state.page,
order: state.order,

View File

@ -1,12 +1,18 @@
import 'dart:io';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/studio_details_data.dart';
import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
import 'package:didvan/views/home/widgets/audio_slider.dart';
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
import 'package:didvan/views/home/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/material.dart';
@ -30,15 +36,15 @@ class _StudioDetailsState extends State<StudioDetails> {
double _dwInPortrait = 0;
double _scaleInPortrait = 1;
bool get _isVideo => widget.pageData['isVideo'];
@override
void initState() {
final state = context.read<StudioDetailsState>();
Future.delayed(
Duration.zero,
() => state.getStudioDetails(widget.pageData['id']),
);
state.args = widget.pageData['args'];
if (Platform.isAndroid) WebView.platform = AndroidWebView();
super.initState();
@ -96,6 +102,8 @@ class _StudioDetailsState extends State<StudioDetails> {
return true;
},
child: DidvanScaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
padding: EdgeInsets.zero,
appBarData: _isFullScreen
? null
: AppBarData(
@ -103,7 +111,6 @@ class _StudioDetailsState extends State<StudioDetails> {
title: state.currentStudio.title,
),
children: [
if (_isVideo)
SizedBox(
width: ds.width,
height: _isFullScreen ? ds.height : ds.width * 9 / 16,
@ -163,8 +170,6 @@ class _StudioDetailsState extends State<StudioDetails> {
],
),
),
if (!_isVideo)
AudioPlayerWidget(podcast: state.currentStudio),
],
),
),
@ -172,39 +177,3 @@ class _StudioDetailsState extends State<StudioDetails> {
);
}
}
class AudioPlayerWidget extends StatelessWidget {
final StudioDetailsData podcast;
const AudioPlayerWidget({Key? key, required this.podcast}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Hero(
tag: podcast.media,
child: SkeletonImage(
imageUrl: podcast.image,
aspectRatio: 1 / 1,
),
),
),
const SizedBox(height: 16),
DidvanText(
podcast.title,
style: Theme.of(context).textTheme.bodyText1,
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: AudioSlider(
tag: podcast.media,
showTimer: true,
),
),
],
);
}
}

View File

@ -6,15 +6,14 @@ import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/studio.dart';
import 'package:didvan/models/studio_details_data.dart';
import 'package:didvan/providers/core_provider.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
class StudioDetailsState extends CoreProvier {
final List<StudioDetailsData?> studios = [];
late Timer _trackingTimer;
int _trackingTimerCounter = 0;
late final int initialIndex;
late final StudioRequestArgs args;
late int initialIndex;
late StudioRequestArgs args;
bool isFetchingNewItem = false;
final List<int> relatedQueue = [];
@ -29,25 +28,36 @@ class StudioDetailsState extends CoreProvier {
}
}
Future<void> getStudioDetails(int id, {bool? isForward}) async {
Future<void> getStudioDetails(int id,
{bool? isForward, StudioRequestArgs? args}) async {
if (args != null) {
this.args = args;
}
if (isForward == null) {
appState = AppState.busy;
} else {
isFetchingNewItem = true;
notifyListeners();
}
final service = RequestService(RequestHelper.studioDetails(id, args));
final service = RequestService(RequestHelper.studioDetails(id, this.args));
await service.httpGet();
_handleTracking(sendRequest: isForward != null);
if (service.isSuccess) {
final result = service.result;
final studio = StudioDetailsData.fromJson(result['studio']);
if (args.page == 0) {
if (this.args.page == 0) {
studios.add(studio);
initialIndex = 0;
appState = AppState.idle;
return;
}
if (this.args.type == 'podcast') {
MediaService.currentPodcast = studio;
MediaService.podcastPlaylistArgs = args;
await MediaService.handleAudioPlayback(
audioSource: studio.media,
isVoiceMessage: false,
);
}
StudioDetailsData? prevStudio;
if (result['prevStudio'].isNotEmpty) {
@ -121,21 +131,4 @@ class StudioDetailsState extends CoreProvier {
count;
notifyListeners();
}
Future<void> _handleTracking({bool sendRequest = true}) async {
if (!sendRequest) {
_trackingTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
_trackingTimerCounter++;
});
return;
}
//send request
_trackingTimerCounter = 0;
}
@override
void dispose() {
_trackingTimer.cancel();
super.dispose();
}
}

View File

@ -1,11 +1,21 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/studio/studio_details/studio_details.dart';
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
import 'package:didvan/views/home/studio/studio_state.dart';
import 'package:didvan/views/home/widgets/audio/audio_player_widget.dart';
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class DidvanBNB extends StatelessWidget {
final int currentTabIndex;
@ -15,6 +25,9 @@ class DidvanBNB extends StatelessWidget {
{Key? key, required this.currentTabIndex, required this.onTabChanged})
: super(key: key);
bool get _enablePlayerController =>
MediaService.currentPodcast != null || MediaService.audioPlayer.playing;
@override
Widget build(BuildContext context) {
return StreamBuilder<bool>(
@ -22,9 +35,12 @@ class DidvanBNB extends StatelessWidget {
builder: (context, snapshot) {
return Stack(
children: [
AnimatedContainer(
GestureDetector(
onTap: () => _showPlayerBottomSheet(context),
child: AnimatedContainer(
padding: const EdgeInsets.only(top: 12),
duration: DesignConfig.lowAnimationDuration,
height: snapshot.data == true ? 120 : 72,
height: _enablePlayerController ? 120 : 72,
decoration: BoxDecoration(
color: DesignConfig.isDark
? Theme.of(context).colorScheme.focused
@ -33,20 +49,77 @@ class DidvanBNB extends StatelessWidget {
top: Radius.circular(16),
),
),
child: !_enablePlayerController
? const SizedBox()
: SizedBox(
height: 48,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DidvanIconButton(
Padding(
padding: const EdgeInsets.only(
right: 12,
left: 16,
),
child: DidvanIconButton(
icon: DidvanIcons.close_regular,
gestureSize: 24,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
gestureSize: 28,
onPressed: MediaService.resetAudioPlayer,
),
),
SkeletonImage(
imageUrl: MediaService.currentPodcast!.image,
width: 32,
height: 32,
),
const SizedBox(width: 16),
if (MediaService.audioPlayerCover != null)
SkeletonImage(imageUrl: MediaService.audioPlayerCover!),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
MediaService.currentPodcast!.title,
color: DesignConfig.isDark
? null
: Theme.of(context)
.colorScheme
.secondCTA,
),
AudioSlider(
disableThumb: true,
tag: MediaService.audioPlayerTag!,
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 12,
right: 16,
),
child: DidvanIconButton(
gestureSize: 28,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
icon: snapshot.data!
? DidvanIcons.pause_solid
: DidvanIcons.play_solid,
onPressed: () {
MediaService.handleAudioPlayback(
audioSource: MediaService.audioPlayerTag,
);
},
),
),
],
),
),
),
),
Positioned(
bottom: 0,
left: 0,
@ -105,6 +178,31 @@ class DidvanBNB extends StatelessWidget {
);
});
}
void _showPlayerBottomSheet(BuildContext context) {
final detailsState = context.read<StudioDetailsState>();
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
isScrollControlled: true,
builder: (context) => ChangeNotifierProvider<StudioDetailsState>.value(
value: detailsState,
child: ExpandableBottomSheet(
background: Container(),
persistentHeader: AudioPlayerWidget(
podcast: MediaService.currentPodcast!,
),
expandableContent: Container(
height: 300,
width: double.infinity,
color: Theme.of(context).colorScheme.surface,
alignment: Alignment.center,
child: const DidvanText('!Under Construction'),
),
),
),
);
}
}
class _NavBarItem extends StatelessWidget {

View File

@ -4,6 +4,7 @@ import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/studio.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
import 'package:didvan/views/home/widgets/bookmark_button.dart';
import 'package:didvan/views/home/widgets/duration_widget.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
@ -13,6 +14,7 @@ import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class VideoOverview extends StatelessWidget {
final OverviewData video;
@ -41,6 +43,7 @@ class VideoOverview extends StatelessWidget {
'args': studioRequestArgs,
'hasUnmarkConfirmation': hasUnmarkConfirmation,
'isVideo': true,
'state': context.read<StudioDetailsState>(),
},
),
child: Row(

View File

@ -127,6 +127,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4+1"
expandable_bottom_sheet:
dependency: "direct main"
description:
name: expandable_bottom_sheet
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1+1"
fake_async:
dependency: transitive
description:

View File

@ -63,6 +63,7 @@ dependencies:
firebase_messaging: ^11.2.8
firebase_core: ^1.13.1
webview_flutter: ^3.0.1
expandable_bottom_sheet: ^1.1.1+1
dev_dependencies: