我创建了一个有状态小部件,其父小部件需要调用一个生活在子级状态的函数。
具体来说,我有一个PlayerContainer类,它创建一个VideoPlayer并具有VideoPlayerController的成员变量。当我按下播放按钮时,我的主类需要在状态的VideoPlayerController上调用play(),所以我在State类中创建了一个函数,但是我不知道如何从中调用该函数。父小部件。
这甚至可能吗?或者我是不是错了?
答案 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更改时重建。另一种方法是监听祖先生成的事件流。
您可能会发现在单个构建中构建整个页面最简单,直到变得难以处理。