flutter - setState() 不会更新 TabBarView 选项卡中的构造函数值
问题描述
我有一个 TabBarView,主小部件中有两个选项卡。第一个选项卡包括带有卡片的网格视图。卡片使用父小部件 (MyHomePage) 作为监听器来监听卡片内按钮的点击。当我单击某个卡片中的按钮时,侦听器 impl。必须打开第二个选项卡并将选定的游览传递给它。但是当我这样做时,在第一次迭代时, ExcursionEditor(currentExcursion) 说,那个参数是空的,但是父版本说,它不是。如果我调整浏览器的大小,它会调用全局重建并且 currentExcursion 达到最后的构建值。所以,我不明白,为什么 MyHomePage 构建不影响 TabBarView 内容与构造函数传递的参数
我的主页类
import 'package:flutter/material.dart';
import 'package:questbuilder/api/content_manager.dart';
import 'package:questbuilder/model/excursion.dart';
import 'package:questbuilder/pages/tab_editor.dart';
import 'package:questbuilder/pages/tab_my_excursions.dart';
import 'package:questbuilder/widgets/excursion_preview_card.dart';
import 'package:logger/logger.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with TickerProviderStateMixin
implements ExcursionCardInteractionListener {
Logger logger = Logger();
Excursion currentExcursion;
TabController tabController;
@override
void initState() {
super.initState();
print("INIT STATE FOR HOME PAGE");
tabController = TabController(vsync: this, length: 2);
}
@override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
print("HOME PAGE BUILD currentExcursion = ${currentExcursion?.toJson()}");
return Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: Size(screenSize.width, 1000),
child: Container(
color: Colors.black,
child: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 30, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: Text('QUESTBUILDER',
style: TextStyle(color: Colors.white))),
SizedBox(width: screenSize.width / 20),
Container(
width: screenSize.width / 6,
child: TabBar(
labelPadding: EdgeInsets.fromLTRB(10, 0, 10, 10),
indicatorColor: Colors.white,
controller: tabController,
tabs: [
Tab(text: "Мои экскурсии"),
Tab(text: "Редактор"),
]))
]),
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
child: Row(
children: [
FlatButton.icon(
label: Text("Создать экскурсию"),
icon: Icon(Icons.add),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40.0)),
textColor: Colors.white,
color: Colors.green,
onPressed: () {
createExcursion();
}),
SizedBox(
width: 40,
),
InkWell(
onTap: () {},
child: Text(
'Вход',
style: TextStyle(color: Colors.white),
),
)
],
)),
],
),
),
),
),
body: Padding(
padding: EdgeInsets.all(15),
child: TabBarView(
controller: tabController,
children: [
// Set listener to cards in this widget to prerform 'edit' clicks
MyExcursionsTab(this),
ExcursionEditor(currentExcursion)
],
)));
}
// Here i call setState from cards
@override
void editExcursion(Excursion excursion) {
setState(() {
currentExcursion = excursion;
});
tabController.animateTo(1);
}
@override
void dispose() {
tabController.dispose();
super.dispose();
}
void createExcursion() {
ContentManager.client.createExcursion(0).then((value) {
currentExcursion = value;
editExcursion(currentExcursion);
});
}
}
类游览编辑
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:questbuilder/api/content_manager.dart';
import 'package:questbuilder/model/excursion.dart';
import 'package:questbuilder/model/excursion_content.dart';
import 'package:questbuilder/model/excursion_data.dart';
import 'package:questbuilder/model/picture.dart';
class ExcursionEditor extends StatefulWidget {
Excursion excursion;
ExcursionEditor(this.excursion);
@override
State<StatefulWidget> createState() => ExcursionEditorState();
}
class ExcursionEditorState extends State<ExcursionEditor> {
ExcursionData currentData;
ExcursionContent currentContent;
Excursion excursion;
List<Picture> pictures = [];
@override
void initState() {
super.initState();
print("INIT EDITOR widget.excrusion = ${widget.excursion?.toJson()}");
// At this point, after call setState() in HomePage widget.excrusion is always null
// until I resize browser, thereby calling global state reset
//
if (widget.excursion != null)
ContentManager.client.getPictureList(widget.excursion.id).then((value) {
pictures.addAll(value);
print(pictures);
});
}
@override
Widget build(BuildContext context) {
excursion = widget.excursion;
print("BUILD EDITOR excursion = ${widget.excursion?.toJson()}");
return excursion != null
? Container()
: Container(
child: Align(
alignment: Alignment.center,
child: Text("Выберите экскурсию для редактирования")));
}
}
首次启动和卡片点击构建顺序的日志:
HOME PAGE BUILD currentExcursion = null
HOME PAGE BUILD currentExcursion = {id: 1}
INIT EDITOR widget.excrusion = null
BUILD EDITOR excursion = null
浏览器窗口调整大小后
HOME PAGE BUILD currentExcursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
HOME PAGE BUILD currentExcursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
屏幕调整大小问题仍然出现后,只需将编辑器中的 null 值替换为旧的 Excursion。新点击卡片没有效果,回调中的setState仍然没有更新。
我试图将它绑定在静态流侦听器上,在 TabController 侦听器上 - 它看起来就像 TabBarView 迟到了 1 个参数更新的构建周期。也许有一些类似的问题,但我已经从 thouse 答案中完成了所有工作,但一无所获
解决方案
我不太确定,但这似乎是和之间的竞争条件setState
,_tabController.animateTo(1);
因为它们都试图重建子ExcursionEditor(currentExcursion)
如果您在 ExcursionEditor 构造函数中打印偏移,您将看到更新后的值。但最后该值没有达到构建功能。
简单的解决方法是将 editExcursion 更改为 async 函数,并在这两个操作之间添加一个小的延迟。否则,您可以尝试使用其他方式在小部件之间传递数据(如提供程序)
@override
Future editExcursion(Excursion excursion) async {
setState(() {
currentExcursion = excursion;
});
await Future.delayed(Duration(milliseconds:50));
tabController.animateTo(1);
}
推荐阅读
- java - JPA:在 where 子句和 Case 语句中一起使用列表 - 有没有办法循环?
- python - 如何从 Python 中的字符串中提取多个浮点值
- python - Vaex 错误:AttributeError:“pyarrow.lib.ChunkedArray”对象没有属性“dtype”
- html - 我想在我的桌子上有隐形的边框......而且这根本不是微不足道的
- docker - 使用 Docker 提供从家庭网络到互联网的服务
- azure - 将 Spark 连接到 Azure Blob 存储
- amazon-web-services - AWS Glue RenameField 和写入存储桶失败并出现 Broken Pipe 错误
- python - BeautifulSoup 使用替换删除标签导致“NoneType”对象不可调用
- javascript - 开玩笑:jest.fn() 和 jest.fn().mockImplementation() 之间的区别是什么
- postgresql - 将 jsonb 字段提取为行