feat(ui): update scrollable_positioned_list with the latest changes.
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
+33
-4
@@ -2,13 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
const screenHeight = 400.0;
|
||||
const screenHeight = 100.0;
|
||||
const screenWidth = 400.0;
|
||||
const itemWidth = screenWidth / 10.0;
|
||||
const itemCount = 500;
|
||||
@@ -46,6 +45,11 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
final fadeTransitionFinder = find.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition),
|
||||
);
|
||||
|
||||
testWidgets('List positioned with 0 at left', (WidgetTester tester) async {
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener);
|
||||
@@ -172,7 +176,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 100')).dx, 0);
|
||||
expect(tester.getBottomRight(find.text('Item 109')).dy, screenWidth);
|
||||
expect(tester.getBottomRight(find.text('Item 109')).dy, screenHeight);
|
||||
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
@@ -196,6 +200,31 @@ void main() {
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('Scroll to 20 without fading', (WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener);
|
||||
|
||||
var fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
final initialOpacity = fadeTransition.opacity;
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 20, duration: scrollDuration));
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity, initialOpacity);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Item 14'), findsNothing);
|
||||
expect(find.text('Item 20'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('padding test - centered sliver at left',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
|
||||
+51
@@ -360,4 +360,55 @@ void main() {
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('Does not crash when updated offscreen',
|
||||
(WidgetTester tester) async {
|
||||
late StateSetter setState;
|
||||
var updated = false;
|
||||
|
||||
// There's 0 relayout boundaries in this subtree.
|
||||
final widget = StatefulBuilder(builder: (context, stateSetter) {
|
||||
setState = stateSetter;
|
||||
return Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: PositionedList(
|
||||
shrinkWrap: true,
|
||||
itemCount: 1,
|
||||
// When `updated` becomes true this line inserts a
|
||||
// RenderIndexedSemantics to the render tree.
|
||||
addSemanticIndexes: updated,
|
||||
itemBuilder: (context, index) => const SizedBox(height: itemHeight),
|
||||
));
|
||||
});
|
||||
|
||||
await tester.pumpWidget(Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Overlay(
|
||||
initialEntries: [
|
||||
OverlayEntry(builder: (context) => widget, maintainState: true),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
// Insert a new opaque OverlayEntry that would prevent the first OverlayEntry
|
||||
// from doing re-layout. Since there's no relayout boundaries in the first
|
||||
// OverlayEntry, no dirty RenderObjects in its render subtree can update
|
||||
// layout.
|
||||
final newOverlay =
|
||||
OverlayEntry(builder: (context) => const SizedBox.expand(), opaque: true);
|
||||
tester.state<OverlayState>(find.byType(Overlay)).insert(newOverlay);
|
||||
await tester.pump();
|
||||
|
||||
// Update the list item's render tree. A new RenderObjectElement is
|
||||
// inflated, registeredElement.renderObject will point to this new
|
||||
// RenderObjectElement's RenderObject (RenderIndexedSemantics), which has
|
||||
// never been laid out.
|
||||
setState(() {
|
||||
updated = true;
|
||||
});
|
||||
|
||||
await tester.pump();
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
|
||||
+1
-2
@@ -2,10 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
const screenHeight = 400.0;
|
||||
|
||||
+192
-193
@@ -2,12 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart';
|
||||
|
||||
@@ -48,8 +48,7 @@ void main() {
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1,
|
||||
'''index needs to be bigger or equal to 0 and smallert than itemCount -1''');
|
||||
assert(index >= 0 && index <= itemCount - 1);
|
||||
return SizedBox(
|
||||
height:
|
||||
variableHeight ? (itemHeight + (index % 13) * 5) : itemHeight,
|
||||
@@ -71,6 +70,11 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
final fadeTransitionFinder = find.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition),
|
||||
);
|
||||
|
||||
testWidgets('List positioned with 0 at top', (WidgetTester tester) async {
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener);
|
||||
@@ -394,11 +398,7 @@ void main() {
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener);
|
||||
|
||||
var fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
var fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
final initialOpacity = fadeTransition.opacity;
|
||||
|
||||
unawaited(
|
||||
@@ -407,11 +407,7 @@ void main() {
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity, initialOpacity);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -456,10 +452,6 @@ void main() {
|
||||
final itemScrollController = ItemScrollController();
|
||||
await setUpWidgetTest(tester, itemScrollController: itemScrollController);
|
||||
|
||||
final fadeTransitionFinder = find.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition));
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 100, duration: scrollDuration));
|
||||
await tester.pump();
|
||||
@@ -533,26 +525,14 @@ void main() {
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
expect(
|
||||
tester
|
||||
.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last)
|
||||
.opacity
|
||||
.value,
|
||||
closeTo(0, 0.01));
|
||||
tester.widget<FadeTransition>(fadeTransitionFinder.last).opacity.value,
|
||||
closeTo(0, 0.01),
|
||||
);
|
||||
await tester.pump(scrollDuration + scrollDurationTolerance);
|
||||
expect(
|
||||
tester
|
||||
.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last)
|
||||
.opacity
|
||||
.value,
|
||||
closeTo(1, 0.01));
|
||||
tester.widget<FadeTransition>(fadeTransitionFinder.last).opacity.value,
|
||||
closeTo(1, 0.01),
|
||||
);
|
||||
|
||||
expect(find.text('Item 0'), findsOneWidget);
|
||||
expect(tester.getTopLeft(find.text('Item 0')).dy, 0);
|
||||
@@ -610,15 +590,9 @@ void main() {
|
||||
expect(tester.getTopLeft(find.text('Item 10')).dy, 0);
|
||||
expect(tester.getBottomLeft(find.text('Item 19')).dy, screenHeight);
|
||||
expect(
|
||||
tester
|
||||
.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last)
|
||||
.opacity
|
||||
.value,
|
||||
closeTo(0.5, 0.01));
|
||||
tester.widget<FadeTransition>(fadeTransitionFinder.last).opacity.value,
|
||||
closeTo(0.5, 0.01),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
@@ -899,11 +873,7 @@ void main() {
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 9')).dy, 0);
|
||||
final fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
final fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity.value, 1.0);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -923,21 +893,12 @@ void main() {
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 10')).dy, 0);
|
||||
final fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
final fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity.value, 1.0);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
final fadeTransitionFinder = find.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition),
|
||||
);
|
||||
|
||||
testWidgets('Scroll to 0 stop before half way', (WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
await setUpWidgetTest(tester, itemScrollController: itemScrollController);
|
||||
@@ -1022,14 +983,13 @@ void main() {
|
||||
itemScrollController.scrollTo(index: 0, duration: scrollDuration));
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2 + scrollDuration ~/ 20);
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
await tester.tap(find.byType(ScrollablePositionedList));
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 9')).dy, closeTo(0, tolerance));
|
||||
final fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity.value, 1.0);
|
||||
expect(tester.getTopLeft(find.text('Item 90')).dy, 0);
|
||||
expect(fadeTransitionFinder, findsNWidgets(1));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
@@ -1098,6 +1058,34 @@ void main() {
|
||||
expect(find.text('Item 100'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets("Second scroll future doesn't complete until scroll is done",
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
await setUpWidgetTest(tester, itemScrollController: itemScrollController);
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 100, duration: scrollDuration));
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
final scrollFuture2 =
|
||||
itemScrollController.scrollTo(index: 250, duration: scrollDuration);
|
||||
|
||||
var futureComplete = false;
|
||||
unawaited(scrollFuture2.then((_) => futureComplete = true));
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
expect(futureComplete, isFalse);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(futureComplete, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('Scroll to 250, scroll to 100, scroll to 0 half way',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
@@ -1145,7 +1133,7 @@ void main() {
|
||||
}, skip: true);
|
||||
|
||||
testWidgets(
|
||||
'''Jump to 400 at bottom, manually scroll, scroll to 100 at bottom and back''',
|
||||
'Jump to 400 at bottom, manually scroll, scroll to 100 at bottom and back',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
@@ -1664,7 +1652,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'''Maintain programmatic and user position (9 half way off top) in page view''',
|
||||
'Maintain programmatic and user position (9 half way off top) in page view',
|
||||
(WidgetTester tester) async {
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
final itemScrollController = ItemScrollController();
|
||||
@@ -1751,21 +1739,21 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<int>(
|
||||
valueListenable: itemCount,
|
||||
builder: (context, itemCount, child) =>
|
||||
ScrollablePositionedList.builder(
|
||||
initialScrollIndex: min(100, itemCount),
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1,
|
||||
'index not bigger than 0 and smaller than itemCount - 1');
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
builder: (context, itemCount, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
initialScrollIndex: min(100, itemCount),
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1);
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1795,19 +1783,19 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<int>(
|
||||
valueListenable: itemCount,
|
||||
builder: (context, itemCount, child) =>
|
||||
ScrollablePositionedList.builder(
|
||||
initialScrollIndex: min(100, itemCount - 1),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1,
|
||||
'index not bigger than 0 and smaller than itemCount -1');
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
builder: (context, itemCount, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
initialScrollIndex: min(100, itemCount - 1),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1);
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1834,19 +1822,19 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<int>(
|
||||
valueListenable: itemCount,
|
||||
builder: (context, itemCount, child) =>
|
||||
ScrollablePositionedList.builder(
|
||||
initialScrollIndex: itemCount - 1,
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1,
|
||||
'index not bigger than 0 and smaller than itemCount -1');
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
builder: (context, itemCount, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
initialScrollIndex: itemCount - 1,
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
assert(index >= 0 && index <= itemCount - 1);
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1878,11 +1866,7 @@ void main() {
|
||||
minCacheExtent: 10,
|
||||
);
|
||||
|
||||
var fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
var fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
final initialOpacity = fadeTransition.opacity;
|
||||
|
||||
unawaited(
|
||||
@@ -1891,11 +1875,7 @@ void main() {
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity, initialOpacity);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -1914,11 +1894,9 @@ void main() {
|
||||
minCacheExtent: itemHeight * 200,
|
||||
);
|
||||
|
||||
var fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
var fadeTransition = tester.widget<FadeTransition>(
|
||||
fadeTransitionFinder,
|
||||
);
|
||||
final initialOpacity = fadeTransition.opacity;
|
||||
|
||||
unawaited(
|
||||
@@ -1927,11 +1905,7 @@ void main() {
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration ~/ 2);
|
||||
|
||||
fadeTransition = tester.widget<FadeTransition>(find
|
||||
.descendant(
|
||||
of: find.byType(ScrollablePositionedList),
|
||||
matching: find.byType(FadeTransition))
|
||||
.last);
|
||||
fadeTransition = tester.widget<FadeTransition>(fadeTransitionFinder);
|
||||
expect(fadeTransition.opacity, initialOpacity);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -1965,17 +1939,21 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<Key>(
|
||||
valueListenable: key,
|
||||
builder: (context, key, child) => Container(
|
||||
key: key,
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: 200,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
builder: (context, key, child) {
|
||||
return Container(
|
||||
key: key,
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: 200,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2054,15 +2032,19 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<Key>(
|
||||
valueListenable: key,
|
||||
builder: (context, key, child) => ScrollablePositionedList.builder(
|
||||
key: key,
|
||||
itemCount: 10,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
),
|
||||
builder: (context, key, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
key: key,
|
||||
itemCount: 10,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2084,17 +2066,21 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<Key>(
|
||||
valueListenable: key,
|
||||
builder: (context, key, child) => Container(
|
||||
key: key,
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
builder: (context, key, child) {
|
||||
return Container(
|
||||
key: key,
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2124,18 +2110,22 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<Key>(
|
||||
valueListenable: containerKey,
|
||||
builder: (context, key, child) => Container(
|
||||
key: key,
|
||||
child: ScrollablePositionedList.builder(
|
||||
key: scrollKey,
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
builder: (context, key, child) {
|
||||
return Container(
|
||||
key: key,
|
||||
child: ScrollablePositionedList.builder(
|
||||
key: scrollKey,
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2166,15 +2156,18 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<ItemScrollController>(
|
||||
valueListenable: itemScrollControllerListenable,
|
||||
builder: (context, itemScrollController, child) =>
|
||||
ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
),
|
||||
builder: (context, itemScrollController, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2215,29 +2208,35 @@ void main() {
|
||||
Expanded(
|
||||
child: ValueListenableBuilder<ItemScrollController>(
|
||||
valueListenable: topItemScrollControllerListenable,
|
||||
builder: (context, itemScrollController, child) =>
|
||||
ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
),
|
||||
builder: (context, itemScrollController, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ValueListenableBuilder<ItemScrollController>(
|
||||
valueListenable: bottomItemScrollControllerListenable,
|
||||
builder: (context, itemScrollController, child) =>
|
||||
ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
),
|
||||
builder: (context, itemScrollController, child) {
|
||||
return ScrollablePositionedList.builder(
|
||||
itemCount: 100,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
+31
-30
@@ -2,10 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart';
|
||||
|
||||
@@ -497,20 +496,21 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<int>(
|
||||
valueListenable: itemCount,
|
||||
builder: (context, itemCount, child) =>
|
||||
ScrollablePositionedList.separated(
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
separatorBuilder: (context, index) => SizedBox(
|
||||
height: separatorHeight,
|
||||
child: Text('Separator $index'),
|
||||
),
|
||||
),
|
||||
builder: (context, itemCount, child) {
|
||||
return ScrollablePositionedList.separated(
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
separatorBuilder: (context, index) => SizedBox(
|
||||
height: separatorHeight,
|
||||
child: Text('Separator $index'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -538,20 +538,21 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ValueListenableBuilder<int>(
|
||||
valueListenable: itemCount,
|
||||
builder: (context, itemCount, child) =>
|
||||
ScrollablePositionedList.separated(
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
separatorBuilder: (context, index) => SizedBox(
|
||||
height: separatorHeight,
|
||||
child: Text('Separator $index'),
|
||||
),
|
||||
),
|
||||
builder: (context, itemCount, child) {
|
||||
return ScrollablePositionedList.separated(
|
||||
itemCount: itemCount,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
separatorBuilder: (context, index) => SizedBox(
|
||||
height: separatorHeight,
|
||||
child: Text('Separator $index'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
+1
-2
@@ -2,10 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
const screenHeight = 400.0;
|
||||
|
||||
+473
@@ -0,0 +1,473 @@
|
||||
// Copyright 2019 The Fuchsia 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/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/src/positioned_list.dart';
|
||||
|
||||
const screenHeight = 400.0;
|
||||
const screenWidth = 400.0;
|
||||
const itemHeight = screenHeight / 10.0;
|
||||
const defaultItemCount = 500;
|
||||
|
||||
void main() {
|
||||
final itemPositionsNotifier = ItemPositionsListener.create();
|
||||
|
||||
Future<void> setUpWidgetTest(
|
||||
WidgetTester tester, {
|
||||
int topItem = 0,
|
||||
Key? key,
|
||||
ScrollController? scrollController,
|
||||
double anchor = 0,
|
||||
int itemCount = defaultItemCount,
|
||||
bool reverse = false,
|
||||
}) async {
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
tester.binding.window.physicalSizeTestValue =
|
||||
const Size(screenWidth, screenHeight);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
// Use flex layout to ensure that the minimum height is not limited to screenHeight
|
||||
home: Column(children: [
|
||||
// Use Constrained to make max height not more than screenHeight
|
||||
ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxHeight: screenHeight, maxWidth: screenWidth),
|
||||
child: PositionedList(
|
||||
key: key,
|
||||
itemCount: itemCount,
|
||||
positionedIndex: topItem,
|
||||
alignment: anchor,
|
||||
controller: scrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
itemPositionsNotifier:
|
||||
itemPositionsNotifier as ItemPositionsNotifier,
|
||||
shrinkWrap: true,
|
||||
reverse: reverse,
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('short list with shrink wrap', (WidgetTester tester) async {
|
||||
const itemCount = 5;
|
||||
const key = Key('short_list');
|
||||
await setUpWidgetTest(tester, itemCount: itemCount, key: key);
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
tester.getBottomRight(find.text('Item 4')).dy, itemHeight * itemCount);
|
||||
expect(find.text('Item 4'), findsOneWidget);
|
||||
expect(find.text('Item 5'), findsNothing);
|
||||
|
||||
final positionList = find.byKey(key);
|
||||
expect(tester.getBottomRight(positionList).dy, itemHeight * itemCount);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 4)
|
||||
.itemTrailingEdge,
|
||||
1.0);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 0 at top and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 0'), findsOneWidget);
|
||||
expect(find.text('Item 9'), findsOneWidget);
|
||||
expect(find.text('Item 10'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 9)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 10)
|
||||
.itemLeadingEdge,
|
||||
1);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 10)
|
||||
.itemTrailingEdge,
|
||||
11 / 10);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 5 at top and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester, topItem: 5);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 4'), findsNothing);
|
||||
expect(find.text('Item 5'), findsOneWidget);
|
||||
expect(find.text('Item 14'), findsOneWidget);
|
||||
expect(find.text('Item 15'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 4)
|
||||
.itemLeadingEdge,
|
||||
-1 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 4)
|
||||
.itemTrailingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 5)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 14)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 20 at bottom and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester, topItem: 20, anchor: 1);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 20'), findsNothing);
|
||||
expect(find.text('Item 19'), findsOneWidget);
|
||||
expect(find.text('Item 10'), findsOneWidget);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 10)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 19)
|
||||
.itemLeadingEdge,
|
||||
9 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 19)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 20)
|
||||
.itemLeadingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 20 at halfway and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester, topItem: 20, anchor: 0.5);
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 20)
|
||||
.itemLeadingEdge,
|
||||
0.5);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 20)
|
||||
.itemTrailingEdge,
|
||||
0.5 + itemHeight / screenHeight);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 20 half off top of screen and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester,
|
||||
topItem: 20, anchor: -(itemHeight / screenHeight) / 2);
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 20)
|
||||
.itemLeadingEdge,
|
||||
-(itemHeight / screenHeight) / 2);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 20)
|
||||
.itemTrailingEdge,
|
||||
(itemHeight / screenHeight) / 2);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 5 at top then scroll up 2 and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester, topItem: 5);
|
||||
|
||||
await tester.drag(
|
||||
find.byType(PositionedList), const Offset(0, itemHeight * 2));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 2'), findsNothing);
|
||||
expect(find.text('Item 3'), findsOneWidget);
|
||||
expect(find.text('Item 12'), findsOneWidget);
|
||||
expect(find.text('Item 13'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 2)
|
||||
.itemLeadingEdge,
|
||||
-1 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 3)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 12)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'List positioned with 5 at top then scroll down 1/2 and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
await setUpWidgetTest(tester, topItem: 5);
|
||||
|
||||
await tester.drag(
|
||||
find.byType(PositionedList), const Offset(0, -1 / 2 * itemHeight));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 5)
|
||||
.itemTrailingEdge,
|
||||
1 / 20);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 14)
|
||||
.itemLeadingEdge,
|
||||
17 / 20);
|
||||
});
|
||||
|
||||
testWidgets('List positioned with 0 at top scroll up 5 and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final scrollController = ScrollController();
|
||||
await setUpWidgetTest(tester, scrollController: scrollController);
|
||||
await tester.pump();
|
||||
|
||||
scrollController.jumpTo(itemHeight * 5);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Item 4'), findsNothing);
|
||||
expect(find.text('Item 5'), findsOneWidget);
|
||||
expect(find.text('Item 14'), findsOneWidget);
|
||||
expect(find.text('Item 15'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 5)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 4)
|
||||
.itemLeadingEdge,
|
||||
-1 / 10);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'List positioned with 5 at top then scroll up 2 programatically and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final scrollController = ScrollController();
|
||||
await setUpWidgetTest(tester,
|
||||
topItem: 5, scrollController: scrollController);
|
||||
|
||||
scrollController.jumpTo(-2 * itemHeight);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 2'), findsNothing);
|
||||
expect(find.text('Item 3'), findsOneWidget);
|
||||
expect(find.text('Item 12'), findsOneWidget);
|
||||
expect(find.text('Item 13'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 2)
|
||||
.itemLeadingEdge,
|
||||
-1 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 3)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 12)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'List positioned with 5 at top then scroll down 20 programatically and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final scrollController = ScrollController();
|
||||
await setUpWidgetTest(tester,
|
||||
topItem: 5, scrollController: scrollController);
|
||||
|
||||
scrollController.jumpTo(itemHeight * 20);
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 23)
|
||||
.itemLeadingEdge,
|
||||
-2 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 24)
|
||||
.itemLeadingEdge,
|
||||
-1 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 25)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 4)
|
||||
.itemLeadingEdge,
|
||||
-21 / 10);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 5)
|
||||
.itemLeadingEdge,
|
||||
-20 / 10);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'List positioned with 5 at top and initial scroll offset and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final scrollController =
|
||||
ScrollController(initialScrollOffset: -2 * itemHeight);
|
||||
await setUpWidgetTest(tester,
|
||||
topItem: 5, scrollController: scrollController);
|
||||
|
||||
expect(find.text('Item 2'), findsNothing);
|
||||
expect(find.text('Item 3'), findsOneWidget);
|
||||
expect(find.text('Item 12'), findsOneWidget);
|
||||
expect(find.text('Item 13'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 3)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 12)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('short List with reverse and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
const itemCount = 5;
|
||||
const key = Key('short_list');
|
||||
await setUpWidgetTest(tester,
|
||||
itemCount: itemCount, key: key, reverse: true);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 4'), findsOneWidget);
|
||||
expect(find.text('Item 5'), findsNothing);
|
||||
expect(
|
||||
tester.getBottomRight(find.text('Item 0')).dy, itemHeight * itemCount);
|
||||
expect(tester.getTopLeft(find.text('Item 4')).dy, 0);
|
||||
|
||||
final positionList = find.byKey(key);
|
||||
expect(tester.getBottomRight(positionList).dy, itemHeight * itemCount);
|
||||
expect(tester.getTopLeft(positionList).dy, 0);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 4)
|
||||
.itemTrailingEdge,
|
||||
1.0);
|
||||
});
|
||||
|
||||
testWidgets('test nested positioned list', (WidgetTester tester) async {
|
||||
const itemCount = 50;
|
||||
const key = Key('short_list');
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
tester.binding.window.physicalSizeTestValue =
|
||||
const Size(screenWidth, screenHeight);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
// Use flex layout to ensure that the minimum height is not limited to screenHeight
|
||||
home: PositionedList(
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return PositionedList(
|
||||
key: key,
|
||||
itemCount: itemCount,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, idx) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $idx'),
|
||||
));
|
||||
} else {
|
||||
return SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item ${itemCount + index - 1}'),
|
||||
);
|
||||
}
|
||||
},
|
||||
itemPositionsNotifier: itemPositionsNotifier as ItemPositionsNotifier,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Item 0'), findsOneWidget);
|
||||
expect(find.text('Item 50'), findsNothing);
|
||||
expect(tester.getTopLeft(find.text('Item 0')).dy, 0);
|
||||
expect(tester.getBottomRight(find.text('Item 9')).dy, screenHeight);
|
||||
|
||||
final positionList = find.byKey(key);
|
||||
expect(tester.getBottomRight(positionList).dy, itemHeight * itemCount);
|
||||
expect(tester.getTopLeft(positionList).dy, 0);
|
||||
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsNotifier.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemTrailingEdge,
|
||||
5.0);
|
||||
});
|
||||
}
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
// Copyright 2019 The Fuchsia 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/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
const screenHeight = 400.0;
|
||||
const screenWidth = 400.0;
|
||||
const itemHeight = screenHeight / 10.0;
|
||||
const itemCount = 500;
|
||||
const scrollDuration = Duration(seconds: 1);
|
||||
|
||||
void main() {
|
||||
Future<void> setUpWidgetTest(
|
||||
WidgetTester tester, {
|
||||
ItemScrollController? itemScrollController,
|
||||
ItemPositionsListener? itemPositionsListener,
|
||||
EdgeInsets? padding,
|
||||
int initialIndex = 0,
|
||||
}) async {
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
tester.binding.window.physicalSizeTestValue =
|
||||
const Size(screenWidth, screenHeight);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
// Use flex layout to ensure that the minimum height is not limited to screenHeight
|
||||
home: Column(children: [
|
||||
// Use Constrained to make max height not more than screenHeight
|
||||
ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxHeight: screenHeight, maxWidth: screenWidth),
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: itemCount,
|
||||
initialScrollIndex: initialIndex,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, index) => SizedBox(
|
||||
height: itemHeight,
|
||||
child: Text('Item $index'),
|
||||
),
|
||||
itemPositionsListener: itemPositionsListener,
|
||||
shrinkWrap: true,
|
||||
padding: padding,
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('List positioned with 0 at top and shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener);
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 0')).dy, 0);
|
||||
expect(tester.getBottomRight(find.text('Item 9')).dy, screenHeight);
|
||||
expect(find.text('Item 10'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 9)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('Scroll to 1 then 2 (both already on screen) with shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener);
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 1, duration: scrollDuration));
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration);
|
||||
expect(find.text('Item 0'), findsNothing);
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 1)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(tester.getTopLeft(find.text('Item 1')).dy, 0);
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 2, duration: scrollDuration));
|
||||
await tester.pump();
|
||||
await tester.pump(scrollDuration);
|
||||
|
||||
expect(find.text('Item 1'), findsNothing);
|
||||
expect(tester.getTopLeft(find.text('Item 2')).dy, 0);
|
||||
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 2)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 11)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Scroll to 5 (already on screen) and then back to 0 with shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener);
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 5, duration: scrollDuration));
|
||||
await tester.pumpAndSettle();
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 0, duration: scrollDuration));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Item 0'), findsOneWidget);
|
||||
expect(find.text('Item 9'), findsOneWidget);
|
||||
expect(find.text('Item 10'), findsNothing);
|
||||
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 0)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 9)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('Scroll to 100 (not already on screen) with shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener);
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 100, duration: scrollDuration));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Item 99'), findsNothing);
|
||||
expect(find.text('Item 100'), findsOneWidget);
|
||||
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 100)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 109)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('Jump to 100 with shrink wrap', (WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
final itemPositionsListener = ItemPositionsListener.create();
|
||||
await setUpWidgetTest(tester,
|
||||
itemScrollController: itemScrollController,
|
||||
itemPositionsListener: itemPositionsListener);
|
||||
|
||||
itemScrollController.jumpTo(index: 100);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 100')).dy, 0);
|
||||
expect(tester.getBottomRight(find.text('Item 109')).dy, screenHeight);
|
||||
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 100)
|
||||
.itemLeadingEdge,
|
||||
0);
|
||||
expect(
|
||||
itemPositionsListener.itemPositions.value
|
||||
.firstWhere((position) => position.index == 109)
|
||||
.itemTrailingEdge,
|
||||
1);
|
||||
});
|
||||
|
||||
testWidgets('padding test - centered sliver at bottom with shrink wrap',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
await setUpWidgetTest(
|
||||
tester,
|
||||
itemScrollController: itemScrollController,
|
||||
padding: const EdgeInsets.all(10),
|
||||
);
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10));
|
||||
expect(tester.getTopLeft(find.text('Item 1')),
|
||||
const Offset(10, itemHeight + 10));
|
||||
expect(tester.getBottomRight(find.text('Item 1')),
|
||||
const Offset(screenWidth - 10, 10 + itemHeight * 2));
|
||||
|
||||
unawaited(
|
||||
itemScrollController.scrollTo(index: 490, duration: scrollDuration));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.drag(
|
||||
find.byType(ScrollablePositionedList), const Offset(0, -100));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 499')),
|
||||
const Offset(10, screenHeight - itemHeight - 10));
|
||||
});
|
||||
|
||||
testWidgets('padding test - centered sliver not at bottom',
|
||||
(WidgetTester tester) async {
|
||||
final itemScrollController = ItemScrollController();
|
||||
await setUpWidgetTest(
|
||||
tester,
|
||||
itemScrollController: itemScrollController,
|
||||
initialIndex: 2,
|
||||
padding: const EdgeInsets.all(10),
|
||||
);
|
||||
|
||||
await tester.drag(
|
||||
find.byType(ScrollablePositionedList), const Offset(0, 200));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10));
|
||||
expect(tester.getTopLeft(find.text('Item 2')),
|
||||
const Offset(10, 10 + itemHeight * 2));
|
||||
expect(tester.getTopLeft(find.text('Item 3')),
|
||||
const Offset(10, 10 + itemHeight * 3));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user