quire-io / scroll-to-index

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

Doesn't work when put inside CustomScrollView with SliverToBoxAdapter #41

Closed khoadng closed 3 years ago

khoadng commented 3 years ago

I modified the example a bit to reproduce the issue.


//Copyright (C) 2019 Potix Corporation. All Rights Reserved.
//History: Tue Apr 24 09:29 CST 2019
// Author: Jerry Chen

import 'dart:math' as math;

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Scroll To Index Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Scroll To Index Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const maxCount = 100;
  final random = math.Random();
  final scrollDirection = Axis.vertical;

  AutoScrollController controller;
  List<List<int>> randomList;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: ListView(
              shrinkWrap: true,
              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(),
            ),
          )
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _scrollToIndex,
        tooltip: 'Increment',
        child: Text(counter.toString()),
      ),
    );
  }

  int counter = -1;
  Future _scrollToIndex() async {
    setState(() {
      counter++;

      if (counter >= maxCount) counter = 0;
    });

    await controller.scrollToIndex(counter,
        preferPosition: AutoScrollPosition.begin);
    controller.highlight(counter);
  }

  Widget _getRow(int index, double height) {
    return _wrapScrollTag(
        index: index,
        child: Container(
          padding: EdgeInsets.all(8),
          alignment: Alignment.topCenter,
          height: height,
          decoration: BoxDecoration(
              border: Border.all(color: Colors.lightBlue, width: 4),
              borderRadius: BorderRadius.circular(12)),
          child: Text('index: $index, height: $height'),
        ));
  }

  Widget _wrapScrollTag({int index, Widget child}) => AutoScrollTag(
        key: ValueKey(index),
        controller: controller,
        index: index,
        child: child,
        highlightColor: Colors.black.withOpacity(0.1),
      );
}
ariefwijaya commented 3 years ago

@khoadng you should attach the AutoScrollController to CustomScrollView Controller. You forgot it. Like this:

CustomScrollView(
                controller: **_autoScrollController**, //Attach the controller 
                ...
                _wrapScrollTag
                ...
                _wrapScrollTag
                ...
 );
khoadng commented 3 years ago

@ariefwijaya Thanks for the suggestion!

I tried to attach the the controller like you said.

      body: CustomScrollView(
        controller: controller,
        slivers: [
          SliverToBoxAdapter(
            child: ListView(
              shrinkWrap: true,
              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(),
            ),
          )
        ],
      ),

but got the exception ScrollController attached to multiple scroll views. So I removed the controller inside ListView.

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

It works! But unfortunately, you can't scroll with your finger anymore.

ariefwijaya commented 3 years ago

It works! But unfortunately, you can't scroll with your finger anymore.

@khoadng You can't scroll it because you used ListView vertical Inside CustomScrollView. Don't do it if you want that behaviour. Instead, use SliverList and SliverChildBuilderDelegate for the best performance.

body: CustomScrollView(
        controller: controller,
        slivers: [
         /*SliverToBoxAdapter(
            child: ListView(
              shrinkWrap: true,
              scrollDirection: scrollDirection,
              children: randomList.map<Widget>((data) {
                return Padding(
                  padding: EdgeInsets.all(8),
                  child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)),
                );
              }).toList(),
            ),
          )*/

          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, i){
                 final data = randomList[i];
                 return Padding(
                  padding: EdgeInsets.all(8),
                  child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)),
                );
              },
              childCount: randomList.length,
            ),
          )
        ],
      ),
indianictarun commented 3 years ago

@ariefwijaya Thanks for the suggestion!

I tried to attach the the controller like you said.

      body: CustomScrollView(
        controller: controller,
        slivers: [
          SliverToBoxAdapter(
            child: ListView(
              shrinkWrap: true,
              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(),
            ),
          )
        ],
      ),

but got the exception ScrollController attached to multiple scroll views. So I removed the controller inside ListView.

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

It works! But unfortunately, you can't scroll with your finger anymore.

To Keep scroll working use this two lines inside ListView: physics: const NeverScrollableScrollPhysics(), shrinkWrap: true,

jerrywell commented 3 years ago

@indianictarun @ariefwijaya thanks for help.

@khoadng, does above suggestions work?

khoadng commented 3 years ago

@jerrywell Sorry I haven't tried these solutions yet since I’m swamped with my current work! I will try them this weekend and get back to you later.

khoadng commented 3 years ago

Works great! I am closing this now.