188 lines
6.4 KiB
Dart
188 lines
6.4 KiB
Dart
// 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.
|
||
|
||
import 'package:flutter/rendering.dart';
|
||
import '../flutter_test_alternative.dart';
|
||
|
||
import 'rendering_tester.dart';
|
||
|
||
void main() {
|
||
test('RenderSliverFixedExtentList layout test - rounding error', () {
|
||
final List<RenderBox> children = <RenderBox>[
|
||
RenderSizedBox(const Size(400.0, 100.0)),
|
||
RenderSizedBox(const Size(400.0, 100.0)),
|
||
RenderSizedBox(const Size(400.0, 100.0)),
|
||
];
|
||
final TestRenderSliverBoxChildManager childManager = TestRenderSliverBoxChildManager(
|
||
children: children,
|
||
);
|
||
final RenderViewport root = RenderViewport(
|
||
axisDirection: AxisDirection.down,
|
||
crossAxisDirection: AxisDirection.right,
|
||
offset: ViewportOffset.zero(),
|
||
cacheExtent: 0,
|
||
children: <RenderSliver>[
|
||
childManager.createRenderSliverFillViewport(),
|
||
],
|
||
);
|
||
layout(root);
|
||
expect(children[0].attached, true);
|
||
expect(children[1].attached, false);
|
||
|
||
root.offset = ViewportOffset.fixed(600);
|
||
pumpFrame();
|
||
expect(children[0].attached, false);
|
||
expect(children[1].attached, true);
|
||
|
||
// Simulate double precision error.
|
||
root.offset = ViewportOffset.fixed(1199.999999999998);
|
||
pumpFrame();
|
||
expect(children[1].attached, false);
|
||
expect(children[2].attached, true);
|
||
});
|
||
|
||
group('getMaxChildIndexForScrollOffset', () {
|
||
// Regression test for https://github.com/flutter/flutter/issues/68182
|
||
|
||
const double genericItemExtent = 600.0;
|
||
const double extraValueToNotHaveRoundingIssues = 0.0000001; // 6 zeros
|
||
const double extraValueToHaveRoundingIssues = 0.00000001; // 7 zeros
|
||
|
||
test('should be 0 when item extent is 0', () {
|
||
const double offsetValueWhichDoesntCare = 1234;
|
||
final int actual = testGetMaxChildIndexForScrollOffset(offsetValueWhichDoesntCare, 0);
|
||
expect(actual, 0);
|
||
});
|
||
|
||
test('should be 0 when offset is 0', () {
|
||
final int actual = testGetMaxChildIndexForScrollOffset(0, genericItemExtent);
|
||
expect(actual, 0);
|
||
});
|
||
|
||
test('should be 0 when offset is equal to item extent', () {
|
||
final int actual = testGetMaxChildIndexForScrollOffset(genericItemExtent, genericItemExtent);
|
||
expect(actual, 0);
|
||
});
|
||
|
||
test('should be 1 when offset is greater than item extent', () {
|
||
final int actual = testGetMaxChildIndexForScrollOffset(
|
||
genericItemExtent + 1, genericItemExtent);
|
||
expect(actual, 1);
|
||
});
|
||
|
||
test('should be 1 when offset is slightly greater than item extent', () {
|
||
final int actual = testGetMaxChildIndexForScrollOffset(
|
||
genericItemExtent + extraValueToNotHaveRoundingIssues, genericItemExtent);
|
||
expect(actual, 1);
|
||
});
|
||
|
||
test('should be 4 when offset is four times and a half greater than item extent', () {
|
||
final int actual = testGetMaxChildIndexForScrollOffset(
|
||
genericItemExtent * 4.5, genericItemExtent);
|
||
expect(actual, 4);
|
||
});
|
||
|
||
test('should be 5 when offset is 6 times greater than item extent', () {
|
||
const double anotherGenericItemExtent = 414.0;
|
||
final int actual = testGetMaxChildIndexForScrollOffset(
|
||
anotherGenericItemExtent * 6, anotherGenericItemExtent);
|
||
expect(actual, 5);
|
||
});
|
||
|
||
test('should be 5 when offset is 6 times greater than a specific item extent where the division will return more than 13 zero decimals', () {
|
||
const double itemExtentSpecificForAProblematicSreenSize = 411.42857142857144;
|
||
final int actual = testGetMaxChildIndexForScrollOffset(
|
||
itemExtentSpecificForAProblematicSreenSize * 6 + extraValueToHaveRoundingIssues,
|
||
itemExtentSpecificForAProblematicSreenSize);
|
||
expect(actual, 5);
|
||
});
|
||
|
||
test('should be 0 when offset is 0.00000001 times greater than item extent where the divison will return more than 13 zero decimals', () {
|
||
final int actual = testGetMaxChildIndexForScrollOffset(
|
||
genericItemExtent + extraValueToHaveRoundingIssues, genericItemExtent);
|
||
expect(actual, 0);
|
||
});
|
||
});
|
||
}
|
||
|
||
int testGetMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
|
||
final TestRenderSliverFixedExtentBoxAdaptor renderSliver = TestRenderSliverFixedExtentBoxAdaptor();
|
||
return renderSliver.getMaxChildIndexForScrollOffset(scrollOffset, itemExtent);
|
||
}
|
||
|
||
class TestRenderSliverBoxChildManager extends RenderSliverBoxChildManager {
|
||
TestRenderSliverBoxChildManager({
|
||
required this.children,
|
||
});
|
||
|
||
RenderSliverMultiBoxAdaptor? _renderObject;
|
||
List<RenderBox> children;
|
||
|
||
RenderSliverFillViewport createRenderSliverFillViewport() {
|
||
assert(_renderObject == null);
|
||
_renderObject = RenderSliverFillViewport(
|
||
childManager: this,
|
||
);
|
||
return _renderObject! as RenderSliverFillViewport;
|
||
}
|
||
|
||
int? _currentlyUpdatingChildIndex;
|
||
|
||
@override
|
||
void createChild(int index, { required RenderBox? after }) {
|
||
if (index < 0 || index >= children.length)
|
||
return;
|
||
try {
|
||
_currentlyUpdatingChildIndex = index;
|
||
_renderObject!.insert(children[index], after: after);
|
||
} finally {
|
||
_currentlyUpdatingChildIndex = null;
|
||
}
|
||
}
|
||
|
||
@override
|
||
void removeChild(RenderBox child) {
|
||
_renderObject!.remove(child);
|
||
}
|
||
|
||
@override
|
||
double estimateMaxScrollOffset(
|
||
SliverConstraints constraints, {
|
||
int? firstIndex,
|
||
int? lastIndex,
|
||
double? leadingScrollOffset,
|
||
double? trailingScrollOffset,
|
||
}) {
|
||
assert(lastIndex! >= firstIndex!);
|
||
return children.length * (trailingScrollOffset! - leadingScrollOffset!) / (lastIndex! - firstIndex! + 1);
|
||
}
|
||
|
||
@override
|
||
int get childCount => children.length;
|
||
|
||
@override
|
||
void didAdoptChild(RenderBox child) {
|
||
assert(_currentlyUpdatingChildIndex != null);
|
||
final SliverMultiBoxAdaptorParentData childParentData = child.parentData! as SliverMultiBoxAdaptorParentData;
|
||
childParentData.index = _currentlyUpdatingChildIndex;
|
||
}
|
||
|
||
@override
|
||
void setDidUnderflow(bool value) { }
|
||
}
|
||
|
||
class TestRenderSliverFixedExtentBoxAdaptor extends RenderSliverFixedExtentBoxAdaptor {
|
||
TestRenderSliverFixedExtentBoxAdaptor()
|
||
:super(childManager: TestRenderSliverBoxChildManager(children: <RenderBox>[]));
|
||
|
||
@override
|
||
// ignore: unnecessary_overrides
|
||
int getMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
|
||
return super.getMaxChildIndexForScrollOffset(scrollOffset, itemExtent);
|
||
}
|
||
|
||
@override
|
||
double get itemExtent => throw UnimplementedError();
|
||
}
|