chore: linting

This commit is contained in:
Gordon Hayes
2021-10-18 10:51:06 +02:00
parent 3a7343904d
commit 51e0b90f94
12 changed files with 333 additions and 358 deletions
@@ -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;
} }
@@ -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';
}
} }
@@ -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)''';
} }
@@ -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 {
@@ -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 -
@@ -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();
} }
} }
@@ -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,
@@ -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,
@@ -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);
@@ -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'), ),
); ),
},
);
},
), ),
), ),
], ],
@@ -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'), ),
),
);
},
), ),
), ),
); );