chore: linting
This commit is contained in:
+2
-2
@@ -46,12 +46,12 @@ class _RegistryWidgetState extends State<RegistryWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _InheritedRegistryWidget extends InheritedWidget {
|
class _InheritedRegistryWidget extends InheritedWidget {
|
||||||
final _RegistryWidgetState state;
|
|
||||||
|
|
||||||
const _InheritedRegistryWidget(
|
const _InheritedRegistryWidget(
|
||||||
{Key? key, required this.state, required Widget child})
|
{Key? key, required this.state, required Widget child})
|
||||||
: super(key: key, child: child);
|
: super(key: key, child: child);
|
||||||
|
|
||||||
|
final _RegistryWidgetState state;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(InheritedWidget oldWidget) => true;
|
bool updateShouldNotify(InheritedWidget oldWidget) => true;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -2,10 +2,13 @@ import 'dart:ui' show hashValues;
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
/// {@template indexed_key}
|
||||||
|
/// Creates an indexed key that delegates its [operator==] to the given key.
|
||||||
|
///
|
||||||
|
/// It contains an index used in [ScrollablePositionedList].
|
||||||
|
/// {@endtemplate}
|
||||||
class IndexedKey extends LocalKey {
|
class IndexedKey extends LocalKey {
|
||||||
/// Creates an indexed key that delegates its [operator==] to the given key.
|
/// {@macro indexed_key}
|
||||||
///
|
|
||||||
/// It contains an index used in [ScrollablePositionedList].
|
|
||||||
const IndexedKey(this.key, this.index);
|
const IndexedKey(this.key, this.index);
|
||||||
|
|
||||||
/// The key to which this this delegates its [operator==].
|
/// The key to which this this delegates its [operator==].
|
||||||
@@ -24,7 +27,5 @@ class IndexedKey extends LocalKey {
|
|||||||
int get hashCode => hashValues(runtimeType, key);
|
int get hashCode => hashValues(runtimeType, key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => '(IndexedKey) index: $index, key: $key';
|
||||||
return '(IndexedKey) index: $index, key: $key';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'item_positions_notifier.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/item_positions_notifier.dart';
|
||||||
import 'scrollable_positioned_list.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/scrollable_positioned_list.dart';
|
||||||
|
|
||||||
/// Provides a listenable iterable of [itemPositions] of items that are on
|
/// Provides a listenable iterable of [itemPositions] of items that are on
|
||||||
/// screen and their locations.
|
/// screen and their locations.
|
||||||
@@ -57,5 +57,5 @@ class ItemPosition {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() =>
|
||||||
'ItemPosition(index: $index, itemLeadingEdge: $itemLeadingEdge, itemTrailingEdge: $itemTrailingEdge)';
|
'''ItemPosition(index: $index, itemLeadingEdge: $itemLeadingEdge, itemTrailingEdge: $itemTrailingEdge)''';
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'item_positions_listener.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/item_positions_listener.dart';
|
||||||
|
|
||||||
/// Internal implementation of [ItemPositionsListener].
|
/// Internal implementation of [ItemPositionsListener].
|
||||||
class ItemPositionsNotifier implements ItemPositionsListener {
|
class ItemPositionsNotifier implements ItemPositionsListener {
|
||||||
|
|||||||
+23
-21
@@ -8,11 +8,11 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'element_registry.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/element_registry.dart';
|
||||||
import 'indexed_key.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/indexed_key.dart';
|
||||||
import 'item_positions_listener.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/item_positions_listener.dart';
|
||||||
import 'item_positions_notifier.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/item_positions_notifier.dart';
|
||||||
import 'scroll_view.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/scroll_view.dart';
|
||||||
|
|
||||||
/// A list of widgets similar to [ListView], except scroll control
|
/// A list of widgets similar to [ListView], except scroll control
|
||||||
/// and position reporting is based on index rather than pixel offset.
|
/// and position reporting is based on index rather than pixel offset.
|
||||||
@@ -27,6 +27,7 @@ import 'scroll_view.dart';
|
|||||||
class PositionedList extends StatefulWidget {
|
class PositionedList extends StatefulWidget {
|
||||||
/// Create a [PositionedList].
|
/// Create a [PositionedList].
|
||||||
const PositionedList({
|
const PositionedList({
|
||||||
|
Key? key,
|
||||||
required this.itemCount,
|
required this.itemCount,
|
||||||
required this.itemBuilder,
|
required this.itemBuilder,
|
||||||
this.separatorBuilder,
|
this.separatorBuilder,
|
||||||
@@ -44,11 +45,12 @@ class PositionedList extends StatefulWidget {
|
|||||||
this.addSemanticIndexes = true,
|
this.addSemanticIndexes = true,
|
||||||
this.addRepaintBoundaries = true,
|
this.addRepaintBoundaries = true,
|
||||||
this.addAutomaticKeepAlives = true,
|
this.addAutomaticKeepAlives = true,
|
||||||
}) : assert(itemCount != null),
|
}) : assert((positionedIndex == 0) || (positionedIndex < itemCount),
|
||||||
assert(itemBuilder != null),
|
'positionedIndex cannot be 0 and must be smaller than itemCount'),
|
||||||
assert((positionedIndex == 0) || (positionedIndex < itemCount));
|
super(key: key);
|
||||||
|
|
||||||
/// Called to find the new index of a child based on its key in case of reordering.
|
/// Called to find the new index of a child based on its key in case of
|
||||||
|
/// reordering.
|
||||||
///
|
///
|
||||||
/// If not provided, a child widget may not map to its existing [RenderObject]
|
/// If not provided, a child widget may not map to its existing [RenderObject]
|
||||||
/// when the order in which children are returned from [builder] changes.
|
/// when the order in which children are returned from [builder] changes.
|
||||||
@@ -263,7 +265,7 @@ class _PositionedListState extends State<PositionedList> {
|
|||||||
: widget.reverse
|
: widget.reverse
|
||||||
? widget.padding?.copyWith(left: 0)
|
? widget.padding?.copyWith(left: 0)
|
||||||
: widget.padding?.copyWith(right: 0)) ??
|
: widget.padding?.copyWith(right: 0)) ??
|
||||||
EdgeInsets.all(0);
|
const EdgeInsets.all(0);
|
||||||
|
|
||||||
EdgeInsets get _centerSliverPadding => widget.scrollDirection == Axis.vertical
|
EdgeInsets get _centerSliverPadding => widget.scrollDirection == Axis.vertical
|
||||||
? widget.reverse
|
? widget.reverse
|
||||||
@@ -274,13 +276,13 @@ class _PositionedListState extends State<PositionedList> {
|
|||||||
bottom: widget.positionedIndex == 0
|
bottom: widget.positionedIndex == 0
|
||||||
? widget.padding!.bottom
|
? widget.padding!.bottom
|
||||||
: 0) ??
|
: 0) ??
|
||||||
EdgeInsets.all(0)
|
const EdgeInsets.all(0)
|
||||||
: widget.padding?.copyWith(
|
: widget.padding?.copyWith(
|
||||||
top: widget.positionedIndex == 0 ? widget.padding!.top : 0,
|
top: widget.positionedIndex == 0 ? widget.padding!.top : 0,
|
||||||
bottom: widget.positionedIndex == widget.itemCount - 1
|
bottom: widget.positionedIndex == widget.itemCount - 1
|
||||||
? widget.padding!.bottom
|
? widget.padding!.bottom
|
||||||
: 0) ??
|
: 0) ??
|
||||||
EdgeInsets.all(0)
|
const EdgeInsets.all(0)
|
||||||
: widget.reverse
|
: widget.reverse
|
||||||
? widget.padding?.copyWith(
|
? widget.padding?.copyWith(
|
||||||
left: widget.positionedIndex == widget.itemCount - 1
|
left: widget.positionedIndex == widget.itemCount - 1
|
||||||
@@ -289,23 +291,23 @@ class _PositionedListState extends State<PositionedList> {
|
|||||||
right: widget.positionedIndex == 0
|
right: widget.positionedIndex == 0
|
||||||
? widget.padding!.right
|
? widget.padding!.right
|
||||||
: 0) ??
|
: 0) ??
|
||||||
EdgeInsets.all(0)
|
const EdgeInsets.all(0)
|
||||||
: widget.padding?.copyWith(
|
: widget.padding?.copyWith(
|
||||||
left: widget.positionedIndex == 0 ? widget.padding!.left : 0,
|
left: widget.positionedIndex == 0 ? widget.padding!.left : 0,
|
||||||
right: widget.positionedIndex == widget.itemCount - 1
|
right: widget.positionedIndex == widget.itemCount - 1
|
||||||
? widget.padding!.right
|
? widget.padding!.right
|
||||||
: 0,
|
: 0,
|
||||||
) ??
|
) ??
|
||||||
EdgeInsets.all(0);
|
const EdgeInsets.all(0);
|
||||||
|
|
||||||
EdgeInsets get _trailingSliverPadding =>
|
EdgeInsets get _trailingSliverPadding =>
|
||||||
widget.scrollDirection == Axis.vertical
|
widget.scrollDirection == Axis.vertical
|
||||||
? widget.reverse
|
? widget.reverse
|
||||||
? widget.padding?.copyWith(bottom: 0) ?? EdgeInsets.all(0)
|
? widget.padding?.copyWith(bottom: 0) ?? const EdgeInsets.all(0)
|
||||||
: widget.padding?.copyWith(top: 0) ?? EdgeInsets.all(0)
|
: widget.padding?.copyWith(top: 0) ?? const EdgeInsets.all(0)
|
||||||
: widget.reverse
|
: widget.reverse
|
||||||
? widget.padding?.copyWith(right: 0) ?? EdgeInsets.all(0)
|
? widget.padding?.copyWith(right: 0) ?? const EdgeInsets.all(0)
|
||||||
: widget.padding?.copyWith(left: 0) ?? EdgeInsets.all(0);
|
: widget.padding?.copyWith(left: 0) ?? const EdgeInsets.all(0);
|
||||||
|
|
||||||
void _schedulePositionNotificationUpdate() {
|
void _schedulePositionNotificationUpdate() {
|
||||||
if (!updateScheduled) {
|
if (!updateScheduled) {
|
||||||
@@ -317,10 +319,10 @@ class _PositionedListState extends State<PositionedList> {
|
|||||||
}
|
}
|
||||||
final positions = <ItemPosition>[];
|
final positions = <ItemPosition>[];
|
||||||
RenderViewport? viewport;
|
RenderViewport? viewport;
|
||||||
for (var element in registeredElements.value!) {
|
for (final element in registeredElements.value!) {
|
||||||
final RenderBox box = element.renderObject as RenderBox;
|
final box = element.renderObject as RenderBox;
|
||||||
viewport ??= RenderAbstractViewport.of(box) as RenderViewport?;
|
viewport ??= RenderAbstractViewport.of(box) as RenderViewport?;
|
||||||
final IndexedKey key = element.widget.key as IndexedKey;
|
final key = element.widget.key as IndexedKey;
|
||||||
if (widget.scrollDirection == Axis.vertical) {
|
if (widget.scrollDirection == Axis.vertical) {
|
||||||
final reveal = viewport!.getOffsetToReveal(box, 0).offset;
|
final reveal = viewport!.getOffsetToReveal(box, 0).offset;
|
||||||
final itemOffset = reveal -
|
final itemOffset = reveal -
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ class _PostMountCallbackElement extends StatelessElement {
|
|||||||
@override
|
@override
|
||||||
void mount(Element? parent, dynamic newSlot) {
|
void mount(Element? parent, dynamic newSlot) {
|
||||||
super.mount(parent, newSlot);
|
super.mount(parent, newSlot);
|
||||||
final PostMountCallback postMountCallback = widget as PostMountCallback;
|
final postMountCallback = widget as PostMountCallback;
|
||||||
postMountCallback.callback?.call();
|
postMountCallback.callback?.call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -6,11 +6,14 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'viewport.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/viewport.dart';
|
||||||
|
|
||||||
/// A version of [CustomScrollView] that allows does not constrict the extents
|
/// {@template custom_scroll_view}
|
||||||
|
/// A version of [CustomScrollView] that does not constrict the extents
|
||||||
/// to be within 0 and 1. See [CustomScrollView] for more information.
|
/// to be within 0 and 1. See [CustomScrollView] for more information.
|
||||||
|
/// {@endtemplate}
|
||||||
class UnboundedCustomScrollView extends CustomScrollView {
|
class UnboundedCustomScrollView extends CustomScrollView {
|
||||||
|
/// {@macro custom_scroll_view}
|
||||||
const UnboundedCustomScrollView({
|
const UnboundedCustomScrollView({
|
||||||
Key? key,
|
Key? key,
|
||||||
Axis scrollDirection = Axis.vertical,
|
Axis scrollDirection = Axis.vertical,
|
||||||
|
|||||||
+82
-80
@@ -10,10 +10,10 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'item_positions_listener.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/item_positions_listener.dart';
|
||||||
import 'item_positions_notifier.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/item_positions_notifier.dart';
|
||||||
import 'positioned_list.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/positioned_list.dart';
|
||||||
import 'post_mount_callback.dart';
|
import 'package:stream_chat_flutter/src/scrollable_positioned_list/src/post_mount_callback.dart';
|
||||||
|
|
||||||
/// Number of screens to scroll when scrolling a long distance.
|
/// Number of screens to scroll when scrolling a long distance.
|
||||||
const int _screenScrollCount = 2;
|
const int _screenScrollCount = 2;
|
||||||
@@ -52,9 +52,7 @@ class ScrollablePositionedList extends StatefulWidget {
|
|||||||
this.addRepaintBoundaries = true,
|
this.addRepaintBoundaries = true,
|
||||||
this.minCacheExtent,
|
this.minCacheExtent,
|
||||||
this.findChildIndexCallback,
|
this.findChildIndexCallback,
|
||||||
}) : assert(itemCount != null),
|
}) : itemPositionsNotifier = itemPositionsListener as ItemPositionsNotifier?,
|
||||||
assert(itemBuilder != null),
|
|
||||||
itemPositionsNotifier = itemPositionsListener as ItemPositionsNotifier?,
|
|
||||||
separatorBuilder = null,
|
separatorBuilder = null,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
@@ -79,13 +77,12 @@ class ScrollablePositionedList extends StatefulWidget {
|
|||||||
this.addRepaintBoundaries = true,
|
this.addRepaintBoundaries = true,
|
||||||
this.minCacheExtent,
|
this.minCacheExtent,
|
||||||
this.findChildIndexCallback,
|
this.findChildIndexCallback,
|
||||||
}) : assert(itemCount != null),
|
}) : assert(separatorBuilder != null, 'seperatorBuilder cannot be null'),
|
||||||
assert(itemBuilder != null),
|
|
||||||
assert(separatorBuilder != null),
|
|
||||||
itemPositionsNotifier = itemPositionsListener as ItemPositionsNotifier?,
|
itemPositionsNotifier = itemPositionsListener as ItemPositionsNotifier?,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// Called to find the new index of a child based on its key in case of reordering.
|
/// Called to find the new index of a child based on its key in case of
|
||||||
|
/// reordering.
|
||||||
///
|
///
|
||||||
/// If not provided, a child widget may not map to its existing [RenderObject]
|
/// If not provided, a child widget may not map to its existing [RenderObject]
|
||||||
/// when the order in which children are returned from [builder] changes.
|
/// when the order in which children are returned from [builder] changes.
|
||||||
@@ -235,9 +232,11 @@ class ItemScrollController {
|
|||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
List<double> opacityAnimationWeights = const [40, 20, 40],
|
List<double> opacityAnimationWeights = const [40, 20, 40],
|
||||||
}) {
|
}) {
|
||||||
assert(_scrollableListState != null);
|
assert(_scrollableListState != null, '_scrollableListState cannot be null');
|
||||||
assert(opacityAnimationWeights.length == 3);
|
assert(opacityAnimationWeights.length == 3,
|
||||||
assert(duration > Duration.zero);
|
'opacityAnimationWeights.length is not equal to 3');
|
||||||
|
assert(duration > Duration.zero,
|
||||||
|
'duration needs to be bigger than Duration.zero');
|
||||||
return _scrollableListState!._scrollTo(
|
return _scrollableListState!._scrollTo(
|
||||||
index: index,
|
index: index,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
@@ -248,7 +247,8 @@ class ItemScrollController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _attach(_ScrollablePositionedListState scrollableListState) {
|
void _attach(_ScrollablePositionedListState scrollableListState) {
|
||||||
assert(_scrollableListState == null);
|
assert(
|
||||||
|
_scrollableListState == null, '_scrollableListState needs to be null');
|
||||||
_scrollableListState = scrollableListState;
|
_scrollableListState = scrollableListState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,11 +260,11 @@ class ItemScrollController {
|
|||||||
class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
/// Details for the primary (active) [ListView].
|
/// Details for the primary (active) [ListView].
|
||||||
var primary = _ListDisplayDetails(const ValueKey('Ping'));
|
_ListDisplayDetails primary = _ListDisplayDetails(const ValueKey('Ping'));
|
||||||
|
|
||||||
/// Details for the secondary (transitional) [ListView] that is temporarily
|
/// Details for the secondary (transitional) [ListView] that is temporarily
|
||||||
/// shown when scrolling a long distance.
|
/// shown when scrolling a long distance.
|
||||||
var secondary = _ListDisplayDetails(const ValueKey('Pong'));
|
_ListDisplayDetails secondary = _ListDisplayDetails(const ValueKey('Pong'));
|
||||||
|
|
||||||
final opacity = ProxyAnimation(const AlwaysStoppedAnimation<double>(0));
|
final opacity = ProxyAnimation(const AlwaysStoppedAnimation<double>(0));
|
||||||
|
|
||||||
@@ -275,10 +275,11 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
ItemPosition? initialPosition = PageStorage.of(context)!.readState(context);
|
final ItemPosition? initialPosition =
|
||||||
primary.target = initialPosition?.index ?? widget.initialScrollIndex;
|
PageStorage.of(context)!.readState(context);
|
||||||
primary.alignment =
|
primary
|
||||||
initialPosition?.itemLeadingEdge ?? widget.initialAlignment;
|
..target = initialPosition?.index ?? widget.initialScrollIndex
|
||||||
|
..alignment = initialPosition?.itemLeadingEdge ?? widget.initialAlignment;
|
||||||
if (widget.itemCount > 0 && primary.target > widget.itemCount - 1) {
|
if (widget.itemCount > 0 && primary.target > widget.itemCount - 1) {
|
||||||
primary.target = widget.itemCount - 1;
|
primary.target = widget.itemCount - 1;
|
||||||
}
|
}
|
||||||
@@ -333,79 +334,78 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => LayoutBuilder(
|
||||||
return LayoutBuilder(
|
builder: (context, constraints) {
|
||||||
builder: (context, constraints) {
|
final cacheExtent = _cacheExtent(constraints);
|
||||||
final cacheExtent = _cacheExtent(constraints);
|
return GestureDetector(
|
||||||
return GestureDetector(
|
onPanDown: (_) => _stopScroll(canceled: true),
|
||||||
onPanDown: (_) => _stopScroll(canceled: true),
|
excludeFromSemantics: true,
|
||||||
excludeFromSemantics: true,
|
child: Stack(
|
||||||
child: Stack(
|
children: <Widget>[
|
||||||
children: <Widget>[
|
|
||||||
PostMountCallback(
|
|
||||||
key: primary.key,
|
|
||||||
callback: startAnimationCallback,
|
|
||||||
child: FadeTransition(
|
|
||||||
opacity: ReverseAnimation(opacity),
|
|
||||||
child: NotificationListener<ScrollNotification>(
|
|
||||||
onNotification: (_) => _isTransitioning,
|
|
||||||
child: PositionedList(
|
|
||||||
itemBuilder: widget.itemBuilder,
|
|
||||||
separatorBuilder: widget.separatorBuilder,
|
|
||||||
itemCount: widget.itemCount,
|
|
||||||
positionedIndex: primary.target,
|
|
||||||
controller: primary.scrollController,
|
|
||||||
itemPositionsNotifier: primary.itemPositionsNotifier,
|
|
||||||
scrollDirection: widget.scrollDirection,
|
|
||||||
reverse: widget.reverse,
|
|
||||||
cacheExtent: cacheExtent,
|
|
||||||
alignment: primary.alignment,
|
|
||||||
physics: widget.physics,
|
|
||||||
addSemanticIndexes: widget.addSemanticIndexes,
|
|
||||||
semanticChildCount: widget.semanticChildCount,
|
|
||||||
padding: widget.padding,
|
|
||||||
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
|
|
||||||
addRepaintBoundaries: widget.addRepaintBoundaries,
|
|
||||||
findChildIndexCallback: widget.findChildIndexCallback,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_isTransitioning)
|
|
||||||
PostMountCallback(
|
PostMountCallback(
|
||||||
key: secondary.key,
|
key: primary.key,
|
||||||
callback: startAnimationCallback,
|
callback: startAnimationCallback,
|
||||||
child: FadeTransition(
|
child: FadeTransition(
|
||||||
opacity: opacity,
|
opacity: ReverseAnimation(opacity),
|
||||||
child: NotificationListener<ScrollNotification>(
|
child: NotificationListener<ScrollNotification>(
|
||||||
onNotification: (_) => false,
|
onNotification: (_) => _isTransitioning,
|
||||||
child: PositionedList(
|
child: PositionedList(
|
||||||
itemBuilder: widget.itemBuilder,
|
itemBuilder: widget.itemBuilder,
|
||||||
separatorBuilder: widget.separatorBuilder,
|
separatorBuilder: widget.separatorBuilder,
|
||||||
itemCount: widget.itemCount,
|
itemCount: widget.itemCount,
|
||||||
itemPositionsNotifier: secondary.itemPositionsNotifier,
|
positionedIndex: primary.target,
|
||||||
positionedIndex: secondary.target,
|
controller: primary.scrollController,
|
||||||
controller: secondary.scrollController,
|
itemPositionsNotifier: primary.itemPositionsNotifier,
|
||||||
scrollDirection: widget.scrollDirection,
|
scrollDirection: widget.scrollDirection,
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
cacheExtent: cacheExtent,
|
cacheExtent: cacheExtent,
|
||||||
alignment: secondary.alignment,
|
alignment: primary.alignment,
|
||||||
physics: widget.physics,
|
physics: widget.physics,
|
||||||
addSemanticIndexes: widget.addSemanticIndexes,
|
addSemanticIndexes: widget.addSemanticIndexes,
|
||||||
semanticChildCount: widget.semanticChildCount,
|
semanticChildCount: widget.semanticChildCount,
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
|
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
|
||||||
addRepaintBoundaries: widget.addRepaintBoundaries,
|
addRepaintBoundaries: widget.addRepaintBoundaries,
|
||||||
|
findChildIndexCallback: widget.findChildIndexCallback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
if (_isTransitioning)
|
||||||
),
|
PostMountCallback(
|
||||||
);
|
key: secondary.key,
|
||||||
},
|
callback: startAnimationCallback,
|
||||||
);
|
child: FadeTransition(
|
||||||
}
|
opacity: opacity,
|
||||||
|
child: NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (_) => false,
|
||||||
|
child: PositionedList(
|
||||||
|
itemBuilder: widget.itemBuilder,
|
||||||
|
separatorBuilder: widget.separatorBuilder,
|
||||||
|
itemCount: widget.itemCount,
|
||||||
|
itemPositionsNotifier:
|
||||||
|
secondary.itemPositionsNotifier,
|
||||||
|
positionedIndex: secondary.target,
|
||||||
|
controller: secondary.scrollController,
|
||||||
|
scrollDirection: widget.scrollDirection,
|
||||||
|
reverse: widget.reverse,
|
||||||
|
cacheExtent: cacheExtent,
|
||||||
|
alignment: secondary.alignment,
|
||||||
|
physics: widget.physics,
|
||||||
|
addSemanticIndexes: widget.addSemanticIndexes,
|
||||||
|
semanticChildCount: widget.semanticChildCount,
|
||||||
|
padding: widget.padding,
|
||||||
|
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
|
||||||
|
addRepaintBoundaries: widget.addRepaintBoundaries,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
double _cacheExtent(BoxConstraints constraints) => max(
|
double _cacheExtent(BoxConstraints constraints) => max(
|
||||||
constraints.maxHeight * _screenScrollCount,
|
constraints.maxHeight * _screenScrollCount,
|
||||||
@@ -419,8 +419,9 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
|||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
primary.scrollController.jumpTo(0);
|
primary.scrollController.jumpTo(0);
|
||||||
primary.target = index;
|
primary
|
||||||
primary.alignment = alignment;
|
..target = index
|
||||||
|
..alignment = alignment;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,8 +506,9 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
|||||||
setState(() {
|
setState(() {
|
||||||
// TODO: _startScroll can be re-entrant, which invalidates this assert.
|
// TODO: _startScroll can be re-entrant, which invalidates this assert.
|
||||||
// assert(!_isTransitioning);
|
// assert(!_isTransitioning);
|
||||||
secondary.target = index;
|
secondary
|
||||||
secondary.alignment = alignment;
|
..target = index
|
||||||
|
..alignment = alignment;
|
||||||
_isTransitioning = true;
|
_isTransitioning = true;
|
||||||
});
|
});
|
||||||
await Future.wait<void>([startCompleter.future, endCompleter.future]);
|
await Future.wait<void>([startCompleter.future, endCompleter.future]);
|
||||||
@@ -532,7 +534,7 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
|||||||
if (opacity.value >= 0.5) {
|
if (opacity.value >= 0.5) {
|
||||||
// Secondary [ListView] is more visible than the primary; make it the
|
// Secondary [ListView] is more visible than the primary; make it the
|
||||||
// new primary.
|
// new primary.
|
||||||
var temp = primary;
|
final temp = primary;
|
||||||
primary = secondary;
|
primary = secondary;
|
||||||
secondary = temp;
|
secondary = temp;
|
||||||
}
|
}
|
||||||
@@ -542,8 +544,8 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Animatable<double> _opacityAnimation(List<double> opacityAnimationWeights) {
|
Animatable<double> _opacityAnimation(List<double> opacityAnimationWeights) {
|
||||||
final startOpacity = 0.0;
|
const startOpacity = 0.0;
|
||||||
final endOpacity = 1.0;
|
const endOpacity = 1.0;
|
||||||
return TweenSequence<double>(<TweenSequenceItem<double>>[
|
return TweenSequence<double>(<TweenSequenceItem<double>>[
|
||||||
TweenSequenceItem<double>(
|
TweenSequenceItem<double>(
|
||||||
tween: ConstantTween<double>(startOpacity),
|
tween: ConstantTween<double>(startOpacity),
|
||||||
|
|||||||
@@ -2,17 +2,22 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// {@template unbounded_viewport}
|
||||||
/// A render object that is bigger on the inside.
|
/// A render object that is bigger on the inside.
|
||||||
///
|
///
|
||||||
/// Version of [Viewport] with some modifications to how extents are
|
/// Version of [Viewport] with some modifications to how extents are
|
||||||
/// computed to allow scroll extents outside 0 to 1. See [Viewport]
|
/// computed to allow scroll extents outside 0 to 1. See [Viewport]
|
||||||
/// for more information.
|
/// for more information.
|
||||||
|
/// description
|
||||||
class UnboundedViewport extends Viewport {
|
class UnboundedViewport extends Viewport {
|
||||||
|
/// {@macro unbounded_viewport}
|
||||||
UnboundedViewport({
|
UnboundedViewport({
|
||||||
Key? key,
|
Key? key,
|
||||||
AxisDirection axisDirection = AxisDirection.down,
|
AxisDirection axisDirection = AxisDirection.down,
|
||||||
@@ -40,16 +45,15 @@ class UnboundedViewport extends Viewport {
|
|||||||
double get anchor => _anchor;
|
double get anchor => _anchor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderViewport createRenderObject(BuildContext context) {
|
RenderViewport createRenderObject(BuildContext context) =>
|
||||||
return UnboundedRenderViewport(
|
UnboundedRenderViewport(
|
||||||
axisDirection: axisDirection,
|
axisDirection: axisDirection,
|
||||||
crossAxisDirection: crossAxisDirection ??
|
crossAxisDirection: crossAxisDirection ??
|
||||||
Viewport.getDefaultCrossAxisDirection(context, axisDirection),
|
Viewport.getDefaultCrossAxisDirection(context, axisDirection),
|
||||||
anchor: anchor,
|
anchor: anchor,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
cacheExtent: cacheExtent,
|
cacheExtent: cacheExtent,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A render object that is bigger on the inside.
|
/// A render object that is bigger on the inside.
|
||||||
@@ -100,7 +104,6 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
set anchor(double value) {
|
set anchor(double value) {
|
||||||
assert(value != null);
|
|
||||||
if (value == _anchor) return;
|
if (value == _anchor) return;
|
||||||
_anchor = value;
|
_anchor = value;
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
@@ -124,8 +127,6 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Rect describeSemanticsClip(RenderSliver? child) {
|
Rect describeSemanticsClip(RenderSliver? child) {
|
||||||
assert(axis != null);
|
|
||||||
|
|
||||||
if (_calculatedCacheExtent == null) {
|
if (_calculatedCacheExtent == null) {
|
||||||
return semanticBounds;
|
return semanticBounds;
|
||||||
}
|
}
|
||||||
@@ -151,14 +152,14 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
@override
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
if (center == null) {
|
if (center == null) {
|
||||||
assert(firstChild == null);
|
assert(firstChild == null, 'firstChild cannot be null');
|
||||||
_minScrollExtent = 0.0;
|
_minScrollExtent = 0.0;
|
||||||
_maxScrollExtent = 0.0;
|
_maxScrollExtent = 0.0;
|
||||||
_hasVisualOverflow = false;
|
_hasVisualOverflow = false;
|
||||||
offset.applyContentDimensions(0.0, 0.0);
|
offset.applyContentDimensions(0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(center!.parent == this);
|
assert(center!.parent == this, 'center.parent cannot be equal to this');
|
||||||
|
|
||||||
late double mainAxisExtent;
|
late double mainAxisExtent;
|
||||||
late double crossAxisExtent;
|
late double crossAxisExtent;
|
||||||
@@ -178,7 +179,6 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
double correction;
|
double correction;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
do {
|
do {
|
||||||
assert(offset.pixels != null);
|
|
||||||
correction = _attemptLayout(mainAxisExtent, crossAxisExtent,
|
correction = _attemptLayout(mainAxisExtent, crossAxisExtent,
|
||||||
offset.pixels + centerOffsetAdjustment);
|
offset.pixels + centerOffsetAdjustment);
|
||||||
if (correction != 0.0) {
|
if (correction != 0.0) {
|
||||||
@@ -187,17 +187,18 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
// *** Difference from [RenderViewport].
|
// *** Difference from [RenderViewport].
|
||||||
final top = _minScrollExtent + mainAxisExtent * anchor;
|
final top = _minScrollExtent + mainAxisExtent * anchor;
|
||||||
final bottom = _maxScrollExtent - mainAxisExtent * (1.0 - anchor);
|
final bottom = _maxScrollExtent - mainAxisExtent * (1.0 - anchor);
|
||||||
final maxScrollOffset = math.max(math.min(0.0, top), bottom);
|
final maxScrollOffset = math.max<double>(math.min(0, top), bottom);
|
||||||
final minScrollOffset = math.min(top, maxScrollOffset);
|
final minScrollOffset = math.min<double>(top, maxScrollOffset);
|
||||||
if (offset.applyContentDimensions(minScrollOffset, maxScrollOffset))
|
if (offset.applyContentDimensions(minScrollOffset, maxScrollOffset)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// *** End of difference from [RenderViewport].
|
// *** End of difference from [RenderViewport].
|
||||||
}
|
}
|
||||||
count += 1;
|
count += 1;
|
||||||
} while (count < _maxLayoutCycles);
|
} while (count < _maxLayoutCycles);
|
||||||
assert(() {
|
assert(() {
|
||||||
if (count >= _maxLayoutCycles) {
|
if (count >= _maxLayoutCycles) {
|
||||||
assert(count != 1);
|
assert(count != 1, 'count not equal to 1');
|
||||||
throw FlutterError(
|
throw FlutterError(
|
||||||
'A RenderViewport exceeded its maximum number of layout cycles.\n'
|
'A RenderViewport exceeded its maximum number of layout cycles.\n'
|
||||||
'RenderViewport render objects, during layout, can retry if either their '
|
'RenderViewport render objects, during layout, can retry if either their '
|
||||||
@@ -217,16 +218,16 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
' layout passes.');
|
' layout passes.');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}(), 'count needs to be bigger than _maxLayoutCycles');
|
||||||
}
|
}
|
||||||
|
|
||||||
double _attemptLayout(
|
double _attemptLayout(
|
||||||
double mainAxisExtent, double crossAxisExtent, double correctedOffset) {
|
double mainAxisExtent, double crossAxisExtent, double correctedOffset) {
|
||||||
assert(!mainAxisExtent.isNaN);
|
assert(!mainAxisExtent.isNaN, 'assert mainAxisExtent.isNaN');
|
||||||
assert(mainAxisExtent >= 0.0);
|
assert(mainAxisExtent >= 0.0, 'assert mainAxisExtent >= 0.0');
|
||||||
assert(crossAxisExtent.isFinite);
|
assert(crossAxisExtent.isFinite, 'assert crossAxisExtent.isFinite');
|
||||||
assert(crossAxisExtent >= 0.0);
|
assert(crossAxisExtent >= 0.0, 'assert crossAxisExtent >= 0.0');
|
||||||
assert(correctedOffset.isFinite);
|
assert(correctedOffset.isFinite, 'assert correctedOffset.isFinite');
|
||||||
_minScrollExtent = 0.0;
|
_minScrollExtent = 0.0;
|
||||||
_maxScrollExtent = 0.0;
|
_maxScrollExtent = 0.0;
|
||||||
_hasVisualOverflow = false;
|
_hasVisualOverflow = false;
|
||||||
@@ -234,10 +235,10 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
// centerOffset is the offset from the leading edge of the RenderViewport
|
// centerOffset is the offset from the leading edge of the RenderViewport
|
||||||
// to the zero scroll offset (the line between the forward slivers and the
|
// to the zero scroll offset (the line between the forward slivers and the
|
||||||
// reverse slivers).
|
// reverse slivers).
|
||||||
final double centerOffset = mainAxisExtent * anchor - correctedOffset;
|
final centerOffset = mainAxisExtent * anchor - correctedOffset;
|
||||||
final double reverseDirectionRemainingPaintExtent =
|
final reverseDirectionRemainingPaintExtent =
|
||||||
centerOffset.clamp(0.0, mainAxisExtent);
|
centerOffset.clamp(0.0, mainAxisExtent);
|
||||||
final double forwardDirectionRemainingPaintExtent =
|
final forwardDirectionRemainingPaintExtent =
|
||||||
(mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent);
|
(mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent);
|
||||||
|
|
||||||
switch (cacheExtentStyle) {
|
switch (cacheExtentStyle) {
|
||||||
@@ -249,21 +250,21 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
final double fullCacheExtent = mainAxisExtent + 2 * _calculatedCacheExtent!;
|
final fullCacheExtent = mainAxisExtent + 2 * _calculatedCacheExtent!;
|
||||||
final double centerCacheOffset = centerOffset + _calculatedCacheExtent!;
|
final centerCacheOffset = centerOffset + _calculatedCacheExtent!;
|
||||||
final double reverseDirectionRemainingCacheExtent =
|
final reverseDirectionRemainingCacheExtent =
|
||||||
centerCacheOffset.clamp(0.0, fullCacheExtent);
|
centerCacheOffset.clamp(0.0, fullCacheExtent);
|
||||||
final double forwardDirectionRemainingCacheExtent =
|
final forwardDirectionRemainingCacheExtent =
|
||||||
(fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent);
|
(fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent);
|
||||||
|
|
||||||
final RenderSliver? leadingNegativeChild = childBefore(center!);
|
final leadingNegativeChild = childBefore(center!);
|
||||||
|
|
||||||
if (leadingNegativeChild != null) {
|
if (leadingNegativeChild != null) {
|
||||||
// negative scroll offsets
|
// negative scroll offsets
|
||||||
final double result = layoutChildSequence(
|
final result = layoutChildSequence(
|
||||||
child: leadingNegativeChild,
|
child: leadingNegativeChild,
|
||||||
scrollOffset: math.max(mainAxisExtent, centerOffset) - mainAxisExtent,
|
scrollOffset: math.max(mainAxisExtent, centerOffset) - mainAxisExtent,
|
||||||
overlap: 0.0,
|
overlap: 0,
|
||||||
layoutOffset: forwardDirectionRemainingPaintExtent,
|
layoutOffset: forwardDirectionRemainingPaintExtent,
|
||||||
remainingPaintExtent: reverseDirectionRemainingPaintExtent,
|
remainingPaintExtent: reverseDirectionRemainingPaintExtent,
|
||||||
mainAxisExtent: mainAxisExtent,
|
mainAxisExtent: mainAxisExtent,
|
||||||
@@ -280,9 +281,8 @@ class UnboundedRenderViewport extends RenderViewport {
|
|||||||
// positive scroll offsets
|
// positive scroll offsets
|
||||||
return layoutChildSequence(
|
return layoutChildSequence(
|
||||||
child: center,
|
child: center,
|
||||||
scrollOffset: math.max(0.0, -centerOffset),
|
scrollOffset: math.max(0, -centerOffset),
|
||||||
overlap:
|
overlap: leadingNegativeChild == null ? math.min(0, -centerOffset) : 0.0,
|
||||||
leadingNegativeChild == null ? math.min(0.0, -centerOffset) : 0.0,
|
|
||||||
layoutOffset: centerOffset >= mainAxisExtent
|
layoutOffset: centerOffset >= mainAxisExtent
|
||||||
? centerOffset
|
? centerOffset
|
||||||
: reverseDirectionRemainingPaintExtent,
|
: reverseDirectionRemainingPaintExtent,
|
||||||
|
|||||||
+1
-1
@@ -117,7 +117,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('List positioned with 15 at bottom', (WidgetTester tester) async {
|
testWidgets('List positioned with 15 at bottom', (WidgetTester tester) async {
|
||||||
await setUpWidgetTest(tester, topItem: 15, anchor: 0);
|
await setUpWidgetTest(tester, topItem: 15);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
expect(find.text('Item 14'), findsNothing);
|
expect(find.text('Item 14'), findsNothing);
|
||||||
|
|||||||
+138
-165
@@ -50,7 +50,8 @@ void main() {
|
|||||||
itemCount: itemCount,
|
itemCount: itemCount,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
assert(index >= 0 && index <= itemCount - 1);
|
assert(index >= 0 && index <= itemCount - 1,
|
||||||
|
'''index needs to be bigger or equal to 0 and smallert than itemCount -1''');
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height:
|
height:
|
||||||
variableHeight ? (itemHeight + (index % 13) * 5) : itemHeight,
|
variableHeight ? (itemHeight + (index % 13) * 5) : itemHeight,
|
||||||
@@ -256,7 +257,7 @@ void main() {
|
|||||||
initialAlignment: 0.5);
|
initialAlignment: 0.5);
|
||||||
|
|
||||||
unawaited(itemScrollController.scrollTo(
|
unawaited(itemScrollController.scrollTo(
|
||||||
index: 16, duration: scrollDuration, alignment: 1.0));
|
index: 16, duration: scrollDuration, alignment: 1));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(scrollDuration + scrollDurationTolerance);
|
await tester.pump(scrollDuration + scrollDurationTolerance);
|
||||||
@@ -457,7 +458,7 @@ void main() {
|
|||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
await setUpWidgetTest(tester, itemScrollController: itemScrollController);
|
await setUpWidgetTest(tester, itemScrollController: itemScrollController);
|
||||||
|
|
||||||
var fadeTransitionFinder = find.descendant(
|
final fadeTransitionFinder = find.descendant(
|
||||||
of: find.byType(ScrollablePositionedList),
|
of: find.byType(ScrollablePositionedList),
|
||||||
matching: find.byType(FadeTransition));
|
matching: find.byType(FadeTransition));
|
||||||
|
|
||||||
@@ -687,7 +688,7 @@ void main() {
|
|||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
itemPositionsListener: itemPositionsListener);
|
itemPositionsListener: itemPositionsListener);
|
||||||
|
|
||||||
itemScrollController.jumpTo(index: 100, alignment: 1.0);
|
itemScrollController.jumpTo(index: 100, alignment: 1);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(scrollDuration + scrollDurationTolerance);
|
await tester.pump(scrollDuration + scrollDurationTolerance);
|
||||||
@@ -758,7 +759,7 @@ void main() {
|
|||||||
itemPositionsListener: itemPositionsListener);
|
itemPositionsListener: itemPositionsListener);
|
||||||
|
|
||||||
unawaited(itemScrollController.scrollTo(
|
unawaited(itemScrollController.scrollTo(
|
||||||
index: 100, alignment: 1.0, duration: scrollDuration));
|
index: 100, alignment: 1, duration: scrollDuration));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(scrollDuration + scrollDurationTolerance);
|
await tester.pump(scrollDuration + scrollDurationTolerance);
|
||||||
@@ -1138,13 +1139,13 @@ void main() {
|
|||||||
expect(find.text('Item 100'), findsNothing);
|
expect(find.text('Item 100'), findsNothing);
|
||||||
expect(find.text('Item 200'), findsNothing);
|
expect(find.text('Item 200'), findsNothing);
|
||||||
|
|
||||||
var itemFinder = find.text('Item 300');
|
final itemFinder = find.text('Item 300');
|
||||||
expect(itemFinder, findsOneWidget);
|
expect(itemFinder, findsOneWidget);
|
||||||
expect(tester.getTopLeft(itemFinder).dy, 0);
|
expect(tester.getTopLeft(itemFinder).dy, 0);
|
||||||
}, skip: true);
|
}, skip: true);
|
||||||
|
|
||||||
testWidgets(
|
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 {
|
(WidgetTester tester) async {
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
@@ -1168,7 +1169,7 @@ void main() {
|
|||||||
index: 400, alignment: 1, duration: scrollDuration));
|
index: 400, alignment: 1, duration: scrollDuration));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
var itemFinder = find.text('Item 399');
|
final itemFinder = find.text('Item 399');
|
||||||
expect(itemFinder, findsOneWidget);
|
expect(itemFinder, findsOneWidget);
|
||||||
expect(tester.getBottomLeft(itemFinder).dy, screenHeight);
|
expect(tester.getBottomLeft(itemFinder).dy, screenHeight);
|
||||||
});
|
});
|
||||||
@@ -1537,7 +1538,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('List can be keyed', (WidgetTester tester) async {
|
testWidgets('List can be keyed', (WidgetTester tester) async {
|
||||||
final key = ValueKey('key');
|
const key = ValueKey('key');
|
||||||
|
|
||||||
await setUpWidgetTest(tester, key: key);
|
await setUpWidgetTest(tester, key: key);
|
||||||
|
|
||||||
@@ -1559,7 +1560,7 @@ void main() {
|
|||||||
home: PageView(
|
home: PageView(
|
||||||
children: [
|
children: [
|
||||||
KeyedSubtree(
|
KeyedSubtree(
|
||||||
key: PageStorageKey('key'),
|
key: const PageStorageKey('key'),
|
||||||
child: ScrollablePositionedList.builder(
|
child: ScrollablePositionedList.builder(
|
||||||
itemCount: defaultItemCount,
|
itemCount: defaultItemCount,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
@@ -1570,7 +1571,7 @@ void main() {
|
|||||||
itemPositionsListener: itemPositionsListener,
|
itemPositionsListener: itemPositionsListener,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Center(
|
const Center(
|
||||||
child: Text('Test'),
|
child: Text('Test'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -1616,7 +1617,7 @@ void main() {
|
|||||||
home: PageView(
|
home: PageView(
|
||||||
children: [
|
children: [
|
||||||
KeyedSubtree(
|
KeyedSubtree(
|
||||||
key: PageStorageKey('key'),
|
key: const PageStorageKey('key'),
|
||||||
child: ScrollablePositionedList.builder(
|
child: ScrollablePositionedList.builder(
|
||||||
itemCount: defaultItemCount,
|
itemCount: defaultItemCount,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
@@ -1627,7 +1628,7 @@ void main() {
|
|||||||
itemPositionsListener: itemPositionsListener,
|
itemPositionsListener: itemPositionsListener,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Center(
|
const Center(
|
||||||
child: Text('Test'),
|
child: Text('Test'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -1663,7 +1664,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets(
|
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 {
|
(WidgetTester tester) async {
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
@@ -1677,7 +1678,7 @@ void main() {
|
|||||||
home: PageView(
|
home: PageView(
|
||||||
children: [
|
children: [
|
||||||
KeyedSubtree(
|
KeyedSubtree(
|
||||||
key: PageStorageKey('key'),
|
key: const PageStorageKey('key'),
|
||||||
child: ScrollablePositionedList.builder(
|
child: ScrollablePositionedList.builder(
|
||||||
itemCount: defaultItemCount,
|
itemCount: defaultItemCount,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
@@ -1688,7 +1689,7 @@ void main() {
|
|||||||
itemPositionsListener: itemPositionsListener,
|
itemPositionsListener: itemPositionsListener,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Center(
|
const Center(
|
||||||
child: Text('Test'),
|
child: Text('Test'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -1728,7 +1729,7 @@ void main() {
|
|||||||
(itemHeight / screenHeight) / 2);
|
(itemHeight / screenHeight) / 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("List with no items", (WidgetTester tester) async {
|
testWidgets('List with no items', (WidgetTester tester) async {
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
await setUpWidgetTest(tester,
|
await setUpWidgetTest(tester,
|
||||||
itemScrollController: itemScrollController, itemCount: 0);
|
itemScrollController: itemScrollController, itemCount: 0);
|
||||||
@@ -1750,22 +1751,21 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<int>(
|
home: ValueListenableBuilder<int>(
|
||||||
valueListenable: itemCount,
|
valueListenable: itemCount,
|
||||||
builder: (context, itemCount, child) {
|
builder: (context, itemCount, child) =>
|
||||||
return ScrollablePositionedList.builder(
|
ScrollablePositionedList.builder(
|
||||||
initialScrollIndex: min(100, itemCount),
|
initialScrollIndex: min(100, itemCount),
|
||||||
initialAlignment: 0,
|
itemCount: itemCount,
|
||||||
itemCount: itemCount,
|
itemScrollController: itemScrollController,
|
||||||
itemScrollController: itemScrollController,
|
itemPositionsListener: itemPositionsListener,
|
||||||
itemPositionsListener: itemPositionsListener,
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
assert(index >= 0 && index <= itemCount - 1,
|
||||||
assert(index >= 0 && index <= itemCount - 1);
|
'index not bigger than 0 and smaller than itemCount - 1');
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: itemHeight,
|
height: itemHeight,
|
||||||
child: Text('Item $index'),
|
child: Text('Item $index'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1795,20 +1795,19 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<int>(
|
home: ValueListenableBuilder<int>(
|
||||||
valueListenable: itemCount,
|
valueListenable: itemCount,
|
||||||
builder: (context, itemCount, child) {
|
builder: (context, itemCount, child) =>
|
||||||
return ScrollablePositionedList.builder(
|
ScrollablePositionedList.builder(
|
||||||
initialScrollIndex: min(100, itemCount - 1),
|
initialScrollIndex: min(100, itemCount - 1),
|
||||||
initialAlignment: 0,
|
itemCount: itemCount,
|
||||||
itemCount: itemCount,
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
assert(index >= 0 && index <= itemCount - 1,
|
||||||
assert(index >= 0 && index <= itemCount - 1);
|
'index not bigger than 0 and smaller than itemCount -1');
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: itemHeight,
|
height: itemHeight,
|
||||||
child: Text('Item $index'),
|
child: Text('Item $index'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1835,19 +1834,19 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<int>(
|
home: ValueListenableBuilder<int>(
|
||||||
valueListenable: itemCount,
|
valueListenable: itemCount,
|
||||||
builder: (context, itemCount, child) {
|
builder: (context, itemCount, child) =>
|
||||||
return ScrollablePositionedList.builder(
|
ScrollablePositionedList.builder(
|
||||||
initialScrollIndex: itemCount - 1,
|
initialScrollIndex: itemCount - 1,
|
||||||
itemCount: itemCount,
|
itemCount: itemCount,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
assert(index >= 0 && index <= itemCount - 1);
|
assert(index >= 0 && index <= itemCount - 1,
|
||||||
return SizedBox(
|
'index not bigger than 0 and smaller than itemCount -1');
|
||||||
height: itemHeight,
|
return SizedBox(
|
||||||
child: Text('Item $index'),
|
height: itemHeight,
|
||||||
);
|
child: Text('Item $index'),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1948,7 +1947,6 @@ void main() {
|
|||||||
tester,
|
tester,
|
||||||
itemCount: 2,
|
itemCount: 2,
|
||||||
initialAlignment: alignment,
|
initialAlignment: alignment,
|
||||||
initialIndex: 0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
@@ -1960,35 +1958,31 @@ void main() {
|
|||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.binding.window.physicalSizeTestValue =
|
||||||
const Size(screenWidth, screenHeight);
|
const Size(screenWidth, screenHeight);
|
||||||
final key = ValueNotifier<Key>(ValueKey('key'));
|
final key = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<Key>(
|
home: ValueListenableBuilder<Key>(
|
||||||
valueListenable: key,
|
valueListenable: key,
|
||||||
builder: (context, key, child) {
|
builder: (context, key, child) => Container(
|
||||||
return Container(
|
key: key,
|
||||||
key: key,
|
child: ScrollablePositionedList.builder(
|
||||||
child: ScrollablePositionedList.builder(
|
itemCount: 200,
|
||||||
itemCount: 200,
|
itemScrollController: itemScrollController,
|
||||||
itemScrollController: itemScrollController,
|
itemBuilder: (context, index) => SizedBox(
|
||||||
itemBuilder: (context, index) {
|
height: itemHeight,
|
||||||
return SizedBox(
|
child: Text('Item $index'),
|
||||||
height: itemHeight,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
key.value = ValueKey('newKey');
|
key.value = const ValueKey('newKey');
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
@@ -2003,9 +1997,9 @@ void main() {
|
|||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.binding.window.physicalSizeTestValue =
|
||||||
const Size(screenWidth, screenHeight);
|
const Size(screenWidth, screenHeight);
|
||||||
final outerKey = ValueNotifier<Key>(ValueKey('outerKey'));
|
final outerKey = ValueNotifier<Key>(const ValueKey('outerKey'));
|
||||||
final innerKey = GlobalKey();
|
final innerKey = GlobalKey();
|
||||||
final listKey = ValueNotifier<Key>(ValueKey(null));
|
final listKey = ValueNotifier<Key>(const ValueKey(null));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -2038,8 +2032,8 @@ void main() {
|
|||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
outerKey.value = ValueKey('newOuterKey');
|
outerKey.value = const ValueKey('newOuterKey');
|
||||||
listKey.value = ValueKey('newListKey');
|
listKey.value = const ValueKey('newListKey');
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
@@ -2053,33 +2047,29 @@ void main() {
|
|||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.binding.window.physicalSizeTestValue =
|
||||||
const Size(screenWidth, screenHeight);
|
const Size(screenWidth, screenHeight);
|
||||||
final key = ValueNotifier<Key>(ValueKey('key'));
|
final key = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<Key>(
|
home: ValueListenableBuilder<Key>(
|
||||||
valueListenable: key,
|
valueListenable: key,
|
||||||
builder: (context, key, child) {
|
builder: (context, key, child) => ScrollablePositionedList.builder(
|
||||||
return ScrollablePositionedList.builder(
|
key: key,
|
||||||
key: key,
|
itemCount: 10,
|
||||||
itemCount: 10,
|
itemScrollController: itemScrollController,
|
||||||
itemScrollController: itemScrollController,
|
itemBuilder: (context, index) => SizedBox(
|
||||||
itemBuilder: (context, index) {
|
height: itemHeight,
|
||||||
return SizedBox(
|
child: Text('Item $index'),
|
||||||
height: itemHeight,
|
),
|
||||||
child: Text('Item $index'),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
key.value = ValueKey('newKey');
|
key.value = const ValueKey('newKey');
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2087,35 +2077,31 @@ void main() {
|
|||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.binding.window.physicalSizeTestValue =
|
||||||
const Size(screenWidth, screenHeight);
|
const Size(screenWidth, screenHeight);
|
||||||
final key = ValueNotifier<Key>(ValueKey('key'));
|
final key = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<Key>(
|
home: ValueListenableBuilder<Key>(
|
||||||
valueListenable: key,
|
valueListenable: key,
|
||||||
builder: (context, key, child) {
|
builder: (context, key, child) => Container(
|
||||||
return Container(
|
key: key,
|
||||||
key: key,
|
child: ScrollablePositionedList.builder(
|
||||||
child: ScrollablePositionedList.builder(
|
itemCount: 100,
|
||||||
itemCount: 100,
|
itemScrollController: itemScrollController,
|
||||||
itemScrollController: itemScrollController,
|
itemBuilder: (context, index) => SizedBox(
|
||||||
itemBuilder: (context, index) {
|
height: itemHeight,
|
||||||
return SizedBox(
|
child: Text('Item $index'),
|
||||||
height: itemHeight,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
key.value = ValueKey('newKey');
|
key.value = const ValueKey('newKey');
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
@@ -2130,7 +2116,7 @@ void main() {
|
|||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.binding.window.physicalSizeTestValue =
|
||||||
const Size(screenWidth, screenHeight);
|
const Size(screenWidth, screenHeight);
|
||||||
final containerKey = ValueNotifier<Key>(ValueKey('key'));
|
final containerKey = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final scrollKey = GlobalKey();
|
final scrollKey = GlobalKey();
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
@@ -2138,29 +2124,25 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<Key>(
|
home: ValueListenableBuilder<Key>(
|
||||||
valueListenable: containerKey,
|
valueListenable: containerKey,
|
||||||
builder: (context, key, child) {
|
builder: (context, key, child) => Container(
|
||||||
return Container(
|
key: key,
|
||||||
key: key,
|
child: ScrollablePositionedList.builder(
|
||||||
child: ScrollablePositionedList.builder(
|
key: scrollKey,
|
||||||
key: scrollKey,
|
itemCount: 100,
|
||||||
itemCount: 100,
|
itemScrollController: itemScrollController,
|
||||||
itemScrollController: itemScrollController,
|
itemBuilder: (context, index) => SizedBox(
|
||||||
itemBuilder: (context, index) {
|
height: itemHeight,
|
||||||
return SizedBox(
|
child: Text('Item $index'),
|
||||||
height: itemHeight,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
containerKey.value = ValueKey('newKey');
|
containerKey.value = const ValueKey('newKey');
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
@@ -2184,18 +2166,15 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<ItemScrollController>(
|
home: ValueListenableBuilder<ItemScrollController>(
|
||||||
valueListenable: itemScrollControllerListenable,
|
valueListenable: itemScrollControllerListenable,
|
||||||
builder: (context, itemScrollController, child) {
|
builder: (context, itemScrollController, child) =>
|
||||||
return ScrollablePositionedList.builder(
|
ScrollablePositionedList.builder(
|
||||||
itemCount: 100,
|
itemCount: 100,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) => SizedBox(
|
||||||
return SizedBox(
|
height: itemHeight,
|
||||||
height: itemHeight,
|
child: Text('Item $index'),
|
||||||
child: Text('Item $index'),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -2236,35 +2215,29 @@ void main() {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ValueListenableBuilder<ItemScrollController>(
|
child: ValueListenableBuilder<ItemScrollController>(
|
||||||
valueListenable: topItemScrollControllerListenable,
|
valueListenable: topItemScrollControllerListenable,
|
||||||
builder: (context, itemScrollController, child) {
|
builder: (context, itemScrollController, child) =>
|
||||||
return ScrollablePositionedList.builder(
|
ScrollablePositionedList.builder(
|
||||||
itemCount: 100,
|
itemCount: 100,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) => SizedBox(
|
||||||
return SizedBox(
|
height: itemHeight,
|
||||||
height: itemHeight,
|
child: Text('Item $index'),
|
||||||
child: Text('Item $index'),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ValueListenableBuilder<ItemScrollController>(
|
child: ValueListenableBuilder<ItemScrollController>(
|
||||||
valueListenable: bottomItemScrollControllerListenable,
|
valueListenable: bottomItemScrollControllerListenable,
|
||||||
builder: (context, itemScrollController, child) {
|
builder: (context, itemScrollController, child) =>
|
||||||
return ScrollablePositionedList.builder(
|
ScrollablePositionedList.builder(
|
||||||
itemCount: 100,
|
itemCount: 100,
|
||||||
itemScrollController: itemScrollController,
|
itemScrollController: itemScrollController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) => SizedBox(
|
||||||
return SizedBox(
|
height: itemHeight,
|
||||||
height: itemHeight,
|
child: Text('Item $index'),
|
||||||
child: Text('Item $index'),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
+30
-36
@@ -270,7 +270,7 @@ void main() {
|
|||||||
initialAlignment: 0.5);
|
initialAlignment: 0.5);
|
||||||
|
|
||||||
unawaited(itemScrollController.scrollTo(
|
unawaited(itemScrollController.scrollTo(
|
||||||
index: 16, duration: scrollDuration, alignment: 1.0));
|
index: 16, duration: scrollDuration, alignment: 1));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(scrollDuration + scrollDurationTolerance);
|
await tester.pump(scrollDuration + scrollDurationTolerance);
|
||||||
@@ -477,7 +477,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('List can be keyed', (WidgetTester tester) async {
|
testWidgets('List can be keyed', (WidgetTester tester) async {
|
||||||
final key = ValueKey('key');
|
const key = ValueKey('key');
|
||||||
|
|
||||||
await setUpWidgetTest(tester, key: key);
|
await setUpWidgetTest(tester, key: key);
|
||||||
|
|
||||||
@@ -498,23 +498,20 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<int>(
|
home: ValueListenableBuilder<int>(
|
||||||
valueListenable: itemCount,
|
valueListenable: itemCount,
|
||||||
builder: (context, itemCount, child) {
|
builder: (context, itemCount, child) =>
|
||||||
return ScrollablePositionedList.separated(
|
ScrollablePositionedList.separated(
|
||||||
initialScrollIndex: 0,
|
itemCount: itemCount,
|
||||||
initialAlignment: 0,
|
itemScrollController: itemScrollController,
|
||||||
itemCount: itemCount,
|
itemPositionsListener: itemPositionsListener,
|
||||||
itemScrollController: itemScrollController,
|
itemBuilder: (context, index) => SizedBox(
|
||||||
itemPositionsListener: itemPositionsListener,
|
height: itemHeight,
|
||||||
itemBuilder: (context, index) => SizedBox(
|
child: Text('Item $index'),
|
||||||
height: itemHeight,
|
),
|
||||||
child: Text('Item $index'),
|
separatorBuilder: (context, index) => SizedBox(
|
||||||
),
|
height: separatorHeight,
|
||||||
separatorBuilder: (context, index) => SizedBox(
|
child: Text('Separator $index'),
|
||||||
height: separatorHeight,
|
),
|
||||||
child: Text('Separator $index'),
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -542,23 +539,20 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ValueListenableBuilder<int>(
|
home: ValueListenableBuilder<int>(
|
||||||
valueListenable: itemCount,
|
valueListenable: itemCount,
|
||||||
builder: (context, itemCount, child) {
|
builder: (context, itemCount, child) =>
|
||||||
return ScrollablePositionedList.separated(
|
ScrollablePositionedList.separated(
|
||||||
initialScrollIndex: 0,
|
itemCount: itemCount,
|
||||||
initialAlignment: 0,
|
itemScrollController: itemScrollController,
|
||||||
itemCount: itemCount,
|
itemPositionsListener: itemPositionsListener,
|
||||||
itemScrollController: itemScrollController,
|
itemBuilder: (context, index) => SizedBox(
|
||||||
itemPositionsListener: itemPositionsListener,
|
height: itemHeight,
|
||||||
itemBuilder: (context, index) => SizedBox(
|
child: Text('Item $index'),
|
||||||
height: itemHeight,
|
),
|
||||||
child: Text('Item $index'),
|
separatorBuilder: (context, index) => SizedBox(
|
||||||
),
|
height: separatorHeight,
|
||||||
separatorBuilder: (context, index) => SizedBox(
|
child: Text('Separator $index'),
|
||||||
height: separatorHeight,
|
),
|
||||||
child: Text('Separator $index'),
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user