Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support nested scroll view #88

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 65 additions & 14 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,47 @@ class _MyHomePageState extends State<MyHomePage> {
static const double maxHeight = 1000;
final random = math.Random();
final scrollDirection = Axis.vertical;
final nestedParentScrollViewKey = GlobalKey();

late AutoScrollController controller;
late List<List<int>> randomList;
bool nested = false;

@override
void initState() {
super.initState();
controller = AutoScrollController(
viewportBoundaryGetter: () =>
Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
axis: scrollDirection);
controller = _getController(nested);
randomList = List.generate(maxCount,
(index) => <int>[index, (maxHeight * random.nextDouble()).toInt()]);
}

AutoScrollController _getController(bool withScrollerKey) {
final scrollerKey = withScrollerKey ? nestedParentScrollViewKey : null;

return AutoScrollController(
viewportBoundaryGetter: () =>
Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
axis: scrollDirection,
scrollerWidgetKey: scrollerKey, // only need to assign in nested use case
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(
onPressed: () {
setState(() {
nested = !nested;
controller = _getController(nested);
});
},
icon: Text(nested ? 'Single' : 'Nested'),
iconSize: 50,
),
IconButton(
onPressed: () {
setState(() => counter = 0);
Expand All @@ -74,16 +94,7 @@ class _MyHomePageState extends State<MyHomePage> {
)
],
),
body: ListView(
scrollDirection: scrollDirection,
controller: controller,
children: randomList.map<Widget>((data) {
return Padding(
padding: EdgeInsets.all(8),
child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)),
);
}).toList(),
),
body: nested ? _buildNestedListView() : _buildSingleListView(),
floatingActionButton: FloatingActionButton(
onPressed: _nextCounter,
tooltip: 'Increment',
Expand All @@ -92,6 +103,46 @@ class _MyHomePageState extends State<MyHomePage> {
);
}

Widget _buildSingleListView() {
return ListView(
scrollDirection: scrollDirection,
controller: controller,
children: randomList.map<Widget>((data) {
return Padding(
padding: EdgeInsets.all(8),
child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)),
);
}).toList(),
);
}

Widget _buildNestedListView() {
return SingleChildScrollView(
key: nestedParentScrollViewKey,
scrollDirection: Axis.vertical,
controller: controller,
child: Column(
children: [
Padding(
padding: EdgeInsets.all(40),
child: Text('Nested Demo', style: TextStyle(fontSize: 48)),
),
ListView(
scrollDirection: scrollDirection,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: randomList.map<Widget>((data) {
return Padding(
padding: EdgeInsets.all(8),
child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)),
);
}).toList(),
),
],
),
);
}

int counter = -1;
Future _nextCounter() {
setState(() => counter = (counter + 1) % maxCount);
Expand Down
38 changes: 35 additions & 3 deletions lib/scroll_to_index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ abstract class AutoScrollController implements ScrollController {
factory AutoScrollController(
{double initialScrollOffset: 0.0,
bool keepScrollOffset: true,
GlobalKey? scrollerWidgetKey,
double? suggestedRowHeight,
ViewportBoundaryGetter viewportBoundaryGetter:
defaultViewportBoundaryGetter,
Expand All @@ -38,6 +39,7 @@ abstract class AutoScrollController implements ScrollController {
return SimpleAutoScrollController(
initialScrollOffset: initialScrollOffset,
keepScrollOffset: keepScrollOffset,
scrollerWidgetKey: scrollerWidgetKey,
suggestedRowHeight: suggestedRowHeight,
viewportBoundaryGetter: viewportBoundaryGetter,
beginGetter: axis == Axis.horizontal ? (r) => r.left : (r) => r.top,
Expand Down Expand Up @@ -92,6 +94,8 @@ abstract class AutoScrollController implements ScrollController {
class SimpleAutoScrollController extends ScrollController
with AutoScrollControllerMixin {
@override
final GlobalKey? scrollerWidgetKey;
@override
final double? suggestedRowHeight;
@override
final ViewportBoundaryGetter viewportBoundaryGetter;
Expand All @@ -103,6 +107,7 @@ class SimpleAutoScrollController extends ScrollController
SimpleAutoScrollController(
{double initialScrollOffset: 0.0,
bool keepScrollOffset: true,
this.scrollerWidgetKey,
this.suggestedRowHeight,
this.viewportBoundaryGetter: defaultViewportBoundaryGetter,
required this.beginGetter,
Expand All @@ -120,6 +125,8 @@ class SimpleAutoScrollController extends ScrollController
class PageAutoScrollController extends PageController
with AutoScrollControllerMixin {
@override
final GlobalKey? scrollerWidgetKey;
@override
final double? suggestedRowHeight;
@override
final ViewportBoundaryGetter viewportBoundaryGetter;
Expand All @@ -132,6 +139,7 @@ class PageAutoScrollController extends PageController
{int initialPage: 0,
bool keepPage: true,
double viewportFraction: 1.0,
this.scrollerWidgetKey,
this.suggestedRowHeight,
this.viewportBoundaryGetter: defaultViewportBoundaryGetter,
AutoScrollController? copyTagsFrom,
Expand All @@ -149,6 +157,7 @@ mixin AutoScrollControllerMixin on ScrollController
implements AutoScrollController {
@override
final Map<int, AutoScrollTagState> tagMap = <int, AutoScrollTagState>{};
GlobalKey? scrollerWidgetKey;
double? get suggestedRowHeight;
ViewportBoundaryGetter get viewportBoundaryGetter;
AxisValueGetter get beginGetter;
Expand Down Expand Up @@ -477,12 +486,35 @@ mixin AutoScrollControllerMixin on ScrollController

final renderBox = ctx.findRenderObject()!;
assert(Scrollable.of(ctx) != null);
final RenderAbstractViewport viewport =
RenderAbstractViewport.of(renderBox)!;
final revealedOffset = viewport.getOffsetToReveal(renderBox, alignment);
final RenderAbstractViewport? viewport = scrollerWidgetKey != null
? _getScrollerWidgetViewPort(scrollerWidgetKey!, renderBox)
: RenderAbstractViewport.of(renderBox);
assert(viewport != null);
final revealedOffset = viewport!.getOffsetToReveal(renderBox, alignment);

return revealedOffset;
}

RenderAbstractViewport? _getScrollerWidgetViewPort(
GlobalKey scrollerWidgetKey,
RenderObject? childItem,
) {
final scrollerWidgetCtx = scrollerWidgetKey.currentContext;
assert(scrollerWidgetCtx != null);
final scrollerRenderBox = scrollerWidgetCtx!.findRenderObject();
RenderAbstractViewport? viewportBeforeController;

while (childItem != null) {
if (childItem == scrollerRenderBox) {
return viewportBeforeController;
}
if (childItem is RenderAbstractViewport) {
viewportBeforeController = childItem;
}
childItem = childItem.parent as RenderObject?;
}
return null;
}
}

void _cancelAllHighlights([AutoScrollTagState? state]) {
Expand Down