perf(mobile): skip unchanged message footer layout

Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
Tom Brow
2026-10-18 12:32:57 -07:00
parent 27b9c827fc
commit 06c0bb0d1b
2 changed files with 66 additions and 5 deletions

View File

@@ -54,9 +54,7 @@ class _FooterLayout extends MultiChildRenderObjectWidget {
BuildContext context,
covariant RenderBox renderObject,
) {
(renderObject as _RenderMessageFooter)
..overlap = overlap
..markNeedsLayout();
(renderObject as _RenderMessageFooter).overlap = overlap;
}
}
@@ -70,9 +68,15 @@ class _RenderMessageFooter extends RenderBox
RenderBox,
ContainerBoxParentData<RenderBox>
> {
_RenderMessageFooter(this.overlap);
_RenderMessageFooter(this._overlap);
double overlap;
double _overlap;
double get overlap => _overlap;
set overlap(double value) {
if (_overlap == value) return;
_overlap = value;
markNeedsLayout();
}
@override
void setupParentData(RenderBox child) {

View File

@@ -1,8 +1,47 @@
import 'package:buzz/features/channels/message_footer_layout.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('unchanged footer geometry does not relayout its timeline row', (
tester,
) async {
var layouts = 0;
final overlap = ValueNotifier(8.0);
addTearDown(overlap.dispose);
await tester.pumpWidget(
MaterialApp(
home: Align(
alignment: Alignment.topLeft,
child: _LayoutCounter(
onLayout: () => layouts++,
child: ValueListenableBuilder(
valueListenable: overlap,
builder: (context, value, child) => MessageFooterLayout(
overlap: value,
footer: const SizedBox(width: 100, height: 32),
child: const SizedBox(width: 200, height: 60),
),
),
),
),
),
);
final initialLayouts = layouts;
tester.element(find.byType(MessageFooterLayout)).markNeedsBuild();
await tester.pump();
expect(
layouts,
initialLayouts,
reason: 'Rebuilding unchanged reactions must not dirty the row layout',
);
overlap.value = 12;
await tester.pump();
expect(layouts, greaterThan(initialLayouts));
expect(tester.getSize(find.byType(MessageFooterLayout)).height, 80);
});
testWidgets('footer global transforms and semantics include overlap offset', (
tester,
) async {
@@ -118,3 +157,21 @@ void main() {
},
);
}
class _LayoutCounter extends SingleChildRenderObjectWidget {
const _LayoutCounter({required this.onLayout, required super.child});
final VoidCallback onLayout;
@override
RenderObject createRenderObject(BuildContext context) =>
_CountLayouts(onLayout);
}
class _CountLayouts extends RenderProxyBox {
_CountLayouts(this.onLayout);
final VoidCallback onLayout;
@override
void performLayout() {
onLayout();
super.performLayout();
}
}