quire-io / scroll-to-index

scroll to index with fixed/variable row height inside Flutter scrollable widget
MIT License
518 stars 105 forks source link

Scroll to index in nested CustomScrollView #22

Open ovidiu-anghelina opened 4 years ago

ovidiu-anghelina commented 4 years ago

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?

jerrywell commented 4 years ago

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

ovidiu-anghelina commented 4 years ago

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.

rahuldange09 commented 4 years ago

Hello @jerrywell any updates on this?

nhat108 commented 4 years ago

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