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