Tooltip unusable when row reordering is enabled
Let's say I added a button with tooltip inside the row it throws this exception:
Click to expand the exception
════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during paint():
'package:material_table_view/src/table_painting_context.dart': Failed assertion: line 84 pos 12: '!child.needsCompositing': is not true.
The relevant error-causing widget was:
TableSection TableSection:file:///C:/Users/???/Desktop/Github/material_table_view2/lib/src/table_view.dart:521:12
When the exception was thrown, this was the stack:
#2 TablePaintingContext.paintChild (package:material_table_view/src/table_painting_context.dart:84:12)
table_painting_context.dart:84
#3 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:13)
proxy_box.dart:140
#4 RenderTableSection._customPaint (package:material_table_view/src/render_table_section.dart:325:11)
render_table_section.dart:325
#5 RenderTableSection.paint (package:material_table_view/src/render_table_section.dart:255:60)
render_table_section.dart:255
#6 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3423:7)
object.dart:3423
#7 PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:176:11)
object.dart:176
#8 PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:121:5)
object.dart:121
#9 PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:1309:31)
object.dart:1309
#10 PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:1319:15)
object.dart:1319
#11 RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:631:23)
binding.dart:631
#12 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:1261:13)
binding.dart:1261
#13 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:495:5)
binding.dart:495
#14 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1434:15)
binding.dart:1434
#15 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1347:9)
binding.dart:1347
#16 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1200:5)
binding.dart:1200
#17 _invoke (dart:ui/hooks.dart:330:13)
hooks.dart:330
#18 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:444:5)
platform_dispatcher.dart:444
#19 _drawFrame (dart:ui/hooks.dart:302:31)
hooks.dart:302
(elided 2 frames from class _AssertionError)
The following RenderObject was being processed when the exception was fired: RenderTableSection#3dd5a relayoutBoundary=up15
needs compositing
parentData: <none> (can use size)
constraints: BoxConstraints(0.0<=w<=1264.0, 0.0<=h<=495.0)
layer: OffsetLayer#0ac33
engine layer: OffsetEngineLayer#32223
handles: 2
offset: Offset(0.0, 0.0)
size: Size(1264.0, 495.0)
child: _RenderTheater#bc61e relayoutBoundary=up16 NEEDS-PAINT
needs compositing
parentData: <none> (can use size)
constraints: BoxConstraints(0.0<=w<=1264.0, 0.0<=h<=495.0)
size: Size(1264.0, 495.0)
skipCount: 0
textDirection: ltr
onstage 1: _RenderTableViewport#055cb NEEDS-PAINT
parentData: not positioned; offset=Offset(0.0, 0.0) (can use size)
constraints: BoxConstraints(w=1264.0, h=495.0)
size: Size(1264.0, 495.0)
axisDirection: down
crossAxisDirection: right
offset: ScrollPositionWithSingleContext#9c163(offset: 0.0, range: 0.0..85899345395.0, viewport: 495.0, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#2334b, ScrollDirection.idle)
anchor: 0.0
center child: RenderSliverPadding#25416 relayoutBoundary=up1 NEEDS-PAINT
parentData: paintOffset=Offset(0.0, 0.0) (can use size)
constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle, scrollOffset: 0.0, precedingScrollExtent: 0.0, remainingPaintExtent: 495.0, crossAxisExtent: 1264.0, crossAxisDirection: AxisDirection.right, viewportMainAxisExtent: 495.0, remainingCacheExtent: 745.0, cacheOrigin: 0.0)
geometry: SliverGeometry(scrollExtent: 85899345890.0, paintExtent: 495.0, maxPaintExtent: 85899345890.0, hasVisualOverflow: true, cacheExtent: 745.0)
scrollExtent: 85899345890.0
paintExtent: 495.0
maxPaintExtent: 85899345890.0
hasVisualOverflow: true
cacheExtent: 745.0
padding: EdgeInsets(0.0, 0.0, 0.0, 10.0)
textDirection: ltr
child: RenderSliverFixedExtentList#38b6b relayoutBoundary=up2 NEEDS-PAINT
parentData: paintOffset=Offset(0.0, 0.0) (can use size)
constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle, scrollOffset: 0.0, precedingScrollExtent: 0.0, remainingPaintExtent: 495.0, crossAxisExtent: 1264.0, crossAxisDirection: AxisDirection.right, viewportMainAxisExtent: 495.0, remainingCacheExtent: 745.0, cacheOrigin: 0.0)
geometry: SliverGeometry(scrollExtent: 85899345880.0, paintExtent: 495.0, maxPaintExtent: 85899345880.0, hasVisualOverflow: true, cacheExtent: 745.0)
scrollExtent: 85899345880.0
paintExtent: 495.0
maxPaintExtent: 85899345880.0
hasVisualOverflow: true
cacheExtent: 745.0
currently live children: 0 to 18
onstage 1 - 1: _RenderDeferredLayoutBox#d58c8 NEEDS-PAINT
needs compositing
parentData: not positioned; offset=Offset(0.0, 0.0)
constraints: BoxConstraints(w=1264.0, h=495.0)
size: Size(1264.0, 495.0)
child: RenderCustomSingleChildLayoutBox#23783 NEEDS-PAINT
needs compositing
parentData: top=0.0; right=0.0; bottom=0.0; left=0.0; offset=Offset(0.0, 0.0) (can use size)
constraints: BoxConstraints(w=1264.0, h=495.0)
size: Size(1264.0, 495.0)
child: RenderIgnorePointer#b2d83 relayoutBoundary=up1 NEEDS-PAINT
needs compositing
parentData: offset=Offset(1173.4, 284.0) (can use size)
constraints: BoxConstraints(0.0<=w<=1264.0, 0.0<=h<=495.0)
size: Size(80.6, 25.0)
ignoring: true
ignoringSemantics: null
no offstage children
RenderObject: RenderTableSection#3dd5a relayoutBoundary=up15
needs compositing
parentData: <none> (can use size)
constraints: BoxConstraints(0.0<=w<=1264.0, 0.0<=h<=495.0)
layer: OffsetLayer#0ac33
engine layer: OffsetEngineLayer#32223
handles: 2
offset: Offset(0.0, 0.0)
size: Size(1264.0, 495.0)
child: _RenderTheater#bc61e relayoutBoundary=up16 NEEDS-PAINT
needs compositing
parentData: <none> (can use size)
constraints: BoxConstraints(0.0<=w<=1264.0, 0.0<=h<=495.0)
size: Size(1264.0, 495.0)
skipCount: 0
textDirection: ltr
onstage 1: _RenderTableViewport#055cb NEEDS-PAINT
parentData: not positioned; offset=Offset(0.0, 0.0) (can use size)
constraints: BoxConstraints(w=1264.0, h=495.0)
size: Size(1264.0, 495.0)
axisDirection: down
crossAxisDirection: right
offset: ScrollPositionWithSingleContext#9c163(offset: 0.0, range: 0.0..85899345395.0, viewport: 495.0, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#2334b, ScrollDirection.idle)
anchor: 0.0
center child: RenderSliverPadding#25416 relayoutBoundary=up1 NEEDS-PAINT
parentData: paintOffset=Offset(0.0, 0.0) (can use size)
constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle, scrollOffset: 0.0, precedingScrollExtent: 0.0, remainingPaintExtent: 495.0, crossAxisExtent: 1264.0, crossAxisDirection: AxisDirection.right, viewportMainAxisExtent: 495.0, remainingCacheExtent: 745.0, cacheOrigin: 0.0)
geometry: SliverGeometry(scrollExtent: 85899345890.0, paintExtent: 495.0, maxPaintExtent: 85899345890.0, hasVisualOverflow: true, cacheExtent: 745.0)
scrollExtent: 85899345890.0
paintExtent: 495.0
maxPaintExtent: 85899345890.0
hasVisualOverflow: true
cacheExtent: 745.0
padding: EdgeInsets(0.0, 0.0, 0.0, 10.0)
textDirection: ltr
child: RenderSliverFixedExtentList#38b6b relayoutBoundary=up2 NEEDS-PAINT
parentData: paintOffset=Offset(0.0, 0.0) (can use size)
constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle, scrollOffset: 0.0, precedingScrollExtent: 0.0, remainingPaintExtent: 495.0, crossAxisExtent: 1264.0, crossAxisDirection: AxisDirection.right, viewportMainAxisExtent: 495.0, remainingCacheExtent: 745.0, cacheOrigin: 0.0)
geometry: SliverGeometry(scrollExtent: 85899345880.0, paintExtent: 495.0, maxPaintExtent: 85899345880.0, hasVisualOverflow: true, cacheExtent: 745.0)
scrollExtent: 85899345880.0
paintExtent: 495.0
maxPaintExtent: 85899345880.0
hasVisualOverflow: true
cacheExtent: 745.0
currently live children: 0 to 18
onstage 1 - 1: _RenderDeferredLayoutBox#d58c8 NEEDS-PAINT
needs compositing
parentData: not positioned; offset=Offset(0.0, 0.0)
constraints: BoxConstraints(w=1264.0, h=495.0)
size: Size(1264.0, 495.0)
child: RenderCustomSingleChildLayoutBox#23783 NEEDS-PAINT
needs compositing
parentData: top=0.0; right=0.0; bottom=0.0; left=0.0; offset=Offset(0.0, 0.0) (can use size)
constraints: BoxConstraints(w=1264.0, h=495.0)
size: Size(1264.0, 495.0)
child: RenderIgnorePointer#b2d83 relayoutBoundary=up1 NEEDS-PAINT
needs compositing
parentData: offset=Offset(1173.4, 284.0) (can use size)
constraints: BoxConstraints(0.0<=w<=1264.0, 0.0<=h<=495.0)
size: Size(80.6, 25.0)
ignoring: true
ignoringSemantics: null
no offstage children
════════════════════════════════════════════════════════════════════════════════
Then after all that it gets stuck in exception
_AssertionError ('package:flutter/src/rendering/layer.dart': Failed assertion: line 894 pos 12: 'picture != null': is not true.)
How to do it is editing the main.dart in examples From line 597 in createRowBuilder method Like this
/// Creates [TableRowBuilder] closure.
TableRowBuilder createRowBuilder(
BuildContext context,
bool doExpansion, [
int start = 0,
]) {
final theme = Theme.of(context);
final rowTextStyle = Theme.of(context).textTheme.bodyMedium;
final cellPadding = stylingController.useRTL.value
? const EdgeInsets.only(right: 8.0)
: const EdgeInsets.only(left: 8.0);
final cellAlignment = stylingController.useRTL.value
? Alignment.centerRight
: Alignment.centerLeft;
// this can be freely inlined instead
return (context, row, TableRowContentBuilder contentBuilder) {
row += start;
if (stylingController.doPlaceholders.value &&
(row + placeholderOffsetIndex) % 99 < 33) {
return null; // show off the placeholder
}
final selected = selection.contains(row);
final textStyle = selected
? rowTextStyle?.copyWith(color: theme.colorScheme.onPrimaryContainer)
: rowTextStyle;
var cellBuilder = (BuildContext context, int column) {
switch (columns[column].index) {
case 0:
return Checkbox(
value: selection.contains(row),
onChanged: (value) => setState(() => (value ?? false)
? selection.add(row)
: selection.remove(row)));
case -1:
return Center(
child: IconButton(
icon: Icon(
Icons.more_vert,
),
tooltip: 'You will not see me!',
onPressed: () => print('Hello'),
),
);
default:
return Padding(
padding: cellPadding,
child: Align(
alignment: cellAlignment,
child: Text(
'${(row + 2) * columns[column].index}',
style: textStyle,
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: false,
),
),
);
}
};
if (stylingController.statefulRandomBackground.value) {
final builder = cellBuilder;
cellBuilder = (context, column) =>
StatefulRandomBackground(child: builder(context, column));
}
// this is going to be our content
var content = contentBuilder(context, cellBuilder);
if (doExpansion) {
// build expandable row
content = ExpandableTableRow(
vsync: this,
duration: Duration(milliseconds: 200),
expanded: selected,
expandedChild: SizedBox(
height: _rowHeight,
child: contentBuilder(
context,
(context, column) {
switch (columns[column].index) {
case 0:
case -1:
return SizedBox();
default:
return Padding(
padding: cellPadding,
child: Align(
alignment: cellAlignment,
child: Text(
'${sqrt((row + 2) * columns[column].index)}',
style: textStyle,
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: false,
),
),
);
}
},
),
),
child: SizedBox(
height: _rowHeight,
child: content,
),
);
}
return _wrapRow(
row,
AnimatedContainer(
duration: const Duration(milliseconds: 200),
color:
theme.colorScheme.primaryContainer.withAlpha(selected ? 0xFF : 0),
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => setState(() {
selection.clear();
selection.add(row);
}),
child: content,
),
),
),
);
};
}
Latest news, I found in table_painting_context.dart in method paintChild line 82
The method is doing a assert for
assert(!child.needsCompositing);
So I changed it from this
@override
void paintChild(RenderObject child, Offset offset) {
assert(!child.isRepaintBoundary);
assert(!child.needsCompositing);
super.paintChild(child, offset);
}
To this
@override
void paintChild(RenderObject child, Offset offset) {
assert(!child.isRepaintBoundary);
if (child.needsCompositing) {
regular.fixed.paintChild(child, offset);
} else {
super.paintChild(child, offset);
}
}
I'm sure there could be another issues with removing that assert but now it's working fine.
I see your problem. Whenever row reordering feature is used the library inserts TableSectionOverlay between TableSection and the row widgets. Whenever tooltip is to be shown this overlay rather mistakenly gets used instead of the default one, making it require compositing. As per design described in the README this segment of the tree must remain free of such things.
I'm concerned about the solution you proposed undermining that strict rule. Even though it might work for your case, it would not be ideal for the project and all of its use cases.
I'll see if I can spare some time on investigating other potential solutions. Let me know if I've missed anything.