Unverified Commit 8a37b8ba authored by Tae Hyung Kim's avatar Tae Hyung Kim Committed by GitHub

Optimize SliverMainAxisGroup/SliverCrossAxisGroup paint function (#129310)

This PR changes the paint functions for SliverMainAxisGroup and SliverCrossAxisGroup so that only visible slivers are painted.

Fixes https://github.com/flutter/flutter/issues/129214.
parent c65cab8f
......@@ -129,8 +129,10 @@ class RenderSliverCrossAxisGroup extends RenderSliver with ContainerRenderObject
RenderSliver? child = firstChild;
while (child != null) {
final SliverPhysicalParentData childParentData = child.parentData! as SliverPhysicalParentData;
context.paintChild(child, offset + childParentData.paintOffset);
if (child.geometry!.visible) {
final SliverPhysicalParentData childParentData = child.parentData! as SliverPhysicalParentData;
context.paintChild(child, offset + childParentData.paintOffset);
}
child = childAfter(child);
}
}
......@@ -294,8 +296,10 @@ class RenderSliverMainAxisGroup extends RenderSliver with ContainerRenderObjectM
RenderSliver? child = lastChild;
while (child != null) {
final SliverPhysicalParentData childParentData = child.parentData! as SliverPhysicalParentData;
context.paintChild(child, offset + childParentData.paintOffset);
if (child.geometry!.visible) {
final SliverPhysicalParentData childParentData = child.parentData! as SliverPhysicalParentData;
context.paintChild(child, offset + childParentData.paintOffset);
}
child = childBefore(child);
}
}
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test sliver which always attempts to paint itself whether it is visible or not.
// Use for checking if slivers which take sliver children paints optimally.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class RenderMockSliverToBoxAdapter extends RenderSliverToBoxAdapter {
RenderMockSliverToBoxAdapter({
super.child,
required this.incrementCounter,
});
final void Function() incrementCounter;
@override
void paint(PaintingContext context, Offset offset) {
incrementCounter();
}
}
class MockSliverToBoxAdapter extends SingleChildRenderObjectWidget {
/// Creates a sliver that contains a single box widget.
const MockSliverToBoxAdapter({
super.key,
super.child,
required this.incrementCounter,
});
final void Function() incrementCounter;
@override
RenderMockSliverToBoxAdapter createRenderObject(BuildContext context) =>
RenderMockSliverToBoxAdapter(incrementCounter: incrementCounter);
}
......@@ -6,6 +6,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/sliver_utils.dart';
const double VIEWPORT_HEIGHT = 600;
const double VIEWPORT_WIDTH = 300;
......@@ -806,8 +809,63 @@ void main() {
// If renderHeader._lastStartedScrollDirection is not ScrollDirection.forward, then we shouldn't see the header at all.
expect((renderHeader.parentData! as SliverPhysicalParentData).paintOffset.dy, equals(0.0));
});
testWidgets('SliverCrossAxisGroup skips painting invisible children', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
int counter = 0;
void incrementCounter() {
counter += 1;
}
await tester.pumpWidget(
_buildSliverCrossAxisGroup(
controller: controller,
slivers: <Widget>[
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 1000,
decoration: const BoxDecoration(color: Colors.amber),
),
),
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 400,
decoration: const BoxDecoration(color: Colors.amber)
),
),
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 500,
decoration: const BoxDecoration(color: Colors.amber)
),
),
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 300,
decoration: const BoxDecoration(color: Colors.amber)
),
),
],
),
);
expect(counter, equals(4));
// Reset paint counter.
counter = 0;
controller.jumpTo(400);
await tester.pumpAndSettle();
expect(controller.offset, 400);
expect(counter, equals(2));
});
}
Widget _buildSliverList({
double itemMainAxisExtent = 100,
List<int> items = const <int>[],
......
......@@ -6,6 +6,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/sliver_utils.dart';
const double VIEWPORT_HEIGHT = 600;
const double VIEWPORT_WIDTH = 300;
......@@ -604,6 +607,63 @@ void main() {
expect(renderHeader.geometry!.paintExtent, equals(60.0));
expect((renderHeader.parentData! as SliverPhysicalParentData).paintOffset.dy, equals(-50.0));
});
testWidgets('SliverMainAxisGroup skips painting invisible children', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
int counter = 0;
void incrementCounter() {
counter += 1;
}
await tester.pumpWidget(
_buildSliverMainAxisGroup(
controller: controller,
slivers: <Widget>[
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 1000,
decoration: const BoxDecoration(color: Colors.amber),
),
),
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 400,
decoration: const BoxDecoration(color: Colors.amber)
),
),
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 500,
decoration: const BoxDecoration(color: Colors.amber)
),
),
MockSliverToBoxAdapter(
incrementCounter: incrementCounter,
child: Container(
height: 300,
decoration: const BoxDecoration(color: Colors.amber)
),
),
],
),
);
// Can only see top sliver.
expect(counter, equals(1));
// Reset paint counter.
counter = 0;
controller.jumpTo(1000);
await tester.pumpAndSettle();
// Can only see second and third slivers.
expect(controller.offset, 1000);
expect(counter, equals(2));
});
}
Widget _buildSliverList({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment