使用 setState() 时,Flutter 使屏幕闪烁蓝色
Flutter flashes screen light blue when using setState()
好的,找了半天没找到。这个问题似乎是构建缓存的问题,但我找不到有关清除构建缓存的资源,也找不到有关如何解决此特定问题的任何资源。
我的代码如下:
part of finance_app;
class InvestmentDetail extends StatefulWidget {
final String userEmail;
final String userName;
final String profilePic;
final String providerID;
final String uid;
InvestmentDetail(
{@required this.userEmail,
@required this.userName,
@required this.profilePic,
@required this.providerID,
@required this.uid});
@override
_InvestmentDetailState createState() => _InvestmentDetailState();
}
final FirebaseAuth _auth = FirebaseAuth.instance;
class _InvestmentDetailState extends State<InvestmentDetail>
with SingleTickerProviderStateMixin {
bool firstRun = true;
bool showMoney = true;
bool readyToShowChart = false;
var refreshkey = GlobalKey<RefreshIndicatorState>();
LineChart chart;
bool isTapped = false;
Future<String> displayName() async {
FirebaseUser _user = await FirebaseAuth.instance.currentUser();
return _user.displayName;
}
// various other functions go here
void createLine2() {
print("working");
Firestore.instance
.collection("user/" + widget.userEmail + "/investmentHistory")
.snapshots()
.listen(
(data) => {
print(data),
line1 = {},
data.documents.forEach(
(doc) => {
print(doc["percentage"]),
line1[DateTime.fromMillisecondsSinceEpoch(
int.parse(doc.documentID).round())] = doc["percentage"],
},
),
chart = !f.format(percentageChangeTotal).contains("-")
? LineChart.fromDateTimeMaps(
[line1], [Colors.red], ['%'],
)
: LineChart.fromDateTimeMaps(
[line1], [Colors.green], ['%'],
),
},
);
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
createLine2();
super.initState();
}
@override
Widget build(BuildContext context) {
createLine2();
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection(
'user/' + widget.userEmail + '/positionLabels')
.orderBy("Date", descending: true)
.orderBy("Time", descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return new Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(50.0),
child: Center(
child: Text(
"Add your first transaction!\n\nTap your profile picture in the top right to add your first order",
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
);
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
print("ConnectionState: NONE");
return Text(
'Select',
style: TextStyle(color: Colors.white),
);
case ConnectionState.waiting:
print("ConnectionState: WAITING");
return LinearProgressIndicator();
case ConnectionState.done:
print("ConnectionState: DONE");
return Text(
'$${snapshot.data} (closed)',
style: TextStyle(color: Colors.white),
);
default:
return Column(
children: <Widget>[
SearchBar(
widget: widget,
nodeOne: nodeOne,
controller: controller),
GestureDetector(
onTap: () {
setState(() {
readyToShowChart = true;
});
},
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(
top: 20,
right: 10,
left: 10,
bottom: 5),
height: 200,
width: MediaQuery.of(context).size.width,
child: AnimatedOpacity(
duration: Duration(seconds: 2),
opacity: readyToShowChart ? 1 : 0,
child: AnimatedLineChart(
chart,
key: UniqueKey(),
),
),
),
TotalPositionsWidget(
f.format(
getTotalMoneySpent(snapshot.data)),
getTotalAccountValue(snapshot.data)),
PositionsChangeWidget(
workWithNumbers(snapshot.data),
f.format(percentageChangeTotal)),
],
),
),
Expanded(
child: RefreshIndicator(
color: Colors.white,
onRefresh: () => refreshMain(snapshot.data),
key: refreshkey,
child: ListView(
children: snapshot.data.documents.map(
(DocumentSnapshot document) {
return new AnimatedOpacity(
opacity: 1.0,
duration:
Duration(milliseconds: 1000),
child: StockListTile(
document, widget.userEmail),
);
},
).toList(),
),
),
),
],
);
}
}
},
),
),
],
),
),
),
);
}
}
为简化起见:我有一个 StreamBuilder,它有一个列作为其子项。该列包含:
- 搜索栏
- 一个“图形小部件”
- 包裹在 GestureDetector 中的文本小部件
- 流视图中可刷新的数据库条目列表
当我点击文本小部件并触发“setState”时,屏幕在执行命令之前闪烁淡蓝色:
tapping the Text widget three times
看来 Flutter 正在重建整个布局。
一个较小的附带问题(虽然不像闪烁的蓝屏那么重要)是 AnimatedOpacity 和 AnimatedCrossFade 实际上从未在此小部件中设置动画,但在其他小部件中(例如 SearchBar)
我看过例子,和我遇到的类似,但我这里不是处理图片,所以我不知道去哪里。
我也尝试过 Dart DevTools,我所能得到的只是(在时间轴中使用自下而上的视图)它卡在了“performLayout --> layout --> performLayout --> 布局"循环
有人能给我指出正确的方向吗?
以后运行遇到这个问题的人,答案如下:
Flutter 在“setState”上重建了整个有状态小部件,因此蓝色闪光是由重建整个小部件引起的——基本上是整个应用程序。要删除 flash,请将尽可能多的小部件转换为无状态小部件,以便在重建时,Flutter 不必重新重建每个小部件。
好的,找了半天没找到。这个问题似乎是构建缓存的问题,但我找不到有关清除构建缓存的资源,也找不到有关如何解决此特定问题的任何资源。
我的代码如下:
part of finance_app;
class InvestmentDetail extends StatefulWidget {
final String userEmail;
final String userName;
final String profilePic;
final String providerID;
final String uid;
InvestmentDetail(
{@required this.userEmail,
@required this.userName,
@required this.profilePic,
@required this.providerID,
@required this.uid});
@override
_InvestmentDetailState createState() => _InvestmentDetailState();
}
final FirebaseAuth _auth = FirebaseAuth.instance;
class _InvestmentDetailState extends State<InvestmentDetail>
with SingleTickerProviderStateMixin {
bool firstRun = true;
bool showMoney = true;
bool readyToShowChart = false;
var refreshkey = GlobalKey<RefreshIndicatorState>();
LineChart chart;
bool isTapped = false;
Future<String> displayName() async {
FirebaseUser _user = await FirebaseAuth.instance.currentUser();
return _user.displayName;
}
// various other functions go here
void createLine2() {
print("working");
Firestore.instance
.collection("user/" + widget.userEmail + "/investmentHistory")
.snapshots()
.listen(
(data) => {
print(data),
line1 = {},
data.documents.forEach(
(doc) => {
print(doc["percentage"]),
line1[DateTime.fromMillisecondsSinceEpoch(
int.parse(doc.documentID).round())] = doc["percentage"],
},
),
chart = !f.format(percentageChangeTotal).contains("-")
? LineChart.fromDateTimeMaps(
[line1], [Colors.red], ['%'],
)
: LineChart.fromDateTimeMaps(
[line1], [Colors.green], ['%'],
),
},
);
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
createLine2();
super.initState();
}
@override
Widget build(BuildContext context) {
createLine2();
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection(
'user/' + widget.userEmail + '/positionLabels')
.orderBy("Date", descending: true)
.orderBy("Time", descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return new Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(50.0),
child: Center(
child: Text(
"Add your first transaction!\n\nTap your profile picture in the top right to add your first order",
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
);
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
print("ConnectionState: NONE");
return Text(
'Select',
style: TextStyle(color: Colors.white),
);
case ConnectionState.waiting:
print("ConnectionState: WAITING");
return LinearProgressIndicator();
case ConnectionState.done:
print("ConnectionState: DONE");
return Text(
'$${snapshot.data} (closed)',
style: TextStyle(color: Colors.white),
);
default:
return Column(
children: <Widget>[
SearchBar(
widget: widget,
nodeOne: nodeOne,
controller: controller),
GestureDetector(
onTap: () {
setState(() {
readyToShowChart = true;
});
},
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(
top: 20,
right: 10,
left: 10,
bottom: 5),
height: 200,
width: MediaQuery.of(context).size.width,
child: AnimatedOpacity(
duration: Duration(seconds: 2),
opacity: readyToShowChart ? 1 : 0,
child: AnimatedLineChart(
chart,
key: UniqueKey(),
),
),
),
TotalPositionsWidget(
f.format(
getTotalMoneySpent(snapshot.data)),
getTotalAccountValue(snapshot.data)),
PositionsChangeWidget(
workWithNumbers(snapshot.data),
f.format(percentageChangeTotal)),
],
),
),
Expanded(
child: RefreshIndicator(
color: Colors.white,
onRefresh: () => refreshMain(snapshot.data),
key: refreshkey,
child: ListView(
children: snapshot.data.documents.map(
(DocumentSnapshot document) {
return new AnimatedOpacity(
opacity: 1.0,
duration:
Duration(milliseconds: 1000),
child: StockListTile(
document, widget.userEmail),
);
},
).toList(),
),
),
),
],
);
}
}
},
),
),
],
),
),
),
);
}
}
为简化起见:我有一个 StreamBuilder,它有一个列作为其子项。该列包含:
- 搜索栏
- 一个“图形小部件”
- 包裹在 GestureDetector 中的文本小部件
- 流视图中可刷新的数据库条目列表
当我点击文本小部件并触发“setState”时,屏幕在执行命令之前闪烁淡蓝色:
tapping the Text widget three times
看来 Flutter 正在重建整个布局。
一个较小的附带问题(虽然不像闪烁的蓝屏那么重要)是 AnimatedOpacity 和 AnimatedCrossFade 实际上从未在此小部件中设置动画,但在其他小部件中(例如 SearchBar)
我看过
我也尝试过 Dart DevTools,我所能得到的只是(在时间轴中使用自下而上的视图)它卡在了“performLayout --> layout --> performLayout --> 布局"循环
有人能给我指出正确的方向吗?
以后运行遇到这个问题的人,答案如下:
Flutter 在“setState”上重建了整个有状态小部件,因此蓝色闪光是由重建整个小部件引起的——基本上是整个应用程序。要删除 flash,请将尽可能多的小部件转换为无状态小部件,以便在重建时,Flutter 不必重新重建每个小部件。