颤振中的 ChewiePlayer 在进入全屏模式后处理控制器
ChewiePlayer in flutter disposes the controller after going to full screen mode
我在一个有状态小部件中有一个 ChewiePlayer,它正在另一个有状态小部件中使用。当我进入全屏模式时,会调用 dispose 函数,这基本上会删除侦听器,我无法退出全屏模式。
我也收到此错误:
NoSuchMethodError: The method 'dispose' was called on null. Receiver: null Tried calling: dispose()
以及
A ChewiePlayerController was used after being disposed. Once you have called dispose() on a ChewiePlayerController...
似乎是一个常见问题,尝试了几乎所有解决方案,但似乎没有任何效果。
这是我的 flutter doctor 输出:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.22.6, on Mac OS X 10.15.7 19H1217 darwin-x64,
locale en-GB)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 12.1)
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.57.0)
[✓] Connected device (1 available)
• No issues found!
这是我的有状态小部件中的代码:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html/style.dart' as HtmlStyle;
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:ria/src/models/game_pebble.dart';
import 'package:ria/src/models/trail.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
class VideoOutputMineral extends StatefulWidget {
@override
VideoOutputMineralState createState() => VideoOutputMineralState();
final Mineral mineral;
final List<GameStyle> styles;
final Function onChangeAnswer;
VideoOutputMineral(this.mineral, this.styles, this.onChangeAnswer, {Key key})
: super(key: key);
}
class VideoOutputMineralState extends State<VideoOutputMineral>
with TickerProviderStateMixin {
VideoPlayerController videoPlayerController;
ChewieController chewieController;
Color hexToColor(String code) {
return new Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
}
Color getFontColor() {
if (widget.styles != null &&
widget.styles.first != null &&
widget.styles.first.attributes.fontColor != null) {
return hexToColor(widget.styles.first.attributes.fontColor);
} else {
return Colors.black;
}
}
Future<void> initializeVideoPlayer() async {
// the async function which is responsible to get the trail detail, then initialize the video player controller, once done rebuild the widget with new data.
if (widget.mineral.properties.first.media.url != null &&
widget.mineral.properties.first.media.url != "") {
videoPlayerController = VideoPlayerController.network(
widget.mineral.properties.first.media.url);
await videoPlayerController.initialize();
chewieController = ChewieController(
videoPlayerController: videoPlayerController,
looping: true,
aspectRatio: 16 / 9,
deviceOrientationsAfterFullScreen: [
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown
],
allowFullScreen: true,
fullScreenByDefault: false,
autoInitialize: true,
errorBuilder: (context, errorMessage) {
return Center(
child: Text(
errorMessage,
style: TextStyle(color: Colors.white),
),
);
},
);
}
setState(() {});
}
@override
void initState() {
this.initializeVideoPlayer();
super.initState();
}
@override
void dispose() {
super.dispose();
if (chewieController != null && chewieController.isFullScreen) {
chewieController?.dispose();
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 30, right: 30),
child: Column(
children: [
widget.mineral.properties.first.title != null &&
widget.mineral.properties.first.title != ""
? Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Linkify(
options: LinkifyOptions(
defaultToHttps: true, looseUrl: true),
onOpen: (link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
},
text:
"${widget.mineral.properties.first.title}${widget.mineral.properties?.first?.rulesInput?.rulesInputRequired == 'required' ? ' *' : ''}",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
linkStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
textAlign: TextAlign.center,
),
),
],
),
SizedBox(
height: 12,
),
],
)
: Container(),
widget.mineral.properties.first.description != null &&
widget.mineral.properties.first.description != ""
? Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Linkify(
options: LinkifyOptions(
defaultToHttps: true, looseUrl: true),
onOpen: (link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
},
text:
"${widget.mineral.properties.first.description}",
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
linkStyle: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
textAlign: TextAlign.center,
),
),
],
),
SizedBox(
height: 12,
),
],
)
: Container(),
ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: RotatedBox(
quarterTurns: 0,
child: Container(
height: 200,
child: chewieController != null &&
chewieController.videoPlayerController.value.initialized
? Chewie(
controller: chewieController,
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SpinKitFadingCircle(
color: Colors.black,
),
],
),
),
),
),
widget.mineral.properties.first.caption != null &&
widget.mineral.properties.first.caption != ""
? Column(
children: [
SizedBox(
height: 12,
),
Text(
widget.mineral.properties.first.caption,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: getFontColor(),
),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
maxLines: 2,
),
],
)
: Container(),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: widget.mineral.properties.first.content != null
? Column(
children: [
Html(
data: widget.mineral.properties.first.content,
style: {
"html": HtmlStyle.Style(
fontSize: HtmlStyle.FontSize.large,
color: getFontColor()),
"p": HtmlStyle.Style(
lineHeight: 1,
display: HtmlStyle.Display.BLOCK,
margin: EdgeInsets.fromLTRB(0, 0, 0, 0),
),
}),
],
)
: Container(),
)
],
),
);
}
}
提前致谢!
终于在经过大约一周的努力后发现了问题,问题是因为在父小部件的 AppBar 中我使用了 PreferedSize 并为其内部的元素提供了一些填充,因为播放器是在它上面渲染的并且因为 appBar 它无法进入全屏,所以它会处理控制器。
解决方法是检查子窗口小部件内部是否有 VideoPlayer,忽略填充,效果很好。
我在一个有状态小部件中有一个 ChewiePlayer,它正在另一个有状态小部件中使用。当我进入全屏模式时,会调用 dispose 函数,这基本上会删除侦听器,我无法退出全屏模式。 我也收到此错误:
NoSuchMethodError: The method 'dispose' was called on null. Receiver: null Tried calling: dispose()
以及
A ChewiePlayerController was used after being disposed. Once you have called dispose() on a ChewiePlayerController...
似乎是一个常见问题,尝试了几乎所有解决方案,但似乎没有任何效果。
这是我的 flutter doctor 输出:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.22.6, on Mac OS X 10.15.7 19H1217 darwin-x64,
locale en-GB)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 12.1)
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.57.0)
[✓] Connected device (1 available)
• No issues found!
这是我的有状态小部件中的代码:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html/style.dart' as HtmlStyle;
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:ria/src/models/game_pebble.dart';
import 'package:ria/src/models/trail.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
class VideoOutputMineral extends StatefulWidget {
@override
VideoOutputMineralState createState() => VideoOutputMineralState();
final Mineral mineral;
final List<GameStyle> styles;
final Function onChangeAnswer;
VideoOutputMineral(this.mineral, this.styles, this.onChangeAnswer, {Key key})
: super(key: key);
}
class VideoOutputMineralState extends State<VideoOutputMineral>
with TickerProviderStateMixin {
VideoPlayerController videoPlayerController;
ChewieController chewieController;
Color hexToColor(String code) {
return new Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
}
Color getFontColor() {
if (widget.styles != null &&
widget.styles.first != null &&
widget.styles.first.attributes.fontColor != null) {
return hexToColor(widget.styles.first.attributes.fontColor);
} else {
return Colors.black;
}
}
Future<void> initializeVideoPlayer() async {
// the async function which is responsible to get the trail detail, then initialize the video player controller, once done rebuild the widget with new data.
if (widget.mineral.properties.first.media.url != null &&
widget.mineral.properties.first.media.url != "") {
videoPlayerController = VideoPlayerController.network(
widget.mineral.properties.first.media.url);
await videoPlayerController.initialize();
chewieController = ChewieController(
videoPlayerController: videoPlayerController,
looping: true,
aspectRatio: 16 / 9,
deviceOrientationsAfterFullScreen: [
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown
],
allowFullScreen: true,
fullScreenByDefault: false,
autoInitialize: true,
errorBuilder: (context, errorMessage) {
return Center(
child: Text(
errorMessage,
style: TextStyle(color: Colors.white),
),
);
},
);
}
setState(() {});
}
@override
void initState() {
this.initializeVideoPlayer();
super.initState();
}
@override
void dispose() {
super.dispose();
if (chewieController != null && chewieController.isFullScreen) {
chewieController?.dispose();
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 30, right: 30),
child: Column(
children: [
widget.mineral.properties.first.title != null &&
widget.mineral.properties.first.title != ""
? Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Linkify(
options: LinkifyOptions(
defaultToHttps: true, looseUrl: true),
onOpen: (link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
},
text:
"${widget.mineral.properties.first.title}${widget.mineral.properties?.first?.rulesInput?.rulesInputRequired == 'required' ? ' *' : ''}",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
linkStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
textAlign: TextAlign.center,
),
),
],
),
SizedBox(
height: 12,
),
],
)
: Container(),
widget.mineral.properties.first.description != null &&
widget.mineral.properties.first.description != ""
? Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Linkify(
options: LinkifyOptions(
defaultToHttps: true, looseUrl: true),
onOpen: (link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
},
text:
"${widget.mineral.properties.first.description}",
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
linkStyle: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: hexToColor(
widget.styles.first.attributes.fontColor),
),
textAlign: TextAlign.center,
),
),
],
),
SizedBox(
height: 12,
),
],
)
: Container(),
ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: RotatedBox(
quarterTurns: 0,
child: Container(
height: 200,
child: chewieController != null &&
chewieController.videoPlayerController.value.initialized
? Chewie(
controller: chewieController,
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SpinKitFadingCircle(
color: Colors.black,
),
],
),
),
),
),
widget.mineral.properties.first.caption != null &&
widget.mineral.properties.first.caption != ""
? Column(
children: [
SizedBox(
height: 12,
),
Text(
widget.mineral.properties.first.caption,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: getFontColor(),
),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
maxLines: 2,
),
],
)
: Container(),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: widget.mineral.properties.first.content != null
? Column(
children: [
Html(
data: widget.mineral.properties.first.content,
style: {
"html": HtmlStyle.Style(
fontSize: HtmlStyle.FontSize.large,
color: getFontColor()),
"p": HtmlStyle.Style(
lineHeight: 1,
display: HtmlStyle.Display.BLOCK,
margin: EdgeInsets.fromLTRB(0, 0, 0, 0),
),
}),
],
)
: Container(),
)
],
),
);
}
}
提前致谢!
终于在经过大约一周的努力后发现了问题,问题是因为在父小部件的 AppBar 中我使用了 PreferedSize 并为其内部的元素提供了一些填充,因为播放器是在它上面渲染的并且因为 appBar 它无法进入全屏,所以它会处理控制器。 解决方法是检查子窗口小部件内部是否有 VideoPlayer,忽略填充,效果很好。