chore: linting
This commit is contained in:
+2
-2
@@ -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;
|
||||
}
|
||||
|
||||
+7
-6
@@ -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';
|
||||
}
|
||||
|
||||
+3
-3
@@ -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)''';
|
||||
}
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+23
-21
@@ -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 -
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -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,
|
||||
|
||||
+82
-80
@@ -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,
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+138
-165
@@ -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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
+30
-36
@@ -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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user