#flutter #dart
#трепетать #дротик #флаттер
Вопрос:
У меня есть страница, на которой показано видео. Но когда я завершаю видео и возвращаюсь на предыдущую страницу, я получаю следующую ошибку. Мое приложение работает нормально и без сбоев, но приведенная ниже ошибка меня беспокоит.
setState() или markNeedsBuild(), вызываемые во время сборки, — это ошибка, которую я не могу исправить
Мне нужна помощь, чтобы исправить эту ошибку.
Вот моя ошибка:
══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
The following assertion was thrown while dispatching notifications for VideoPlayerController:
setState() or markNeedsBuild() called during build.
This VideoPlayerScreen widget cannot be marked as needing to build because the framework is already
in the process of building widgets. A widget can be marked as needing to be built during the build
phase only if one of its ancestors is currently building. This exception is allowed because the
framework builds parent widgets before children, which means a dirty descendant will always be
built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
VideoPlayerScreen
The widget which was currently being built when the offending call was made was:
Overlay-[LabeledGlobalKey<OverlayState>#e44a5]
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4211:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4226:6)
#2 State.setState (package:flutter/src/widgets/framework.dart:1260:14)
#3 _VideoPlayerScreenState._listener (package:flow/widget/page/video_player.dart:52:7)
#4 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:209:21)
#5 ValueNotifier.value= (package:flutter/src/foundation/change_notifier.dart:276:5)
#6 VideoPlayerController.pause (package:video_player/video_player.dart:360:5)
#7 _VideoPlayerScreenState.deactivate (package:/widget/page/video_player.dart:175:34)
(elided 3 frames from dart:async)
The VideoPlayerController sending notification was:
VideoPlayerController#619c5(VideoPlayerValue(duration: 0:10:55.430000, size: Size(1920.0, 1080.0),
position: 0:10:55.430000, caption: Instance of 'Caption', buffered: [DurationRange(start:
0:00:00.000000, end: 0:10:55.430000)], isPlaying: false, isLooping: false, isBuffering:
falsevolume: 1.0, errorDescription: null))
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by foundation library ════════════════════════════════════════════════════
The following assertion was thrown while dispatching notifications for VideoPlayerController:
setState() or markNeedsBuild() called during build.
This VideoPlayerScreen widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: VideoPlayerScreen
dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#fe7c2], MediaQuery]
state: _VideoPlayerScreenState#4559e
The widget which was currently being built when the offending call was made was: Overlay-[LabeledGlobalKey<OverlayState>#e44a5]
state: OverlayState#cf68b(entries: [OverlayEntry#517dd(opaque: true; maintainState: false), OverlayEntry#87d9a(opaque: false; maintainState: true)])
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4211:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4226:6)
#2 State.setState (package:flutter/src/widgets/framework.dart:1260:14)
#3 _VideoPlayerScreenState._listener (package:flow/widget/page/video_player.dart:52:7)
#4 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:209:21)
...
The VideoPlayerController sending notification was: VideoPlayerController#619c5(VideoPlayerValue(duration: 0:10:55.430000, size: Size(1920.0, 1080.0), position: 0:10:55.430000, caption: Instance of 'Caption', buffered: [DurationRange(start: 0:00:00.000000, end: 0:10:55.430000)], isPlaying: false, isLooping: false, isBuffering: falsevolume: 1.0, errorDescription: null))
════════════════════════════════════════════════════════════════════════════════════════════════════
I/ExoPlayerImpl(24022): Release 569e95d [ExoPlayerLib/2.9.6] [tissot_sprout, Mi A1, Xiaomi, 28] [goog.exo.core]
E/BufferQueueProducer(24022): [SurfaceTexture-0-24022-9] cancelBuffer: BufferQueue has been abandoned
D/SurfaceUtils(24022): disconnecting from surface 0x70a0b7e010, reason disconnectFromSurface
Вот мой код:
class VideoPlayerScreen extends StatefulWidget {
final VideoPlayerController videoPlayerController;
const VideoPlayerScreen({
Key key,
@required this.videoPlayerController,
}) : super(key: key);
@override
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
bool _buttonEnabled = false;
VideoPlayerController _videoPlayerController;
int playBackTime = 0;
//VoidCallback _listener;
void _listener() {
if (_videoPlayerController.value.position ==
_videoPlayerController.value.duration) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_buttonEnabled = true;
});
}
});
}
if (mounted) {
setState(() {
playBackTime = _videoPlayerController.value.position.inSeconds;
});
}
}
@override
void initState() {
_videoPlayerController = widget.videoPlayerController;
_videoPlayerController
..addListener(_listener)
..initialize().then((_) {
_videoPlayerController.play();
setState(() {});
});
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
//DeviceOrientation.landscapeRight,
]);
AutoOrientation.landscapeAutoMode();
super.initState();
}
@override
Widget build(BuildContext context) {
//final length = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Stack(
children: <Widget>[
_videoPlayerController.value.initialized
? _playerWidget()
: Center(
child: CircularProgressIndicator(),
),
Visibility(
visible: _buttonEnabled,
child: Container(),
),
],
),
);
}
_playAndPauseButton() {
return InkWell(
child: Icon(
_videoPlayerController.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Theme.of(context).buttonColor,
size: 25,
),
onTap: () {
_videoPlayerController.value.isPlaying
? _videoPlayerController.pause()
: _videoPlayerController.play();
},
);
}
_slider() {
final length = MediaQuery.of(context).size;
return Container(
width: length.width * 0.85,
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.black,
inactiveTrackColor: Colors.grey,
trackHeight: 1.0,
thumbColor: Colors.yellow,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 6.0),
),
child: Slider(
max: _videoPlayerController.value.duration.inSeconds.toDouble(),
min: 0.0,
value: playBackTime.toDouble(),
onChanged: (v) {
_videoPlayerController.seekTo(Duration(seconds: v.toInt()));
}),
),
);
}
_playerWidget() {
final length = MediaQuery.of(context).size;
return GestureDetector(
onTap: () {
final snackBar = SnackBar(
content: Container(
height: length.height * 0.02,
width: length.width,
child: Row(
children: [
_playAndPauseButton(),
_slider(),
],
),
),
);
Scaffold.of(context).showSnackBar(snackBar);
},
child: AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
),
);
}
@override
void deactivate() {
widget.videoPlayerController.pause();
super.deactivate();
}
@override
void dispose() {
widget.videoPlayerController.dispose();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
AutoOrientation.landscapeAutoMode();
AutoOrientation.portraitUpMode();
super.dispose();
}
}
Ответ №1:
Попытайтесь избавиться videoController
:
@override
void dispose() {
widget.videoPlayerController?.dispose();
_videoPlayerController.removeListener(_listener);
_videoPlayerController.dispose();
super.dispose();
}
Наконец, измените void _listener()
на :
void _listener() {
if (_videoPlayerController.value.position ==
_videoPlayerController.value.duration) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_buttonEnabled = true;
});
}
});
}
WidgetsBinding.instance.addPostFrameCallback((__) {
if (mounted) {
setState(() {
playBackTime = _videoPlayerController.value.position.inSeconds;
});
}
});
}
Комментарии:
1. Я обновил свой код. Не могли бы вы, пожалуйста, проверить, есть ли проблема с моим кодом. Или предоставьте мне пример кода, в котором вы используете видеопроигрыватель, при нажатии на кнопку видео должно воспроизводиться в альбомном режиме, а при возврате на предыдущую страницу приложение должно быть в портретном режиме.
2. Префект! Большое вам спасибо за вашу помощь. Ошибка устранена, и мое приложение работает нормально. Такое облегчение.