I am trying to build a simple app that plays audio files in the asset folder.
I am using a Stream Builder to return a card for each sound to show pause and play icons. but strangely my app throws a null error when I add more than 10 sounds in the sound model.
here is my sound model class :
class CoughViewModel extends ChangeNotifier{
final List<SoundModel> _soundsList = [
SoundModel(soundIcon: 'assets/vectors/death_bed.svg', soundName: 'DEATH BED', soundPath: 'assets/sounds/cough_sounds/death_bed.mp3'),
];
List<SoundModel> get soundsList => _soundsList;
}
here is the code which I am using to display the sound cards :
Expanded(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: soundModel.length,
itemBuilder: (BuildContext context,int index){
return SoundCard(soundTitle: soundModel[index].soundName,soundPath: soundModel[index].soundPath,cardPicture: soundModel[index].soundIcon,);
}),
),
here is my stream builder :
return StreamBuilder<PlayerState>(
stream: player.playerStateStream,
builder: (context, snapshot) {
print("STATEOFPLAYER : ${player.playerStateStream}");
final playerState = snapshot.data;
if(snapshot.hasData){
return _cardReturner(playerState!);
}
else{
return const CircularProgressIndicator(
color: MyColors.color3,
);
}
},
);
here is my widget returner function :
Widget _cardReturner(PlayerState playerState) {
final processingState = playerState.processingState;
if (player.playing != true) {
return soundCard(isPlaying: false);
} else if (processingState != ProcessingState.completed) {
return soundCard(isPlaying: true);
} else {
return soundCard(isPlaying: false);
}
}
for some strange reason my player.playerStateStream's playerState gets null when number of sounds in model increase to 11
If I manually set the ListView.builder's itemCount to 10 the app runs with no problem. but as soon as the number exceeds I get an exception
furthermore here is my soundCard code
class SoundCard extends StatefulWidget {
final String? cardPicture;
final String soundTitle;
final String soundPath;
const SoundCard({Key? key,this.cardPicture,required this.soundTitle,required this.soundPath}) : super(key: key);
@override
_SoundCardState createState() => _SoundCardState();
}
class _SoundCardState extends State<SoundCard> {
final player = AudioPlayer();
final Icon playIcon = const Icon(
Icons.play_arrow,
color: MyColors.color3,
);
final Icon pauseIcon = const Icon(
Icons.pause,
color: MyColors.color3,
);
final String playString = 'TAP TO PLAY';
final String pauseString = 'TAP TO PAUSE';
@override
void dispose() {
player.dispose();
super.dispose();
}
@override
void initState() {
player.setAsset(widget.soundPath);
super.initState();
}
@override
Widget build(BuildContext context) {
ScreenUtil.setContext(context);
return StreamBuilder<PlayerState>(
stream: player.playerStateStream,
builder: (context, snapshot) {
print("STATEOFPLAYER : ${player.playerStateStream}");
final playerState = snapshot.data;
if(snapshot.hasData){
return _cardReturner(playerState!);
}
else{
return const CircularProgressIndicator(
color: MyColors.color3,
);
}
},
);
}
Widget _cardReturner(PlayerState playerState) {
final processingState = playerState.processingState;
if (player.playing != true) {
return soundCard(isPlaying: false);
} else if (processingState != ProcessingState.completed) {
return soundCard(isPlaying: true);
} else {
return soundCard(isPlaying: false);
}
}
Widget soundCard({required bool isPlaying}) {
return Container(
margin: EdgeInsets.only(left:25.w,right: 25.w,top: 10.h,bottom: 10.h),
decoration: BoxDecoration(
color: MyColors.color2,
borderRadius: BorderRadius.all(Radius.circular(10.r))),
child: GestureDetector(
onTap: () async {
if (isPlaying) {
player.stop();
} else {
player.setAsset(widget.soundPath);
player.play();
}
},
child: ListTile(
leading: CircleAvatar(
child: SvgPicture.asset(
widget.cardPicture??'assets/vectors/sound_icon_cough.svg',
height: 30.h,
),
backgroundColor: MyColors.color1,
),
title: Text(
widget.soundTitle,
style: MyTextStyles.bold15.copyWith(color: MyColors.color3),
),
subtitle: Text(
isPlaying ? pauseString : playString,
style: MyTextStyles.medium15.copyWith(color: MyColors.color1),
),
trailing: isPlaying ? pauseIcon : playIcon,
),
),
);
}
}
STACKTRACE
flutter: STATEOFPLAYER : Instance of 'BehaviorSubject<PlayerState>'
======== Exception caught by widgets library =======================================================
The following _CastError was thrown building StreamBuilder<PlayerState>(dirty, state: _StreamBuilderBaseState<PlayerState, AsyncSnapshot<PlayerState>>#075ca):
Null check operator used on a null value
The relevant error-causing widget was:
StreamBuilder<PlayerState> StreamBuilder:file:///Users/junaidtariq/Documents/Documents/AndroidStudioProjects/the_hoax_flu/lib/global_widgets.dart:141:12
When the exception was thrown, this was the stack:
#0 Element.widget (package:flutter/src/widgets/framework.dart:3203:31)
#1 debugCheckHasMediaQuery.<anonymous closure> (package:flutter/src/widgets/debug.dart:229:17)
#2 debugCheckHasMediaQuery (package:flutter/src/widgets/debug.dart:245:4)
#3 MediaQuery.of (package:flutter/src/widgets/media_query.dart:859:12)
#4 ScreenUtil.screenWidth (package:flutter_screenutil/screen_util.dart:69:51)
#5 ScreenUtil.scaleWidth (package:flutter_screenutil/screen_util.dart:88:28)
#6 ScreenUtil.setWidth (package:flutter_screenutil/screen_util.dart:101:41)
#7 SizeExtension.w (package:flutter_screenutil/size_extension.dart:5:32)
#8 _SoundCardState.soundCard (package:the_hoax_flu/global_widgets.dart:171:39)
#9 _SoundCardState._cardReturner (package:the_hoax_flu/global_widgets.dart:161:14)
#10 _SoundCardState.build.<anonymous closure> (package:the_hoax_flu/global_widgets.dart:147:18)
#11 StreamBuilder.build (package:flutter/src/widgets/async.dart:442:81)
#12 _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:124:48)
#13 StatefulElement.build (package:flutter/src/widgets/framework.dart:4870:27)
#14 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4754:15)
#15 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#16 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#17 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#18 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#19 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#20 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#21 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#22 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995:5)
#26 _invoke (dart:ui/hooks.dart:151:10)
#27 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#28 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
====================================================================================================
I am on a deadline as I have to deliver this app to a client. any help would be highly appreciated thankyou
CodePudding user response:
You access the .w
extension when calling 25.w
. This only works when the context inside ScreenUtil
is up to date, it doesn't work with an outdated context (from the last build).
The build
callback of StreamBuilder
updates independently of your Widgets build
function.
The StreamBuilder
build function is called way more often. Therefore, you have to call ScreenUtil.setContext(context);
again to update the BuildContext.
@override
Widget build(BuildContext context) {
ScreenUtil.setContext(context);
return StreamBuilder<PlayerState>(
stream: player.playerStateStream,
builder: (context, snapshot) {
// Update context
ScreenUtil.setContext(context);
print("STATEOFPLAYER : ${player.playerStateStream}");
final playerState = snapshot.data;
if (snapshot.hasData){
return _cardReturner(playerState!);
} else {
return const CircularProgressIndicator(
color: MyColors.color3,
);
}
},
);
}