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

Mik*_*ler 6 flutter

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

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

这甚至可能吗?或者我这样做是错的?

Dar*_*ole 13

我知道我参加派对的时间已经很晚了,但我觉得我认为可能有所帮助.所以,你需要在你的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;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,该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,
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以_handlePressPlay()通过onPressed播放按钮的方法拨打电话.或者,只需_vpc.play()输入onPressed方法即可.你的选择 :-).

  • 'package:flutter/src/widgets/framework.dart':断言失败:第 3819 行 pos 12:'_state._widget == null':不正确。 (5认同)

rgi*_*isi 7

虽然达伦·科尔的上述答案无法正常工作,但有一种简单的方法可以避免眼前的问题。代替

State<StatefulWidget> createState() => vpcs;

final VideoPlayerControllerState vpcs = VideoPlayerControllerState();
Run Code Online (Sandbox Code Playgroud)

写:

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

VideoPlayerControllerState vpcs;

Run Code Online (Sandbox Code Playgroud)

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

不过,我想这是一个有点黑客的解决方案 - 有人能指出更好的解决方案吗?

  • 旁注:这将生成一个“must_be_immutable”警告,但我相信其后果比最终的“State”实例要温和得多。 (2认同)

C. *_*wis 5

状态管理是他们在这个框架方面放弃的一件事。我使用了静态变量(如果我需要在状态之间共享数据)和 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> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

我们需要获取 videoPlayerKey 的 currentState 以使用 setState() 并重新运行 build 方法,以便它知道 update,然后我们可以使用静态变量获取存储在任何地方的播放器的控制器。它可以在 VideoPlayer 或 VideoPlayerContainer 中不存在的任何其他地方,因为它是静态的 - 将 GlobalKey 分配给需要重建的任何 Widget 是很重要的。它会起作用,因为每当用户可以点击按钮时,静态变量都会被设置为任何 void 以通过 VideoPlayerContainer 的 build() 方法读取。对于这种方法,重要的是要注意它'

注意:如果您尝试在一个布局中使用多个 VideoPlayer,这将不起作用,因为 GlobalKeys 必须是唯一的,并且所有 VideoPlayer 将使用相同的键进行初始化。这更像是一个肮脏的黑客。我目前正在研究一个更强大的状态管理解决方案来解决这样的问题。


Ric*_*eap 1

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

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

您可能会发现最简单的方法是在一次构建中构建整个页面,直到这变得难以处理。