How to pass function from a child class to parent class, I am getting this error: setState() or markNeedsBuild() called during build

0

Here data(_data =video/ images links, _id= v or i) is coming from an api. Here, i made a slideshow of videos and images with a fade in fade out effect, images have a certain time period to display but videos have different lengths so I want to stop the animation till the video has ended, using timer doesn't work as the loading time takes longer and video changes before ending. Here I tried to send a callback from a child to parent but I am getting this error.

class LogoApp extends StatefulWidget {

  LogoApp(this.data, this.id, this.seconds, this.length);

  final int length;
  final List<String> id;
  final List<String> data;
  final List<int> seconds;
  _LogoAppState createState() => _LogoAppState(data, id, seconds, length);
}

class _LogoAppState extends State<LogoApp> with TickerProviderStateMixin {
  _LogoAppState(this._data, this._id, this._seconds, this._length);

  static int i = 0;
  int _length;
  List<String> _id;
  List<String> _data;
  List<int> _seconds;
  AnimationController controller;
  Animation<double> animation;
  Timer timer;
  Future<dynamic> cachedData;
  List<Map<String, dynamic>> listOfCache = [];
  bool state = false;
  File compressedImage;
  bool videoEnded = false;

  _updateVideoState(bool state) {
    setState(() {
      videoEnded = state;
    });
  }

  int miliSec(int sec) {
    if (sec == 0)
      return 10000;
    else
      return sec * 1000;
  }

  initState() {
    super.initState();
    this.cache(0);
  }

  Future<dynamic> downloadFile(String url) async {
    var fetchedFile = await DefaultCacheManager().getSingleFile(url);
    return fetchedFile;
  }

   void cache(int index) async {
    if (index == _data.length) {
      animate();
      setState(() {
        state = true;
      });

      return;
    }
    var cachedData = await DefaultCacheManager().getSingleFile(_data[index]);
    if (_id[index] == 'i') {
      compressedImage = await FlutterNativeImage.compressImage(
        cachedData.path,
        quality: 75,
        percentage: 100,
      );
      cachedData = compressedImage;
    }

    listOfCache.add({"data": cachedData, "type": _id[index]});
    this.cache(index + 1);
  }

  void animate() {
    controller = AnimationController(
        duration: Duration(milliseconds: 1000), vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.ease);

    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        if (_id[i] == "v") {
          while (true) {
            if (videoEnded) {
              break;
            }
          }
        } else {
          timer = Timer(
            Duration(milliseconds: 10000),
            () {
              controller.reverse();
            },
          );
        }
      } else if (status == AnimationStatus.dismissed) {
        setState(() {
          i++;
          if (i == _length) {
            i = 0;
          }
        });
        controller.forward();
      }
    });
    controller.forward();
  }

  Widget initVideo() {
    return ChewieListItem(
      parentAction: _updateVideoState,
      videoPlayerController: VideoPlayerController.file(listOfCache[i]["data"]),
    );
  }

  Widget build(BuildContext context) {
    if (state == true) {
      return Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        alignment: Alignment.bottomCenter,
        color: Colors.black,
        child: FadeTransition(
          opacity: animation,
          child: (listOfCache[i]["type"] == "i")
              ? Image.file(
                  listOfCache[i]["data"],
                  fit: BoxFit.fill,
                )
              : initVideo(),
        ),
      );
    } else {
      return Center(child: CircularProgressIndicator());
    }
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }
}

class ChewieListItem extends StatefulWidget {
  final ValueChanged<bool> parentAction;
  final VideoPlayerController videoPlayerController;

  ChewieListItem({
    @required this.parentAction,
    this.videoPlayerController,
    Key key,
  }) : super(key: key);

  @override
  _ChewieListItemState createState() => _ChewieListItemState();
}

class _ChewieListItemState extends State<ChewieListItem> {
  ChewieController _chewieController;

  @override
  void initState() {
    super.initState();
    widget.videoPlayerController.addListener(checkVideo);
    _chewieController = ChewieController(
      videoPlayerController: widget.videoPlayerController,
      autoInitialize: true,
      autoPlay: true,
      //looping: true,
      showControls: false,
      errorBuilder: (context, errorMessage) {
        return Center(
          child: Text(
            errorMessage,
            style: TextStyle(color: Colors.white),
          ),
        );
      },
    );
  }

  void checkVideo() {
    if (widget.videoPlayerController.value.position ==
        widget.videoPlayerController.value.duration) {
      widget.parentAction(true);
    } else
      widget.parentAction(false);
  }

  @override
  Widget build(BuildContext context) {
    return Chewie(
      controller: _chewieController,
    );
  }

  @override
  void dispose() {
    super.dispose();
    _chewieController.dispose();
  }
}

Error:

Launching lib\main.dart on D6633 in debug mode...
Built build\app\outputs\apk\debug\app-debug.apk.

