bug fixes

This commit is contained in:
MohammadTaha Basiri 2022-03-30 03:37:38 +04:30
parent a128684f27
commit 873c1f6692
8 changed files with 377 additions and 219 deletions

View File

@ -87,11 +87,6 @@ class _StudioDetailsState extends State<StudioDetails> {
state: state, state: state,
onRetry: () => state.getStudioDetails(state.studio.id), onRetry: () => state.getStudioDetails(state.studio.id),
builder: (context, state) { builder: (context, state) {
if (state.prevStudio == null &&
state.nextStudio == null &&
state.args.page != 0) {
return const SizedBox();
}
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
if (_isFullScreen) { if (_isFullScreen) {

View File

@ -75,11 +75,6 @@ class _StudioDetailsState extends State<StudioDetails> {
state: state, state: state,
onRetry: () => state.getStudioDetails(state.studio.id), onRetry: () => state.getStudioDetails(state.studio.id),
builder: (context, state) { builder: (context, state) {
if (state.prevStudio == null &&
state.nextStudio == null &&
state.args.page != 0) {
return const SizedBox();
}
// ignore: undefined_prefixed_name // ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory( ui.platformViewRegistry.registerViewFactory(
"video", "video",

View File

@ -24,6 +24,7 @@ class StudioDetailsState extends CoreProvier {
int _selectedDetailsIndex = 0; int _selectedDetailsIndex = 0;
Timer? timer; Timer? timer;
int timerValue = 10; int timerValue = 10;
bool stopOnPodcastEnds = false;
int get selectedDetailsIndex => _selectedDetailsIndex; int get selectedDetailsIndex => _selectedDetailsIndex;
set selectedDetailsIndex(int value) { set selectedDetailsIndex(int value) {
@ -45,6 +46,7 @@ class StudioDetailsState extends CoreProvier {
if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast') { if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast') {
return; return;
} }
_getDownloadsList(); _getDownloadsList();
_selectedDetailsIndex = 0; _selectedDetailsIndex = 0;
if (isForward != null) { if (isForward != null) {
@ -61,12 +63,19 @@ class StudioDetailsState extends CoreProvier {
_handlePodcastPlayback(studio); _handlePodcastPlayback(studio);
} }
if (isForward == null) { if (isForward == null) {
if (this.args.type == 'podcast') {
MediaService.audioPlayerTag = 'podcast';
}
appState = AppState.busy; appState = AppState.busy;
} }
final service = RequestService(RequestHelper.studioDetails(id, this.args)); final service = RequestService(RequestHelper.studioDetails(id, this.args));
await service.httpGet(); await service.httpGet();
nextStudio = null; nextStudio = null;
prevStudio = null; prevStudio = null;
if (stopOnPodcastEnds) {
timerValue = 10;
}
stopOnPodcastEnds = false;
if (service.isSuccess) { if (service.isSuccess) {
final result = service.result; final result = service.result;
studio = StudioDetailsData.fromJson(result['studio']); studio = StudioDetailsData.fromJson(result['studio']);
@ -109,6 +118,10 @@ class StudioDetailsState extends CoreProvier {
final duration = MediaService.audioPlayer.duration ?? final duration = MediaService.audioPlayer.duration ??
Duration(seconds: studio.duration); Duration(seconds: studio.duration);
if (event.compareTo(duration) > 0 && nextStudio != null) { if (event.compareTo(duration) > 0 && nextStudio != null) {
if (stopOnPodcastEnds) {
MediaService.resetAudioPlayer();
return;
}
getStudioDetails(nextStudio!.id, isForward: true); getStudioDetails(nextStudio!.id, isForward: true);
} }
}); });

View File

@ -15,6 +15,9 @@ class StudioState extends CoreProvier {
final List<OverviewData> studios = []; final List<OverviewData> studios = [];
final List<SliderData> sliders = []; final List<SliderData> sliders = [];
final List<int> downloadedFileIds = []; final List<int> downloadedFileIds = [];
final List<String> downloadQueue = [];
AppState downloadState = AppState.idle;
String search = ''; String search = '';
String lastSearch = ''; String lastSearch = '';
@ -147,8 +150,13 @@ class StudioState extends CoreProvier {
} }
Future<void> download(String url, String fileName) async { Future<void> download(String url, String fileName) async {
downloadState = AppState.busy;
downloadQueue.add(url);
notifyListeners();
final service = RequestService(url); final service = RequestService(url);
await service.download(fileName, videosSelected ? 'videos' : 'podcasts'); await service.download(fileName, videosSelected ? 'videos' : 'podcasts');
downloadState = AppState.idle;
downloadQueue.remove(url);
notifyListeners(); notifyListeners();
} }
} }

View File

@ -101,6 +101,7 @@ class _StudioSliderState extends State<StudioSlider> {
), ),
], ],
options: CarouselOptions( options: CarouselOptions(
autoPlayAnimationDuration: DesignConfig.mediumAnimationDuration,
onPageChanged: (index, reason) => setState( onPageChanged: (index, reason) => setState(
() => selectedIndex = index, () => selectedIndex = index,
), ),

View File

@ -4,6 +4,7 @@ import 'dart:math';
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/studio_details_data.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/media/media.dart';
@ -12,6 +13,7 @@ import 'package:didvan/views/home/studio/studio_details/studio_details_state.dar
import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/studio/studio_state.dart';
import 'package:didvan/views/home/widgets/audio/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/home/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/button.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart'; import 'package:didvan/views/widgets/ink_wrapper.dart';
@ -64,95 +66,117 @@ class AudioPlayerWidget extends StatelessWidget {
), ),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const SizedBox(), Expanded(
StatefulBuilder( child: Center(
builder: (context, setState) => Column( child: StatefulBuilder(
children: [ builder: (context, setState) => Column(
DidvanIconButton( children: [
icon: state.timer == null DidvanIconButton(
? DidvanIcons.sleep_timer_regular icon: state.timer == null && !state.stopOnPodcastEnds
: DidvanIcons.sleep_enabled_regular, ? DidvanIcons.sleep_timer_regular
color: Theme.of(context).colorScheme.title, : DidvanIcons.sleep_enabled_regular,
onPressed: () => _showSleepTimer( color: Theme.of(context).colorScheme.title,
state, onPressed: () => _showSleepTimer(
() => setState(() {}), state,
), () => setState(() {}),
),
if (state.timer != null)
DidvanText(
state.timerValue.toString() + '\'',
isEnglishFont: true,
style: Theme.of(context).textTheme.overline,
color: Theme.of(context).colorScheme.caption,
),
],
),
),
Column(
children: [
DidvanIconButton(
color: Theme.of(context).colorScheme.title,
size: 32,
icon: DidvanIcons.media_forward_solid,
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds:
MediaService.audioPlayer.position.inSeconds + 30,
),
);
},
),
DidvanText(
'30',
isEnglishFont: true,
color: Theme.of(context).colorScheme.title,
),
],
),
StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream,
builder: (context, snapshot) {
return _PlayPouseAnimatedIcon(
audioSource: podcast.media,
id: podcast.id,
);
},
),
Column(
children: [
DidvanIconButton(
size: 32,
icon: DidvanIcons.media_backward_solid,
color: Theme.of(context).colorScheme.title,
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds: max(
0,
MediaService.audioPlayer.position.inSeconds - 10,
), ),
), ),
if (state.timer != null)
DidvanText(
state.stopOnPodcastEnds
? 'پایان پادکست'
: '\'' + state.timerValue.toString(),
isEnglishFont: true,
style: Theme.of(context).textTheme.overline,
color: Theme.of(context).colorScheme.title,
),
],
),
),
),
),
Expanded(
child: Center(
child: Column(
children: [
DidvanIconButton(
color: Theme.of(context).colorScheme.title,
size: 32,
icon: DidvanIcons.media_forward_solid,
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds:
MediaService.audioPlayer.position.inSeconds +
30,
),
);
},
),
DidvanText(
'30',
isEnglishFont: true,
color: Theme.of(context).colorScheme.title,
),
],
),
),
),
Expanded(
child: Center(
child: StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream,
builder: (context, snapshot) {
return _PlayPouseAnimatedIcon(
audioSource: podcast.media,
id: podcast.id,
); );
}, },
), ),
DidvanText( ),
'10', ),
isEnglishFont: true, Expanded(
color: Theme.of(context).colorScheme.title, child: Center(
child: Column(
children: [
DidvanIconButton(
size: 32,
icon: DidvanIcons.media_backward_solid,
color: Theme.of(context).colorScheme.title,
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds: max(
0,
MediaService.audioPlayer.position.inSeconds -
10,
),
),
);
},
),
DidvanText(
'10',
isEnglishFont: true,
color: Theme.of(context).colorScheme.title,
),
],
), ),
], ),
), ),
BookmarkButton( Expanded(
gestureSize: 48, child: Center(
color: Theme.of(context).colorScheme.title, child: BookmarkButton(
value: podcast.marked, gestureSize: 48,
onMarkChanged: (value) => color: Theme.of(context).colorScheme.title,
context.read<StudioState>().changeMark(podcast.id, value), value: podcast.marked,
onMarkChanged: (value) => context
.read<StudioState>()
.changeMark(podcast.id, value),
),
),
), ),
const SizedBox(),
], ],
), ),
], ],
@ -163,13 +187,17 @@ class AudioPlayerWidget extends StatelessWidget {
Future<void> _showSleepTimer(StudioDetailsState state, update) async { Future<void> _showSleepTimer(StudioDetailsState state, update) async {
int timerValue = 10; int timerValue = 10;
final controller = FixedExtentScrollController(); final controller = FixedExtentScrollController();
bool isInit = true;
Future.delayed( Future.delayed(
const Duration(milliseconds: 100), const Duration(milliseconds: 100),
() => controller.animateTo( () async {
50 * (state.timerValue / 5 - 2), await controller.animateTo(
duration: DesignConfig.lowAnimationDuration, state.timerValue * 10,
curve: Curves.easeIn, duration: DesignConfig.lowAnimationDuration,
), curve: Curves.easeIn,
);
isInit = false;
},
); );
await ActionSheetUtils.showBottomSheet( await ActionSheetUtils.showBottomSheet(
data: ActionSheetData( data: ActionSheetData(
@ -193,38 +221,71 @@ class AudioPlayerWidget extends StatelessWidget {
child: RotatedBox( child: RotatedBox(
quarterTurns: 3, quarterTurns: 3,
child: ListWheelScrollView( child: ListWheelScrollView(
controller: controller,
physics: const FixedExtentScrollPhysics(), physics: const FixedExtentScrollPhysics(),
itemExtent: 48, controller: controller,
itemExtent: 10,
onSelectedItemChanged: (index) { onSelectedItemChanged: (index) {
final minutes = (index + 2) * 5; if (!isInit) {
state.stopOnPodcastEnds = false;
}
final minutes = index == 0 ? 1 : index;
timerValue = minutes; timerValue = minutes;
setState(() {}); setState(() {});
}, },
children: [ children: [
for (var i = 0; i < 9; i++) for (var i = 0; i < 61; i++) ...[
Center( if (i % 5 == 0)
child: Container( Center(
color: Theme.of(context).colorScheme.text, child: Container(
width: 50, color: Theme.of(context).colorScheme.text,
height: 3, width: 50,
height: 3,
),
), ),
), if (i % 5 != 0) const SizedBox(height: 3),
],
], ],
), ),
), ),
), ),
const SizedBox(height: 32),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 64),
child: DidvanButton(
style: state.timerValue ==
MediaService.audioPlayer.duration?.inMinutes &&
state.stopOnPodcastEnds
? ButtonStyleMode.primary
: ButtonStyleMode.flat,
title: 'پایان پادکست',
onPressed: () async {
state.timerValue =
MediaService.audioPlayer.duration!.inMinutes -
MediaService.audioPlayer.position.inMinutes;
await controller.animateTo(
state.timerValue * 10,
duration: DesignConfig.lowAnimationDuration,
curve: Curves.easeIn,
);
state.stopOnPodcastEnds = true;
setState(() {});
},
),
),
], ],
), ),
), ),
onConfirmed: () { onConfirmed: () {
state.timer = Timer( if (!state.stopOnPodcastEnds) {
Duration(minutes: timerValue), state.timer = Timer(
MediaService.audioPlayer.stop, Duration(minutes: timerValue),
); MediaService.audioPlayer.stop,
);
}
state.timerValue = timerValue; state.timerValue = timerValue;
update(); update();
}, },
confrimTitle: 'شروع زمان خواب',
dismissTitle: 'لغو', dismissTitle: 'لغو',
onDismissed: () { onDismissed: () {
state.timer?.cancel(); state.timer?.cancel();

View File

@ -1,5 +1,6 @@
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/requests/studio.dart';
import 'package:didvan/utils/date_time.dart'; import 'package:didvan/utils/date_time.dart';
@ -81,19 +82,30 @@ class PodcastOverview extends StatelessWidget {
DurationWidget(duration: podcast.duration!), DurationWidget(duration: podcast.duration!),
const Spacer(), const Spacer(),
if (!kIsWeb) ...[ if (!kIsWeb) ...[
DidvanIconButton( if (state.downloadState == AppState.idle ||
gestureSize: 28, !state.downloadQueue.contains(podcast.media))
color: _isDownloaded(state) DidvanIconButton(
? Theme.of(context).colorScheme.primary gestureSize: 28,
: null, color: _isDownloaded(state)
icon: _isDownloaded(state) ? Theme.of(context).colorScheme.primary
? DidvanIcons.download_solid : null,
: DidvanIcons.download_regular, icon: _isDownloaded(state)
onPressed: _isDownloaded(state) ? DidvanIcons.download_solid
? () {} : DidvanIcons.download_regular,
: () => onPressed: _isDownloaded(state)
state.download(podcast.media!, podcast.id.toString()), ? () {}
), : () => state.download(
podcast.media!, podcast.id.toString()),
),
if (state.downloadState == AppState.busy &&
state.downloadQueue.contains(podcast.media))
const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
const SizedBox(width: 16), const SizedBox(width: 16),
], ],
BookmarkButton( BookmarkButton(

View File

@ -14,6 +14,8 @@ import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart'; import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:just_audio/just_audio.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class DidvanBNB extends StatelessWidget { class DidvanBNB extends StatelessWidget {
@ -24,109 +26,169 @@ class DidvanBNB extends StatelessWidget {
{Key? key, required this.currentTabIndex, required this.onTabChanged}) {Key? key, required this.currentTabIndex, required this.onTabChanged})
: super(key: key); : super(key: key);
bool get _enablePlayerController => bool _enablePlayerController(StudioDetailsState state) =>
MediaService.currentPodcast != null && MediaService.currentPodcast != null ||
(MediaService.audioPlayerTag?.contains('podcast') ?? false); (MediaService.audioPlayerTag?.contains('podcast') ?? false);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.read<StudioDetailsState>();
return StreamBuilder<bool>( return StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream, stream: MediaService.audioPlayer.playingStream,
builder: (context, snapshot) { builder: (context, snapshot) {
return Stack( return Stack(
children: [ children: [
GestureDetector( GestureDetector(
onTap: () => _showPlayerBottomSheet(context), onTap: () => MediaService.currentPodcast == null
child: AnimatedContainer( ? null
padding: const EdgeInsets.only(top: 12), : _showPlayerBottomSheet(context),
duration: DesignConfig.lowAnimationDuration, child: Consumer<StudioDetailsState>(
height: _enablePlayerController ? 128 : 72, builder: (context, state, child) => AnimatedContainer(
decoration: BoxDecoration( padding: const EdgeInsets.only(top: 12),
color: DesignConfig.isDark duration: DesignConfig.lowAnimationDuration,
? Theme.of(context).colorScheme.focused height: _enablePlayerController(state) ? 128 : 72,
: Theme.of(context).colorScheme.navigation, decoration: BoxDecoration(
borderRadius: const BorderRadius.vertical( color: DesignConfig.isDark
top: Radius.circular(16), ? Theme.of(context).colorScheme.focused
: Theme.of(context).colorScheme.navigation,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(16),
),
), ),
), alignment: Alignment.topCenter,
child: !_enablePlayerController child: !_enablePlayerController(state)
? const SizedBox() ? const SizedBox()
: SizedBox( : MediaService.currentPodcast == null
height: 56, ? SizedBox(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
right: 12,
left: 16,
),
child: DidvanIconButton(
icon: DidvanIcons.close_regular,
color: DesignConfig.isDark
? null
: Theme.of(context).colorScheme.secondCTA,
gestureSize: 28,
onPressed: MediaService.resetAudioPlayer,
),
),
SkeletonImage(
imageUrl: MediaService.currentPodcast!.image,
width: 32,
height: 32, height: 32,
), child: Center(
const SizedBox(width: 16), child: SpinKitThreeBounce(
Expanded( size: 18,
child: Column( color: DesignConfig.isDark
? null
: Theme.of(context)
.colorScheme
.secondCTA,
),
),
)
: SizedBox(
height: 56,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
DidvanText( Padding(
MediaService.currentPodcast!.title, padding: const EdgeInsets.only(
color: DesignConfig.isDark right: 12,
? null left: 16,
: Theme.of(context) ),
.colorScheme child: DidvanIconButton(
.secondCTA, icon: DidvanIcons.close_regular,
color: DesignConfig.isDark
? null
: Theme.of(context)
.colorScheme
.secondCTA,
gestureSize: 28,
onPressed:
MediaService.resetAudioPlayer,
),
), ),
AudioSlider( SkeletonImage(
disableThumb: true, imageUrl:
tag: MediaService.audioPlayerTag!, MediaService.currentPodcast!.image,
width: 32,
height: 32,
), ),
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!,
),
],
),
),
StreamBuilder<PlayerState>(
stream: MediaService
.audioPlayer.playerStateStream,
builder: (context, snapshot) {
if (state.appState == AppState.busy ||
MediaService.audioPlayer.playerState
.processingState ==
ProcessingState.loading) {
return Padding(
padding: const EdgeInsets.only(
top: 4,
left: 16,
right: 16,
),
child: SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Theme.of(context)
.colorScheme
.secondCTA,
),
),
);
}
return const SizedBox();
},
),
if (state.appState != AppState.busy &&
MediaService.audioPlayer.playerState
.processingState !=
ProcessingState.loading)
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: state
.downloadedFileIds
.contains(state.studio.id)
? StorageService.appDocsDir +
'/podcasts/podcast-${state.studio.id}.mp3'
: state.studio.media,
isNetworkAudio: !state
.downloadedFileIds
.contains(state.studio.id),
id: state.studio.id,
isVoiceMessage: false,
);
},
),
),
], ],
), ),
), ),
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: state.downloadedFileIds
.contains(state.studio.id)
? StorageService.appDocsDir +
'/podcasts/podcast-${state.studio.id}.mp3'
: state.studio.media,
isNetworkAudio: !state.downloadedFileIds
.contains(state.studio.id),
id: state.studio.id,
isVoiceMessage: false,
);
},
),
),
],
),
),
), ),
), ),
Positioned( Positioned(
@ -226,18 +288,23 @@ class DidvanBNB extends StatelessWidget {
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: Column( child: Column(
children: [ children: [
DidvanIconButton( StatefulBuilder(
size: 32, builder: (context, setState) => DidvanIconButton(
icon: DidvanIcons.angle_down_regular, size: 32,
onPressed: () { icon: isExpanded
if (!isExpanded) { ? DidvanIcons.angle_down_regular
sheetKey.currentState?.expand(); : DidvanIcons.angle_up_regular,
isExpanded = true; onPressed: () {
return; if (!isExpanded) {
} sheetKey.currentState?.expand();
isExpanded = false; isExpanded = true;
sheetKey.currentState?.contract(); } else {
}, isExpanded = false;
sheetKey.currentState?.contract();
}
setState(() {});
},
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
], ],
@ -247,7 +314,13 @@ class DidvanBNB extends StatelessWidget {
), ),
), ),
expandableContent: state.appState == AppState.busy expandableContent: state.appState == AppState.busy
? const SizedBox() ? Container(
height: MediaQuery.of(context).size.height / 2,
alignment: Alignment.center,
child: SpinKitSpinningLines(
color: Theme.of(context).colorScheme.primary,
),
)
: StudioDetailsWidget( : StudioDetailsWidget(
studio: detailsState.studio, studio: detailsState.studio,
onCommentsTabSelected: () { onCommentsTabSelected: () {