Flutter: Call a function on a child widget's state

I know that I'm pretty late to the party, but I have something that I think might help. So, you need to do four (4) things in your VideoPlayerController class:
1. Create an instance of your state class.
2. Create a method (play) which will be accessible in your PlayerContainer class
3. In your method, use the VideoPlayerControllerState instance to call the method in your state class.
4. Finally, when you createState, do so using the instance that you already created.

class VideoPlayerController extends StatefulWidget {
  final VideoPlayerControllerState vpcs = VideoPlayerControllerState();

  void play() {
    vpcs.play();
  }

  @override
  State<StatefulWidget> createState() => vpcs;
}

As you see, the play method uses vpcs (the VideoPlayerControllerState instance) to call the play method already in your state class.

In your PlayerContainer class, use your member variable to call the play method.

class PlayerContainerState extends State<PlayerContainer> {
  VideoPlayerController _vpc;

  @override
  void initState() {
    super.initState();
    _vpc = VideoPlayerController();
  }
  ...

  void _handlePressPlay(){
    _vpc.play();
  } 
  ...

  @override
  Widget build(BuildContext context) {
    return ... //your video player widget using _vpc as your VideoPlayerController
      _vpc,
    );
  }
}

You can call _handlePressPlay() from the onPressed method of your play button. Alternatively, just put _vpc.play() in the onPressed method. Your choice :-).


State management is the one thing they dropped the ball on with respect to this framework. I've used static variables (if I need to share data between states) and GlobalKeys (for just one quick, dirty solution) to get this done. We're supposed to use InheritedWidgets, it's just extremely out-of-the-way for something that should be simple. I usually just do this:

// top of code here - this is global
final videoPlayerKey = GlobalKey();

class VideoPlayerContainer extends StatelessWidget {
    static VideoPlayerController videoPlayerController;
    ...
    @override
    Widget build(BuildContext context) {
        videoPlayerController = VideoPlayerController(...);
        // the static variable is empty until the container is built

        return Container(
            child: VideoPlayer(
                child: PlayButton(onTap: () => 
                    videoPlayerKey.currentState.setState(
                    () => VideoPlayerContainer.videoPlayerController.play();
                ))
            ),
        ); 
    }
}

class VideoPlayer extends StatefulWidget {
final Key key = videoPlayerKey;
    ...
}

class VideoPlayerState extends State<VideoPlayer> {
    ...
}

We need to get videoPlayerKey's currentState to use setState() and re-run the build method so it knows to update, and then we can grab the controller for the player wherever it is stored using the static variable. It could be in VideoPlayer or anywhere else that's not here in VideoPlayerContainer, because it's static - it's just important that you assign the GlobalKey to whatever Widget will need to be rebuilt. It will work because whenever a user could tap the button, the static variable will have been set for any void to read by VideoPlayerContainer's build() method. For this method, it's important to note that it's more important that you attach the GlobalKey to the element that needs to be updated - you can put the static pageController literally wherever and set it from wherever within a build() or initState().

Notes: This will not work if you try to use multiple VideoPlayers in one layout, because GlobalKeys must be unique, and all VideoPlayers will be initialized with the same key. This is more of a dirty hack than anything. I am working on a more robust state management solution at the moment to solve stuff like this.


While Darren Cole's answer above does not work correctly, there is an easy way to circumvent the immediate problem. Instead of

State<StatefulWidget> createState() => vpcs;

final VideoPlayerControllerState vpcs = VideoPlayerControllerState();

write:

State<StatefulWidget> createState(){
  vpcs = VideoPlayerControllerState();
  return vpcs;
}

VideoPlayerControllerState vpcs;

This way, the state gets re-written everytime createState() is called, which avoids the Failed assertion: line 3819 pos 12: '_state._widget == null': is not true errors.

Still, I guess this is a somewhat hackish solution - can somebody point out a better one?

Tags:

Flutter