Flutter:FutureBuilder 一直闪烁
Flutter: FutureBuilder keeps flashing
我在调用 setState()
时遇到问题。
这是问题的屏幕录像:https://cln.sh/ndL24t。
如您所见,单击小部件会导致图片从加载到图片闪烁。
问题是因为我有两个期货,因此是未来的建设者。第一个 future returns 将要显示的信息。第二个未来 returns 基于仪表板上显示的条目的 id 的图像。我有第一个未来的建设者得到文本未来。我将未来设置在 initState()
以避免重复调用。然后我在 initState()
中设置了另一个 Future
变量,如下 journalFuture.then(...)
。我还有一个 AnimatedContainer()
,当用户点击或 up/cancels 通过更改值并调用 setState()
时,它会进行动画处理。我认为这是问题的原因,但我不知道如何解决它。
这是我的代码:
const JournalSummary({Key? key}) : super(key: key);
@override
_JournalSummaryState createState() => _JournalSummaryState();
}
class _JournalSummaryState extends State<JournalSummary> {
final GlobalKey lottieKey = GlobalKey(debugLabel: 'Lottie Key');
late Future<List<JournalEntryData>> journalFuture;
Future? picturesFuture;
late Color _shadowColor;
double _blurRadius = 15;
void _animateDown() {
setState(() {
_shadowColor = Theme.of(context).shadowColor.withOpacity(0.40);
_blurRadius = 25;
});
}
void _animateUp() {
setState(() {
_shadowColor = Theme.of(context).shadowColor.withOpacity(0.19);
_blurRadius = 15;
});
}
@override
void initState() {
super.initState();
journalFuture = DatabaseService(uid: AuthService().getUser()!.uid)
.getJournalEntries(limit: 10);
journalFuture.then((entries) {
if (entries.isNotEmpty) {
entries
.sort((JournalEntryData firstEntry, JournalEntryData secondEntry) {
DateTime firstDate = DateTime.parse(firstEntry.date);
DateTime secondDate = DateTime.parse(secondEntry.date);
int feelingCmp = secondEntry.feeling.compareTo(firstEntry.feeling);
if (feelingCmp != 0) return feelingCmp;
return secondDate.compareTo(firstDate);
});
}
picturesFuture = StorageService(AuthService().getUser()!.uid)
.getPictures(entries[0].date);
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_shadowColor = Theme.of(context).shadowColor.withOpacity(0.19);
}
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: FutureBuilder(
future: journalFuture,
builder: (context, entryFuture) {
List<JournalEntryData> entries =
(entryFuture.data as List<JournalEntryData>?) ?? [];
if (entries.isNotEmpty) {
entries.sort(
(JournalEntryData firstEntry, JournalEntryData secondEntry) {
DateTime firstDate = DateTime.parse(firstEntry.date);
DateTime secondDate = DateTime.parse(secondEntry.date);
int feelingCmp =
secondEntry.feeling.compareTo(firstEntry.feeling);
if (feelingCmp != 0) return feelingCmp;
return secondDate.compareTo(firstDate);
});
}
return GestureDetector(
onTap: () => AppTheme.homeNavkey.currentState!.pushReplacement(
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 320),
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return Tasks(false);
},
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return Align(
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
),
),
onTapDown: (_) => _animateDown(),
onTapUp: (_) => _animateUp(),
onTapCancel: () => _animateUp(),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: _shadowColor,
blurRadius: _blurRadius,
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
'assets/adobe/illustrator/icons/svg/journal_selected.svg',
width: 35,
height: 35,
),
SizedBox(width: 8),
Text(
entryFuture.data == null
? 'Journal'
: _formatedDate(entries[0].date),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.subtitle2!
.copyWith(color: Theme.of(context).primaryColor),
),
Spacer(),
(entryFuture.data == null)
? Container()
: Lottie.asset(
'assets/lottie/faces/${(entries[0].feeling == 1 ? 'sad' : (entries[0].feeling == 2 ? 'meh' : 'happy'))}.json',
key: lottieKey,
repeat: false,
width: 50,
height: 50,
),
],
),
SizedBox(height: 10),
FutureBuilder(
key: UniqueKey(),
future: picturesFuture,
initialData: [],
builder: (context, picFuture) {
return (picFuture.connectionState ==
ConnectionState.waiting ||
picturesFuture == null)
? Center(child: CircularProgressIndicator())
: SizedBox(
width:
MediaQuery.of(context).size.width - 40 - 50,
height: 160,
child: ListView(
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
children: _buildPictures(
(picFuture.data as List<Uint8List>)),
),
);
},
),
SizedBox(height: 10),
(entryFuture.data == null)
? Container()
: Text(
entries[0].entryText,
style: Theme.of(context).textTheme.bodyText2,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
},
),
);
}
List<Widget> _buildPictures(List<Uint8List> pictures) {
List<Widget> picWidgets = [];
int index = 0;
for (var pic in pictures) {
picWidgets.add(
Container(
key: UniqueKey(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
blurRadius: 6,
color: Theme.of(context).shadowColor.withOpacity(0.52),
offset: Offset(0, 3),
)
],
),
clipBehavior: Clip.hardEdge,
margin: index == 0
? EdgeInsets.only(right: 10)
: EdgeInsets.symmetric(horizontal: 10),
child: Image.memory(
pic,
key: UniqueKey(),
height: 160,
fit: BoxFit.cover,
),
),
);
index += 1;
}
return picWidgets;
}
String _formatedDate(String date) {
DateTime dateTime = DateTime.parse(date);
Map<int, String> _monthNumToName = {
1: 'Jan',
2: 'Feb',
3: 'Mar',
4: 'Apr',
5: 'May',
6: 'Jun',
7: 'Jul',
8: 'Aug',
9: 'Sep',
10: 'Oct',
11: 'Nov',
12: 'Dec',
};
return '${_monthNumToName[dateTime.month]} ${dateTime.day}, ${dateTime.year}';
}
}
成功了。正如我的评论中提到的,我在问题发布后 运行 flutter upgrade
因为发布问题后出现了新版本,并且还稍微更改了代码所以我不确定是什么解决了问题.也可能是我停止了 运行 然后 运行 应用程序。
我在调用 setState()
时遇到问题。
这是问题的屏幕录像:https://cln.sh/ndL24t。
如您所见,单击小部件会导致图片从加载到图片闪烁。
问题是因为我有两个期货,因此是未来的建设者。第一个 future returns 将要显示的信息。第二个未来 returns 基于仪表板上显示的条目的 id 的图像。我有第一个未来的建设者得到文本未来。我将未来设置在 initState()
以避免重复调用。然后我在 initState()
中设置了另一个 Future
变量,如下 journalFuture.then(...)
。我还有一个 AnimatedContainer()
,当用户点击或 up/cancels 通过更改值并调用 setState()
时,它会进行动画处理。我认为这是问题的原因,但我不知道如何解决它。
这是我的代码:
const JournalSummary({Key? key}) : super(key: key);
@override
_JournalSummaryState createState() => _JournalSummaryState();
}
class _JournalSummaryState extends State<JournalSummary> {
final GlobalKey lottieKey = GlobalKey(debugLabel: 'Lottie Key');
late Future<List<JournalEntryData>> journalFuture;
Future? picturesFuture;
late Color _shadowColor;
double _blurRadius = 15;
void _animateDown() {
setState(() {
_shadowColor = Theme.of(context).shadowColor.withOpacity(0.40);
_blurRadius = 25;
});
}
void _animateUp() {
setState(() {
_shadowColor = Theme.of(context).shadowColor.withOpacity(0.19);
_blurRadius = 15;
});
}
@override
void initState() {
super.initState();
journalFuture = DatabaseService(uid: AuthService().getUser()!.uid)
.getJournalEntries(limit: 10);
journalFuture.then((entries) {
if (entries.isNotEmpty) {
entries
.sort((JournalEntryData firstEntry, JournalEntryData secondEntry) {
DateTime firstDate = DateTime.parse(firstEntry.date);
DateTime secondDate = DateTime.parse(secondEntry.date);
int feelingCmp = secondEntry.feeling.compareTo(firstEntry.feeling);
if (feelingCmp != 0) return feelingCmp;
return secondDate.compareTo(firstDate);
});
}
picturesFuture = StorageService(AuthService().getUser()!.uid)
.getPictures(entries[0].date);
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_shadowColor = Theme.of(context).shadowColor.withOpacity(0.19);
}
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: FutureBuilder(
future: journalFuture,
builder: (context, entryFuture) {
List<JournalEntryData> entries =
(entryFuture.data as List<JournalEntryData>?) ?? [];
if (entries.isNotEmpty) {
entries.sort(
(JournalEntryData firstEntry, JournalEntryData secondEntry) {
DateTime firstDate = DateTime.parse(firstEntry.date);
DateTime secondDate = DateTime.parse(secondEntry.date);
int feelingCmp =
secondEntry.feeling.compareTo(firstEntry.feeling);
if (feelingCmp != 0) return feelingCmp;
return secondDate.compareTo(firstDate);
});
}
return GestureDetector(
onTap: () => AppTheme.homeNavkey.currentState!.pushReplacement(
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 320),
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return Tasks(false);
},
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return Align(
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
),
),
onTapDown: (_) => _animateDown(),
onTapUp: (_) => _animateUp(),
onTapCancel: () => _animateUp(),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: _shadowColor,
blurRadius: _blurRadius,
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
'assets/adobe/illustrator/icons/svg/journal_selected.svg',
width: 35,
height: 35,
),
SizedBox(width: 8),
Text(
entryFuture.data == null
? 'Journal'
: _formatedDate(entries[0].date),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.subtitle2!
.copyWith(color: Theme.of(context).primaryColor),
),
Spacer(),
(entryFuture.data == null)
? Container()
: Lottie.asset(
'assets/lottie/faces/${(entries[0].feeling == 1 ? 'sad' : (entries[0].feeling == 2 ? 'meh' : 'happy'))}.json',
key: lottieKey,
repeat: false,
width: 50,
height: 50,
),
],
),
SizedBox(height: 10),
FutureBuilder(
key: UniqueKey(),
future: picturesFuture,
initialData: [],
builder: (context, picFuture) {
return (picFuture.connectionState ==
ConnectionState.waiting ||
picturesFuture == null)
? Center(child: CircularProgressIndicator())
: SizedBox(
width:
MediaQuery.of(context).size.width - 40 - 50,
height: 160,
child: ListView(
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
children: _buildPictures(
(picFuture.data as List<Uint8List>)),
),
);
},
),
SizedBox(height: 10),
(entryFuture.data == null)
? Container()
: Text(
entries[0].entryText,
style: Theme.of(context).textTheme.bodyText2,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
},
),
);
}
List<Widget> _buildPictures(List<Uint8List> pictures) {
List<Widget> picWidgets = [];
int index = 0;
for (var pic in pictures) {
picWidgets.add(
Container(
key: UniqueKey(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
blurRadius: 6,
color: Theme.of(context).shadowColor.withOpacity(0.52),
offset: Offset(0, 3),
)
],
),
clipBehavior: Clip.hardEdge,
margin: index == 0
? EdgeInsets.only(right: 10)
: EdgeInsets.symmetric(horizontal: 10),
child: Image.memory(
pic,
key: UniqueKey(),
height: 160,
fit: BoxFit.cover,
),
),
);
index += 1;
}
return picWidgets;
}
String _formatedDate(String date) {
DateTime dateTime = DateTime.parse(date);
Map<int, String> _monthNumToName = {
1: 'Jan',
2: 'Feb',
3: 'Mar',
4: 'Apr',
5: 'May',
6: 'Jun',
7: 'Jul',
8: 'Aug',
9: 'Sep',
10: 'Oct',
11: 'Nov',
12: 'Dec',
};
return '${_monthNumToName[dateTime.month]} ${dateTime.day}, ${dateTime.year}';
}
}
成功了。正如我的评论中提到的,我在问题发布后 运行 flutter upgrade
因为发布问题后出现了新版本,并且还稍微更改了代码所以我不确定是什么解决了问题.也可能是我停止了 运行 然后 运行 应用程序。