我在我的Flutter应用程序中发现了一个非常奇怪的错误。我正在使用一个具有itemExtend
值的ListView
。当在应用程序内导航时,一切都正常 - 控制台中没有错误消息。但是,在进行热重载之后,例如通过编辑并保存放置ListView的文件上的代码,我会收到许多RenderRepaintBoundary
错误。
这是我的ListView:
return ListView.builder(
padding: EdgeInsets.only(top: 25, bottom: 20),
shrinkWrap: true,
itemExtent: 95, // Error only appears if I add itemExtent
physics: ClampingScrollPhysics(),
itemCount: entriesController.guestbookEntries.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onLongPress: () async {
// Some stuff
},
child: Text("Test Widget")
);
},
);
在热重载后,控制台会显示以下错误信息(但应用程序按预期工作):
你知道为什么没有使用热重载一切正常吗?如何修复这样的错误?我试图删除[Easy Localization] Build [Easy Localization] Init Localization Delegate [Easy Localization] Init provider
════════ Exception caught by rendering library ═════════════════════════════════ The following _CastError was thrown during performLayout(): Null check operator used on a null value
The relevant error-causing widget was ListView When the exception was thrown, this was the stack #0 RenderSliverFixedExtentBoxAdaptor.performLayout #1 RenderObject.layout #2 RenderSliverEdgeInsetsPadding.performLayout #3 RenderSliverPadding.performLayout #4 RenderObject.layout ... The following RenderObject was being processed when the exception was fired: RenderSliverFixedExtentList#b073b relayoutBoundary=up11 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE RenderObject: RenderSliverFixedExtentList#b073b relayoutBoundary=up11 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: paintOffset=Offset(0.0, 25.0) (can use size) constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle, scrollOffset: 0.0, remainingPaintExtent: Infinity, crossAxisExtent: 390.0, crossAxisDirection: AxisDirection.right, viewportMainAxisExtent: Infinity, remainingCacheExtent: Infinity, cacheOrigin: 0.0) geometry: SliverGeometry(scrollExtent: 760.0, paintExtent: 760.0, maxPaintExtent: 760.0, cacheExtent: 760.0) scrollExtent: 760.0 paintExtent: 760.0 maxPaintExtent: 760.0 cacheExtent: 760.0 currently live children: 0 to 7 child with index 0: RenderIndexedSemantics#56371 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=0; layoutOffset=0.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 0 child: RenderRepaintBoundary#1056f NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#9f74a engine layer: OffsetEngineLayer#9b21b offset: Offset(0.0, 25.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#c0c8b NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#c4b22 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 1: RenderIndexedSemantics#c082a NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=1; layoutOffset=95.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 1 child: RenderRepaintBoundary#f5bbf NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#003b7 engine layer: OffsetEngineLayer#75c0b offset: Offset(0.0, 120.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#eee56 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#82ca0 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 2: RenderIndexedSemantics#dc0db NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=2; layoutOffset=190.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 2 child: RenderRepaintBoundary#394b9 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#19e8a engine layer: OffsetEngineLayer#11d00 offset: Offset(0.0, 215.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#0a954 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#d9fec NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 3: RenderIndexedSemantics#7cad9 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=3; layoutOffset=285.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 3 child: RenderRepaintBoundary#6e0bd NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#cacc4 engine layer: OffsetEngineLayer#8785f offset: Offset(0.0, 310.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#0f21d NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#90d32 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 4: RenderIndexedSemantics#98e3a NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=4; layoutOffset=380.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 4 child: RenderRepaintBoundary#4b651 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#4d349 engine layer: OffsetEngineLayer#3ac9d offset: Offset(0.0, 405.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#d08c0 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#4d447 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 5: RenderIndexedSemantics#3c29c NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=5; layoutOffset=475.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 5 child: RenderRepaintBoundary#95d2d NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#0a5f6 engine layer: OffsetEngineLayer#48bb1 offset: Offset(0.0, 500.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#9d4f2 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#41da2 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 6: RenderIndexedSemantics#97f8c NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=6; layoutOffset=570.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 6 child: RenderRepaintBoundary#43698 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#4c73b engine layer: OffsetEngineLayer#cf9c5 offset: Offset(0.0, 595.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#a439c NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#c1b95 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down child with index 7: RenderIndexedSemantics#af472 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: index=7; layoutOffset=665.0 constraints: BoxConstraints(w=390.0, h=95.0) semantic boundary size: Size(390.0, 95.0) index: 7 child: RenderRepaintBoundary#af250 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) layer: OffsetLayer#45ab6 engine layer: OffsetEngineLayer#f6a96 offset: Offset(0.0, 690.0) size: Size(390.0, 95.0) metrics: 83.3% useful (1 bad vs 5 good) diagnosis: this is a useful repaint boundary and should be kept child: RenderSemanticsGestureHandler#aac55 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) gestures: long press child: RenderPointerListener#428a8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE parentData: (can use size) constraints: BoxConstraints(w=390.0, h=95.0) size: Size(390.0, 95.0) behavior: deferToChild listeners: down ════════════════════════════════════════════════════════════════════════════════ Reloaded 67 of 2009 libraries in 683ms.
itemExtend
,这样就可以避免错误了。但是没有 itemExtend
时,我的 ListView 就会出现一些闪烁情况,看起来像是顺序改变或者其他问题。这很难看。因此,我需要 itemExtend
来解决它。如果您需要更好地查看代码上下文,我可以给您发送整个文件的代码。顺便说一下,还没有像在构建方法中处理数据这样的函数。这是一个旧项目,我正在全面重写它。但是这些事情并不负责错误。有趣的是,当我移除 "itemExtend" 时,错误就不会出现。这怎么可能呢?
class GuestGuestbookContent extends StatelessWidget {
GuestGuestbookContent({
Key key,
}) : super(key: key);
GuestbooksController get guestbooksController => Get.find();
EntriesController get entriesController => Get.find();
UsersController get usersController => Get.find();
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
padding: EdgeInsets.only(bottom: 50, top: 50),
alignment: Alignment.center,
child: entriesController.obx((state) {
return Column(
children: [
Container(
child: MixinBuilder(
initState: (_) async {
await entriesController.checkGuestbookForUserEntries(
guestbooksController.currentGuestbook.value.id);
},
builder: (_) {
if (entriesController.hasUserEntry.value == true) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding:
const EdgeInsets.only(left: 25.0, right: 25),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("your_entries",
style: Get.theme.textTheme.headline4)
.tr(),
IconButton(
alignment: Alignment.centerRight,
padding: EdgeInsets.all(0),
icon: Icon(
entriesController.showOwnEntries.value ==
false
? CupertinoIcons.arrow_down
: CupertinoIcons.arrow_up,
color: Get.theme.colorScheme.primary,
size: 20,
),
onPressed: () => entriesController
.showOwnEntries.value =
!entriesController.showOwnEntries.value)
],
),
),
entriesController.showOwnEntries.value == true
? _buildUserEntries()
: Container(),
SizedBox(height: 15),
],
);
} else {
bool isOpen = true;
DateTime startDate;
if (guestbooksController
.currentGuestbook.value.startDate !=
null) {
startDate = DateTime.parse(guestbooksController
.currentGuestbook.value.startDate);
isOpen = startDate.isAtSameMomentAs(DateTime.now()) ||
startDate.isBefore(DateTime.now());
}
if (isOpen) {
return _buildAddEntryButton();
} else {
return Container(
child: Column(
children: [
Image.asset("assets/animations/clock.gif",
width:
context.isTablet ? 400 : Get.width / 2),
SizedBox(
height: 15,
),
Text("guestbook_opens_at").tr(),
SizedBox(height: 5),
Text(
DateFormat(tr("date_format"))
.format(startDate),
style: Get.theme.textTheme.headline1)
],
),
);
}
}
},
),
),
// Alle Einträge
guestbooksController.currentGuestbook.value.isPublic == true ||
_isModerator() == true
? _buildAllEntriesContainer()
: Container(),
],
);
}, onLoading: LoadingSpinner()),
),
);
}
bool _isModerator() {
return usersController.userIsModerator(
userID: usersController.ownUser.value.uid,
guestbook: guestbooksController.currentGuestbook.value);
}
CupertinoButton _buildAddEntryButton() {
return CupertinoButton(
child: Column(
children: [
Icon(
Icons.add,
size: 50,
),
Text(
"create_entry",
style:
TextStyle(color: Get.theme.colorScheme.primary, fontSize: 20),
).tr(),
SizedBox(height: 25),
],
),
onPressed: () => Get.toNamed("writeNewEntry"));
}
ListView _buildUserEntries() {
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.only(top: 0, bottom: 10),
physics: ClampingScrollPhysics(),
itemCount: entriesController.userEntriesData.length,
itemBuilder: (BuildContext context, int index) {
final Entry entry = entriesController.userEntriesData[index];
// Manage description
String entryContent = "";
List entryContentInput = [];
if (entry.content != null) {
final parsedContent = jsonDecode(entry.content);
if (parsedContent != null) {
for (int i = 0; i < parsedContent.length; i++) {
entryContentInput.add(parsedContent[i]["insert"]);
}
entryContent = entryContentInput.join("").toString();
}
}
return GestureDetector(
onLongPress: () {
_userEntryOptions(entry);
},
child: GuestListTile(
entry: entry,
description: entryContent != null && entryContent.isNotEmpty
? entryContent
: tr("no_text_added"),
owner: entry.owner,
hasImages: entry.images != null && entry.images.isNotEmpty,
hasVideos: entry.videos != null && entry.videos.isNotEmpty,
hasPainting:
entry.paintingImage != null && entry.paintingImage.isNotEmpty,
),
);
});
}
Container _buildAllEntriesContainer() {
return Container(
child: MixinBuilder(
builder: (EntriesController controller) {
if (controller.guestbookEntriesWithoutOwn.length > 0) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("entries_from_all_guests",
style: Get.theme.textTheme.headline4)
.tr(),
CircleAvatar(
radius: 10,
backgroundColor: Get.theme.colorScheme.primary,
child: Text(
controller.guestbookEntriesWithoutOwn.length
.toString(),
style: TextStyle(
fontSize: 10,
color: Get.theme.colorScheme.background)),
)
],
),
),
_buildAllEntries(controller.guestbookEntriesWithoutOwn),
],
);
} else {
return Container();
}
},
),
);
}
Widget _buildAllEntries(List<Entry> entries) {
if (entries != null && entries.isNotEmpty) {
return ListView.builder(
padding: EdgeInsets.only(top: 10, bottom: 10),
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemExtent: 95,
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
Entry entry;
if (entries.length > 0) {
entry = entries[index];
// Manage description
String entryContent = "";
List entryContentInput = [];
if (entry.content != null) {
final parsedContent = jsonDecode(entry.content);
if (parsedContent != null) {
for (int i = 0; i < parsedContent.length; i++) {
entryContentInput.add(parsedContent[i]["insert"]);
}
entryContent = entryContentInput.join("").toString();
}
}
return GestureDetector(
onLongPress: () async {
final userID = await getUid();
if (usersController.userIsModerator(
userID: userID,
guestbook:
guestbooksController.currentGuestbook.value) ==
true) {
_userEntryOptions(entry);
}
},
child: GuestListTile(
entry: entry,
description: entryContent != null &&
entryContent.isNotEmpty &&
entryContent != "\n"
? entryContent
: tr("no_content"),
owner: entry.owner,
hasImages: entry.images != null && entry.images.isNotEmpty,
hasVideos: entry.videos != null && entry.videos.isNotEmpty,
hasPainting: entry.paintingImage != null &&
entry.paintingImage.isNotEmpty,
),
);
} else {
return Container();
}
});
} else {
return Container();
}
}
void _userEntryOptions(Entry entry) {
Get.bottomSheet(CupertinoActionSheet(
actions: [
CupertinoActionSheetAction(
onPressed: () {
Get.back();
_deleteUserEntryConfirmation(entry);
},
child: Text("delete_this_entry").tr(),
isDestructiveAction: true,
)
],
));
}
void _deleteUserEntryConfirmation(Entry entry) {
Get.dialog(CupertinoAlertDialog(
title: Text("delete_my_entry_desc").tr(),
actions: [
CupertinoDialogAction(
child: Text("yes_delete_entry").tr(),
isDestructiveAction: true,
onPressed: () {
Get.back();
_deleteUserEntry(entry);
},
),
CupertinoDialogAction(
child: Text("no_not_delete_entry").tr(),
onPressed: () {
Get.back();
},
)
],
));
}
void _deleteUserEntry(Entry entry) async {
await entriesController.deleteEntireUserEntry(entry);
await MySnackbar()
.createSnackbar(message: tr("entry_deleted_successfully"), seconds: 2);
}
}
顺便说一下:我正在使用getx包进行状态管理。这就是为什么我可以使用Stateless
widget的原因。