Daibaku
Daibaku

Reputation: 12576

Flutter VideoPlayerController "setState() or markNeedsBuild() called during build."

I'm playing video and there is no problem but when navigating to another page, console says this.
I tried everything that I came up with like check if mounted but none of them worked.
What am I missing? Does anyone know how to fix?

flutter: The following assertion was thrown while dispatching notifications for VideoPlayerController:
flutter: setState() or markNeedsBuild() called during build.
flutter: This VideoProgressIndicator widget cannot be marked as needing to build because the framework is
flutter: already in the process of building widgets. A widget can be marked as needing to be built during the
flutter: build phase only if one of its ancestors is currently building. This exception is allowed because
flutter: the framework builds parent widgets before children, which means a dirty descendant will always be
flutter: built. Otherwise, the framework might not visit this widget during this build phase.
flutter: The widget on which setState() or markNeedsBuild() was called was:
flutter: VideoProgressIndicator(state: _VideoProgressIndicatorState#09ac7)

Code:

class _VideoScreenState extends State<VideoScreen> {
      VideoPlayerController _controller;
      FadeAnimation imageFadeAnim =
          new FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0));
      VoidCallback listener;
      bool _isPlaying = false;
      List<String> videos = [
        'http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4',
      ];

      _VideoScreenState() {
        listener = () {
          if (mounted) setState(() {});
        };
      }

      Future _videoOnTap() async {
        if (!_controller.value.initialized) {
          return;
        }
        if (_controller.value.isPlaying) {
          imageFadeAnim = new FadeAnimation(
              child: Icon(Icons.pause,
                  color: Colors.white.withOpacity(0.3), size: 75.0));
          await _controller.pause();
        } else {
          imageFadeAnim = new FadeAnimation(
              child: Icon(Icons.play_arrow,
                  color: Colors.white.withOpacity(0.3), size: 75.0));
          await _controller.play();
        }
      }

      Future _startVideoPlayer(int index) async {
        if (_controller != null) {
          await _controller.dispose();
        }
        _controller = VideoPlayerController.network(videos[index])
          ..addListener(() {
            final bool isPlaying = _controller.value.isPlaying;
            if (isPlaying != _isPlaying) {
              if (mounted)
                setState(() {
                  _isPlaying = isPlaying;
                });
            }
          })
          ..initialize().then((_) async {
            await _controller.play();
            if (mounted) setState(() {});
          });
      }

      @override
      void initState() {
        SystemChrome.setEnabledSystemUIOverlays([]);
        _startVideoPlayer(0);
        super.initState();
      }

      @override
      void deactivate() {
        _controller?.setVolume(0.0);
        _controller?.removeListener(listener);
        super.deactivate();
      }

      @override
      void dispose() {
        _controller?.dispose();
        super.dispose();
      }

      @override
      Widget build(BuildContext context) {
         final List<Widget> children = <Widget>[
      new GestureDetector(
        child: new Center(
          child: _controller.value.initialized
              ? new AspectRatio(
                  aspectRatio: _controller.value.aspectRatio,
                  child: new VideoPlayer(_controller))
              : new Container(),
        ),
        onTap: _videoOnTap,
      ),
      new Align(
        alignment: Alignment.topCenter,
        child: new VideoProgressIndicator(
          _controller,
          allowScrubbing: false,
          colors: VideoProgressColors(
              backgroundColor: Colors.black.withOpacity(0.5),
              playedColor: Colors.white),
        ),
      ),
      new Center(child: imageFadeAnim),
    ];

        return new Scaffold(
          backgroundColor: Colors.black,
          body: new SafeArea(
            top: true,
            left: false,
            right: false,
            bottom: false,
            child: new Stack(
              fit: StackFit.passthrough,
              children: children,
            ),
          ),
        );
      }
    }

    class FadeAnimation extends StatefulWidget {
      final Widget child;
      final Duration duration;

      FadeAnimation({this.child, this.duration: const Duration(milliseconds: 300)});

      @override
      _FadeAnimationState createState() => new _FadeAnimationState();
    }

    class _FadeAnimationState extends State<FadeAnimation>
        with SingleTickerProviderStateMixin {
      AnimationController animationController;

      @override
      void initState() {
        super.initState();
        animationController =
            new AnimationController(duration: widget.duration, vsync: this);
        animationController.addListener(() {
          if (mounted) {
            setState(() {});
          }
        });
        animationController.forward(from: 0.0);
      }

      @override
      void deactivate() {
        animationController.stop();
        super.deactivate();
      }

      @override
      void didUpdateWidget(FadeAnimation oldWidget) {
        super.didUpdateWidget(oldWidget);
        if (oldWidget.child != widget.child) {
          animationController.forward(from: 0.0);
        }
      }

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

      @override
      Widget build(BuildContext context) {
        return animationController.isAnimating
            ? new Container(
                width: 75.0,
                height: 75.0,
                decoration: new BoxDecoration(
                  color: Colors.black.withOpacity(0.01),
                  shape: BoxShape.circle,
                ),
                child: widget.child,
              )
            : new Container();
      }
    }

Upvotes: 12

Views: 2407

Answers (4)

Miro
Miro

Reputation: 449

The error seState() called during build means that you have called setState before the widget has successfully built. In Flutter, before the widget is build, the initState() method is called. So, if you have a setState inside the init method that is being called, you will get this error.

Note: setState() is NOT needed inside initState, because the widget will get the new inherited value upon build, since we first run the whole init state, and then we build.

Second Note: You cannot use await in initState.

Third Note: If you are fetching data from somewhere or if there is a process that needs awaiting in the init state, you should make it a separate method, make it async but call it as unawaited in the initState, and call setState (inside the method) to update the UI once the fetch is done.

Upvotes: 1

Gupta Akshay
Gupta Akshay

Reputation: 72

Have you tried this

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

Upvotes: 0

Chris Wickens
Chris Wickens

Reputation: 490

Seems to me the OP needs to include the code to VideoProgressIndicator to debug their particular issue, but generally when facing this error you'll want to use WidgetsBinding.instance.addPostFrameCallback to prevent Navigator methods or setState from being invoked during the build phase, e.g.

WidgetsBinding.instance.addPostFrameCallback(
        (_) => setState(() => _isPlaying = !_isPlaying),
      );

Upvotes: 0

Nabin Dhakal
Nabin Dhakal

Reputation: 2202

Initialize your animationController in initState and add listener to didchangeDependencies

 @override
      void initState() {
        super.initState();
        animationController =
            new AnimationController(duration: widget.duration, vsync: this);
        
      }

@override
void didChangeDependencies(
{
super.didChangeDependencies();
animationController.addListener(() {
          if (mounted) {
            setState(() {});
          }
        });
        animationController.forward(from: 0.0);
})

Upvotes: 0

Related Questions