颤动:在子窗口小部件的状态上调用函数

时间:2018-05-28 00:51:22

标签: flutter

我创建了一个有状态小部件,其父小部件需要调用一个生活在子级状态的函数。

具体来说,我有一个PlayerContainer类,它创建一个VideoPlayer并具有VideoPlayerController的成员变量。当我按下播放按钮时,我的主类需要在状态的VideoPlayerController上调用play(),所以我在State类中创建了一个函数,但是我不知道如何从中调用该函数。父小部件。

这甚至可能吗?或者我是不是错了?

4 个答案:

答案 0 :(得分:9)

我知道我参加聚会很晚,但是我认为有一些帮助。因此,您需要在VideoPlayerController类中做四(4)件事:
1.创建您的状态类的实例。
2.创建一个可以在您的PlayerContainer类中访问的方法(播放)
3.在您的方法中,使用VideoPlayerControllerState实例在您的状态类中调用该方法。
4.最后,当您createState时,请使用已经创建的实例进行操作。

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

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

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

如您所见,play方法使用vpcs(VideoPlayerControllerState实例)来调用状态类中已经存在的play方法。

在PlayerContainer类中,使用您的成员变量来调用play方法。

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

您可以通过播放按钮的_handlePressPlay()方法调用onPressed。或者,只需将_vpc.play()放在onPressed方法中。您的选择:-)。

答案 1 :(得分:4)

尽管达伦·科尔(Darren Cole)的answer above无法正常工作,但是有一种简单的方法可以解决当前的问题。代替

State<StatefulWidget> createState() => vpcs;

final VideoPlayerControllerState vpcs = VideoPlayerControllerState();

写:

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

VideoPlayerControllerState vpcs;

这样,每次调用createState()时都会重写状态,从而避免了Failed assertion: line 3819 pos 12: '_state._widget == null': is not true错误。

还是,我想这是一个有点棘手的解决方案-有人可以指出更好的解决方案吗?

答案 2 :(得分:2)

对于这个框架,国家管理是他们放弃的一件事。我已经使用静态变量(如果我需要在状态之间共享数据)和GlobalKeys(仅用于一个快速,脏的解决方案)来完成这项工作。我们假设使用InheritedWidgets,它只是非常偏僻,应该是一件非常简单的事情。我通常只是这样做:

// 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> {
    ...
}

我们需要让videoPlayerKey的currentState使用setState()并重新运行构建方法,以便它知道更新,然后我们可以抓住播放器的控制器,无论它在哪里使用静态变量存储。它可能在VideoPlayer中或VideoPlayerContainer中不在其他任何地方,因为它是静态的 - 将GlobalKey分配给需要重建的Widget是非常重要的。它将起作用,因为只要用户可以点击按钮,就会为VideoPlayerContainer的build()方法读取的任何void设置静态变量。对于这种方法,重要的是要注意将GlobalKey附加到需要更新的元素更重要 - 您可以将静态pageController放在任何地方,并从构建中的任何位置设置它()或initState()。

注意:如果您尝试在一个布局中使用多个VideoPlayers,则无效。因为GlobalKeys必须是唯一的,并且所有VideoPlayers都将使用相同的键进行初始化。这比任何事都更糟糕。我正在研究一种更强大的状态管理解决方案,以解决这样的问题。

答案 3 :(得分:0)

使用简单的应用程序,您只需在与VideoPlayer相同的Widget中创建播放按钮。通过将PlayerContainer与其父级组合,您将增加Widget State范围的大小,以便需要访问它的所有内容都是单个较大Widget的一部分。

子Widget受祖先影响的主要方式是:通过使用不同的参数重建,或者通过听取祖先改变的内容。对于后者,您可以在孩子的某处使用InheritedWidget。如果子进程引用InheritedWidget,它将在IW更改时重建。另一种方法是监听祖先生成的事件流。

您可能会发现在单个构建中构建整个页面最简单,直到变得难以处理。