#flutter #setstate #dart-pub #statefulwidget
#flutter #setstate #dart-pub #statefulwidget
Вопрос:
Я использую пакет extended_image для загрузки изображений из сети и отображения мерцания при загрузке или при ошибке.
Я получаю эту ошибку setState() or markNeedsBuild() called during build
, когда пытаюсь вызвать setState внутри loadStateChanged
Фактически, у меня есть два виджета, один VideoThumbnail
из которых отвечает за загрузку эскизов из сети, а другой, VideoDesc
который должен отображать описание эскизов.
Но я бы хотел, чтобы описание отображало мерцание, когда изображение не загружается или загружается дольше.
Я создал две переменные состояния в VideoThumbnail
виджете, которые должны быть переданы VideoDesc
виджету
videoLoading = true;
videoError = false;
Вот мой код, следующий примеру репозитория:
Состояние VideoThumbnail
class _VideoThumbnailState extends State<VideoThumbnail>
with SingleTickerProviderStateMixin {
bool videoLoading;
bool videoError;
AnimationController _controller;
@override
void initState() {
videoLoading = true;
videoError = false;
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
);
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
print("Build Process Complete");
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(4.0),
child: ExtendedImage.network(
widget.videoUrl,
width: widget.width,
height: (widget.width) * 3 / 4,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
_controller.reset();
setState(() {
videoError = false;
videoLoading = true;
});
return Shimmer.fromColors(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
),
),
baseColor: Colors.black12,
highlightColor: Colors.white24,
);
break;
case LoadState.completed:
_controller.forward();
setState(() {
videoError = false;
videoLoading = false;
});
return FadeTransition(
opacity: _controller,
child: ExtendedRawImage(
image: state.extendedImageInfo?.image,
width: widget.width,
height: (widget.width) * 3 / 4,
),
);
break;
case LoadState.failed:
_controller.reset();
state.imageProvider.evict();
setState(() {
videoError = true;
videoLoading = false;
});
return Container(
width: widget.width,
height: (widget.width) * 3 / 4,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/img/not-found.png"),
fit: BoxFit.fill,
),
),
);
break;
default:
return Container();
}
},
),
),
VideoDesc(
desc: widget.desc,
videoError: videoError,
videoLoading: videoLoading,
)
],
),
);
}
}
Виджет видео
class VideoDesc extends StatelessWidget {
final String desc;
final bool videoLoading;
final bool videoError;
const VideoDesc({
Key key,
@required this.desc,
this.videoLoading = true,
this.videoError = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child: videoError || videoLoading
? Shimmer.fromColors(
baseColor: Colors.grey[700],
highlightColor: Colors.white24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12.0),
Container(
width: double.infinity,
height: 8.0,
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(2.0),
),
),
SizedBox(height: 12.0),
Container(
width: 80.0,
height: 8.0,
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(2.0),
),
),
],
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12.0),
Text(
desc,
style: TextStyle(
color: Colors.white,
fontSize: 11.0,
),
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 5.0),
Text(
"361,143,203 views",
style: TextStyle(
color: Colors.white54,
fontSize: 12.0,
),
),
],
),
);
}
}
Кто-нибудь может мне помочь с этой проблемой? Или, если есть лучший способ получить extendedImageLoadState
значение и передать его другому виджету без вызова setState внутри loadStateChanged
Ответ №1:
Вы не можете вызвать setState
во время процесса сборки.
Если вам действительно нужно, вы можете сделать это, используя вместо:
WidgetsBinding.instance.addPostFrameCallback(() => setState((){}));
Однако имейте в виду, что при наличии этого на вашем switch-case
компьютере будет запланирован бесконечный цикл перестроек, который вам тоже не нужен.
Я предлагаю вам переструктурировать логику вашего пользовательского интерфейса или, по крайней мере, сделать ее условной:
if(!videoLoading) {
WidgetsBinding.instance.addPostFrameCallback(() => setState((){
videoError = false;
videoLoading = true;
}));
}
Комментарии:
1. Мигель Руйво, спасибо за быстрый ответ, я попробую ваше предложение. И вернемся к вам позже