Home > Blockchain >  Flutter just_audio package throws null exception if I show more than 10 sounds in my ListView
Flutter just_audio package throws null exception if I show more than 10 sounds in my ListView

Time:02-16

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

MANUAL ITEM COUNT 9

SOUNDMODEL.LENGTH ITEM COUNT

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,
          );
        }
      },
    );
  }
  • Related