scroll-to-index icon indicating copy to clipboard operation
scroll-to-index copied to clipboard

Scroll to index in nested CustomScrollView

Open ovidiu-anghelina opened this issue 6 years ago • 4 comments

I have a custom widget that builds a CustomScrollView with form fields, with each of the form fields wrapped in AutoScrollTag. This works well on its own, when I navigate to a Route that has this custom widget as its child - I can scroll to any of the form fields and highlight them.

I have a use case where a Route has a SingleChildScrollView as its child, with a header followed by my custom widget. For this page to scroll as expected, the CustomScrollView in my custom widget is using physics: NeverScrollableScrollPhysics() and shrinkWrap: true. The problem is, scrollToIndex() no longer scrolls to the specified field in this scenario - the highlighting still works even though the field is not on the screen.

I've tried assigning an AutoScrollController to the SingleChildScrollView and using the same controller with the AutoScrollTag widgets, but that didn't make a difference.

I also tried setting the parentController of the AutoScrollController assigned to the CustomScrollView to that of the SingleChildScrollView, but that also didn't make a difference.

What is the correct way, if any, to achieve this?

ovidiu-anghelina avatar Feb 13 '20 11:02 ovidiu-anghelina

not quite sure what the actual case you got. can you provide the sample code, so i can reproduce it?

jerrywell avatar Feb 14 '20 02:02 jerrywell

Here it is:

import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';

void main() {
  runApp(MaterialApp(
    theme: ThemeData.light(),
    home: Builder(
      builder: (BuildContext context) => Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RaisedButton(
            child: const Text("Not nested - working"),
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(
                builder: (BuildContext context) => CustomWidget(nested: false),
              ));
            },
          ),
          RaisedButton(
            child: const Text("Nested - not working"),
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(
                builder: (BuildContext context) => CustomWidget(nested: true),
              ));
            },
          ),
        ],
      ),
    ),
  ));
}

class CustomWidget extends StatelessWidget {
  final AutoScrollController autoScrollController = AutoScrollController();
  final bool nested;

  CustomWidget({@required this.nested});

  @override
  Widget build(BuildContext context) {

    Widget body = CustomScrollView(
      controller: autoScrollController,
      physics: nested ? const NeverScrollableScrollPhysics() : null,
      shrinkWrap: nested,
      slivers: <Widget>[
        SliverList(
          delegate: SliverChildBuilderDelegate((BuildContext context, int index) => AutoScrollTag(
            key: ValueKey(index),
            controller: autoScrollController,
            index: index,
            highlightColor: Colors.redAccent,
            child: Padding(
              padding: const EdgeInsets.symmetric(vertical: 5),
              child: Container(
                height: 40,
                width: MediaQuery.of(context).size.width,
                color: index % 2 == 0 ? Colors.blueGrey : Colors.grey,
              ),
            ),
          ), childCount: 200),
        )
      ],
    );

    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.looks_one),
            onPressed: () {
              autoScrollController.scrollToIndex(50).then((_) {
                autoScrollController.highlight(50);
              });
            },
          ),
          IconButton(
            icon: Icon(Icons.looks_two),
            onPressed: () {
              autoScrollController.scrollToIndex(100).then((_) {
                autoScrollController.highlight(100);
              });
            },
          ),
        ],
      ),
      body: SafeArea(
        child: !nested ? body : SingleChildScrollView(
          child: body,
        ),
      ),
    );
  }
}

In the actual app, there are several thousand lines of code of logic and layout within that custom widget building a CustomScrollView, which is why it's very important that I can actually nest it in another ScrollView when needed.

While the sample above doesn't make much sense on its own, it accurately replicates the relevant part of the widget hierarchy in the real app.

ovidiu-anghelina avatar Feb 14 '20 12:02 ovidiu-anghelina

Hello @jerrywell any updates on this?

rahuldange09 avatar Apr 20 '20 16:04 rahuldange09

My answer is here: https://stackoverflow.com/a/61709995/11206617 Thanks me later!

nhat108 avatar May 10 '20 09:05 nhat108