�[38;5;248m════════ Exception caught by foundation library ════════════════════════════════�[39;49m
�[38;5;244mThe following assertion was thrown while dispatching notifications for VideoPlayerController:�[39;49m
setState() or markNeedsBuild() called during build.

�[38;5;244mThis LogoApp widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.�[39;49m
�[38;5;244mThe widget on which setState() or markNeedsBuild() was called was: LogoApp�[39;49m
    �[38;5;244mdependencies: [MediaQuery]�[39;49m
    �[38;5;244mstate: _LogoAppState#68420(tickers: tracking 1 ticker)�[39;49m
�[38;5;244mThe widget which was currently being built when the offending call was made was: Container�[39;49m
    �[38;5;244mbottomCenter�[39;49m
    �[38;5;244mbg: BoxDecoration(color: Color(0xff000000))�[39;49m
    �[38;5;244mconstraints: BoxConstraints(w=592.0, h=360.0)�[39;49m
�[38;5;244mWhen the exception was thrown, this was the stack�[39;49m
�[38;5;244m#0      Element.markNeedsBuild.<anonymous closure>�[39;49m
�[38;5;244m#1      Element.markNeedsBuild�[39;49m
�[38;5;244m#2      State.setState�[39;49m
�[38;5;248m#3      _LogoAppState._updateVideoState�[39;49m
�[38;5;248m#4      _ChewieListItemState.checkVideo�[39;49m
�[38;5;244m...�[39;49m
�[38;5;244mThe VideoPlayerController sending notification was: VideoPlayerController#09533(VideoPlayerValue(duration: null, size: null, position: 0:00:00.000000, buffered: [], isPlaying: false, isLooping: false, isBuffering: falsevolume: 1.0, errorDescription: null))�[39;49m
�[38;5;248m════════════════════════════════════════════════════════════════════════════════�[39;49m
I/ExoPlayerImpl( 1537): Init ee97f31 [ExoPlayerLib/2.9.6] [D6633, D6633, Sony, 23]
W/VideoCapabilities( 1537): Unrecognized profile 2130706433 for video/avc
W/AudioCapabilities( 1537): Unsupported mime audio/alac
W/AudioCapabilities( 1537): Unsupported mime audio/dsd
W/VideoCapabilities( 1537): Unsupported mime video/divx
W/VideoCapabilities( 1537): Unsupported mime video/divx311
W/VideoCapabilities( 1537): Unsupported mime video/divx4
W/VideoCapabilities( 1537): Unsupported mime video/mp4v-esdp
I/VideoCapabilities( 1537): Unsupported profile 4 for video/mp4v-es
I/OMXClient( 1537): Using client-side OMX mux.
I/MediaCodec( 1537): [OMX.qcom.video.decoder.avc] setting surface generation to 1573889
D/MediaOutputController( 1537): attach(0x0, 0x9e443180, 0xb5ce7701)
D/MediaCodec( 1537): MediaCodec callback for event 0
D/MediaCodec( 1537): onMediaOutputControllEvent(VIDEO_FLAGS_SET, 0x8ec4755c, 4)
D/MediaCodec( 1537): mVideoOutputFlags = 0x00000000
D/MediaOutputController( 1537): onConnect(): 0x00010003
D/MediaOutputController( 1537): setAudioDevicesState(0x00010003, 1)
D/MediaOutputPolicy( 1537): notifyAudioStateIfNeeded(0x00010003)
D/MediaOutputController( 1537): Registered as audio policy client
I/ExtendedACodec( 1537): setupVideoDecoder()
I/ExtendedACodec( 1537): vpp-enable search is 0 and value is 0
I/ExtendedACodec( 1537): Decoder will be in frame by frame mode
D/ACodec  ( 1537): Found video-output-protection flags set to 00000000
D/SurfaceUtils( 1537): set up nativeWindow 0x980b4a08 for 1280x720, color 0x7fa30c04, rotation 0, usage 0x42002900
I/OMXClient( 1537): Using client-side OMX mux.
D/SurfaceUtils( 1537): set up nativeWindow 0x980b4a08 for 1280x720, color 0x7fa30c04, rotation 0, usage 0x42002900
E/OMXMaster( 1537): A component of name 'OMX.qcom.audio.decoder.aac' already exists, ignoring this one.
D/MediaOutputController( 1537): attach(0x0, 0x9e4432c0, 0xb5ce7701)
D/MediaCodec( 1537): MediaCodec callback for event 0
D/MediaCodec( 1537): onMediaOutputControllEvent(VIDEO_FLAGS_SET, 0x895e355c, 4)
D/MediaCodec( 1537): mVideoOutputFlags = 0x00000000
D/MediaOutputPolicy( 1537): notifyAudioStateIfNeeded(0x00010003)
E/OMXNodeInstance( 1537): setConfig(1:google.aac.decoder, ConfigPriority(0x6f800002)) ERROR: Undefined(0x80001001)
I/ACodec  ( 1537): codec does not support config priority (err -2147483648)
E/OMXNodeInstance( 1537): setConfig(1:google.aac.decoder, ConfigPriority(0x6f800002)) ERROR: Undefined(0x80001001)
I/ACodec  ( 1537): codec does not support config priority (err -2147483648)
I/SoftAAC2( 1537): Reconfiguring decoder: 0->44100 Hz, 0->2 channels
W/ExtendedACodec( 1537): Failed to get Profile and Level from Component
W/ExtendedACodec( 1537): Failed to get Entropy from Component
android
video
flutter
dart
video-player

1 Answer

0

It Helps me

if(mounted) {
  setState(() {
      videoEnded = state;
  });
} 
answered on Stack Overflow Nov 28, 2019 by Jay Gadariya

User contributions licensed under CC BY-SA 3.0