Omar Awamry
Omar Awamry

Reputation: 1619

ScrollController not attached to any scroll views

I'm using CustomScrollView, and providing it with a controller. ScrollController works, I even added a listener to it and print out the position of the scroll view.

CustomScrollView(
    controller: _scrollController,

Now, all i'm trying to do is jump to position 50.0 inside initState() function.

_scrollController.jumpTo(50.0);

But, i get the error

scrollController not attached to any scroll views

Upvotes: 110

Views: 118273

Answers (21)

Ed H
Ed H

Reputation: 1684

Check if the scrollController is attached to a scroll view by using its hasClients property first.

if (_scrollController.hasClients) 
    _scrollController.jumpTo(50.0);



  // small change using "Recursion"
  void _scrollDown() {
    if (controllerListDay.hasClients) {
      controllerListDay.jumpTo(controllerListDay.position.maxScrollExtent);
      return;
    }

    Future.delayed(Duration(seconds: 1)).then((val) => _scrollDown());
  }

// just call _scrollDown() at any time

Upvotes: 166

Large
Large

Reputation: 717

My solution to this is to create a retry function if we do not have clients.

final _scrollController = ScrollController();
Scrollbar(
  controller: _scrollController,
  child: ListView(
  controller: _scrollController,
  ),
);

void scrollToPosition(double pos) async
{
    if (_scrollController.hasClients) {
       await _scrollController.animateTo(pos,
                duration: const Duration(milliseconds: 800), curve: Curves.easeInOut)
    } else {
  Future.delayed(Duration(milliseconds: 200), (){
    scrollToPosition(pos);
  });
 }
}

Ensure to call the scrollToPosition when the frame is available

WidgetsBinding.instance.addPostFrameCallback((_) {
    scrollToPosition(500);
});

Which this solution the app will retry it self, but a potensial infinity loop, so be clever and add a bail-out-logic or at least print something to see if it ever happens.

Upvotes: 0

Lakshmanan Murugappan
Lakshmanan Murugappan

Reputation: 21

I have added a timer along with a hasClients condition and it worked.

Timer timer=Timer.periodic(const Duration(milliseconds: 10), (timer) {
  if(pageCtrl.hasClients) {
      pageCtrl.jumpToPage(index);
      timer.cancel();
  }
});

Upvotes: 2

Abdallah Mahmoud
Abdallah Mahmoud

Reputation: 927

How To Fix At Web Platfrom

error:

The Scrollbar attempted to use the PrimaryScrollController. This ScrollController should be associated with the ScrollView that the Scrollbar is being applied to.A ScrollView with an Axis.vertical ScrollDirection on mobile platforms will automatically use the PrimaryScrollController if the user has not provided a ScrollController. To use the PrimaryScrollController explicitly, set ScrollView.primary to true for the Scrollable widget.

fixed

avoid use "Scrollbar" at web platform, while working fine in Mobile platfrom :

Scrollbar(
        child: scrollChild,
        isAlwaysShown: true,
      )

Upvotes: 0

tensor
tensor

Reputation: 783

If you are using ListView.builder inside ListView and attaching controller to ListView.builder, it will work only if you have ListView.builder.

Upvotes: 0

zzy
zzy

Reputation: 39

If you are using a ListView, GridView, or any other scrollable widget, you need to create a ScrollController and pass it to the controller parameter of the Scrollbar widget:

  final _scrollController = ScrollController();
  Scrollbar(
    controller: _scrollController,
     child: ListView(
     controller: _scrollController, // also pass to the scrollable widget

     ),
  );

It's means,the listView needs its parent component and its own life controller.

Upvotes: 1

Adnan Khan
Adnan Khan

Reputation: 510

Define a scroll conotroller;

final _scrollController = ScrollController();

Assign it to both controllers

Scrollbar(
 controller: _scrollController,
 child: ListView(
    controller: _scrollController,
    itemBuilder: ...
  )
) 

Upvotes: 0

Naveed Jamali
Naveed Jamali

Reputation: 668

Declare an object of controller like this.

 final _scrollController = ScrollController();

And then provide it to both, to Scrollbar and to it child (ListView in this case)

Scrollbar(
       thumbVisibility: true,
       controller: _scrollController,
       child: ListView(
              controller: _scrollController,
              children: [
                         Text("Hello World"),
                         Text("Hello World"),
                         Text("Hello World"),
                   ],
              ),

Upvotes: 0

MarcoFerreira
MarcoFerreira

Reputation: 378

I had this problem, but in my case was much simpler, althought I think it worth mention:

Don't forget to attach the controller to your widget. In my case I rearrange my screens and change the PageView. No widgets bindings would fix it because it was not point to any!

Upvotes: 0

Silas Ribas Martins
Silas Ribas Martins

Reputation: 302

I solving using the same controller in Scrollbar() e ListView() widgets.

Container(
    height: 140,
    child: Scrollbar(
        controller: listviewScrollbarController,
        child: ListView(
            scrollDirection: Axis.horizontal,
            controller: listviewScrollbarController,
            children: [
                // ....
            ]
        )
    )
)

Upvotes: 2

MobileMon
MobileMon

Reputation: 8651

I had this issue when using a ListView with a ScrollBar. The solution was to explicitly set the same controller to both the ScrollBar and ListView

final ScrollController _controller = ScrollController();

CupertinoScrollbar(
  controller: _controller,
  child: ListView.builder(
    controller: _controller,
                          

Upvotes: 4

carlosx2
carlosx2

Reputation: 1835

Delaying it is not the right solution. Better to wait till the tree is done building by using

WidgetsBinding.instance
        .addPostFrameCallback((_){});

sample

WidgetsBinding.instance.addPostFrameCallback((_) {
      if(pageController.hasClients){
              pageController.animateToPage(page index, duration: Duration(milliseconds: 1), curve: Curves.easeInOut);
      }
});

Upvotes: 72

Aziz Marzouki
Aziz Marzouki

Reputation: 34

I used this code to make the color of the appbar transparent at 0 position and black for more than 70

backgroundColor: !_scrollController.hasClients
            ? Colors.transparent
            : _scrollController.offset > 70
                ? Color.fromRGBO(7, 7, 7, 1)
                : Colors.transparent,

Upvotes: 0

Mimu Saha Tishan
Mimu Saha Tishan

Reputation: 2623

I have tried with above solutions but set ScrollController initialScrollOffset, checking hasClients and jumpTo, WidgetsBinding no one is working for me.

at last solved my problem by checking positions 'scrollcontroller.positions.length' like

if (_sc.positions.length == 0 || _sc.position.pixels == 0.0) {
    return Container();
}

You have to check controller positions to get rid of the error

Reference : https://api.flutter.dev/flutter/widgets/ScrollController/position.html

Upvotes: 3

Shariar Saimon
Shariar Saimon

Reputation: 943

Initialize the scrollController:

ScrollController _scrollController = ScrollController(); 

Use the code bellow where you want to scroll:

SchedulerBinding.instance.addPostFrameCallback((_) {
  _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
});

Upvotes: 5

Lucas De Lavra Pinto
Lucas De Lavra Pinto

Reputation: 1368

I resolved my problem using the same ScrollController to both controllers. First I create the ScrollController():

ScrollController scollBarController = ScrollController();

Inside my code:

Container(
    width: MediaQuery.of(context).size.width,
    height: MediaQuery.of(context).size.height,
    child: Scrollbar(
      controller: scollBarController,
      isAlwaysShown: true,
      child: StaggeredGridView.count(
        controller: scollBarController,
        crossAxisCount: 16,
        staggeredTiles: _staggeredTamanho,
        shrinkWrap: true,
        children: [
          ...
        ], //listStaggered,
        mainAxisSpacing: 7.0,
      ),
    ),
  ),

Upvotes: 11

Abdullah Khan
Abdullah Khan

Reputation: 1481

@carlosx2 answer is correct but if someone wonder where to put WigetsBinding. So here it is.

@override
void initState(){
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_){
    //write or call your logic
    //code will run when widget rendering complete
  });
}

Upvotes: 13

Saya Gamers
Saya Gamers

Reputation: 171

Use setState() method to bind to a scroll related widget (your listview etc.)

setState(() {
    topicsScrollController.animateTo(....);
});

Upvotes: 0

Ibn Masood
Ibn Masood

Reputation: 1113

This sometimes happens when you are attempting to bind a ScrollController to a widget that doesn't actually exist (yet). So it attempts to bind, but there is no ListView/ScrollView to bind to. Say you have this code:

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Expanded(
      flex: 8,
      child: Container(
        child: ListView.builder(
          controller: scrollController,
          itemCount: messages.length,
          itemBuilder: (context, index) {
            return MessageTile(message: messages[index]);
          }),
        ),
     ),
 ]),

Here we are building a ListView dynamically. Since we never declared this ListView before, the scrollController does not have anything to bind to. But this can easily be fixed:

// first declare the ListView and ScrollController

final scrollController = ScrollController(initialScrollOffset: 0);
ListView list = ListView.builder(
  controller: scrollController,
  itemCount: messages.length,
  itemBuilder: (context, index) {
    return MessageTile(message: messages[index]);
  }
);

// then inside your build function just reference the already built list

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Expanded(
      flex: 8,
      child: Container(
         child: list,
      ),
    ),
  ]),

Upvotes: 3

Yerkebulan Duisebay
Yerkebulan Duisebay

Reputation: 437

I found working solution, but frankly the method is not best practice, I suppose. In my case controller.jumpTo() was called before it was attached to any scroll view. In order to solve problem I delayed some milliseconds and then call .jumpTo(), because build will be called and controller will be attached to any scroll view.

Future.delayed(Duration(milliseconds: <some time, ex: 100>), () {
         _scrollController.jumpTo(50.0);
        });

I full agree that it is bad solution, but It can solve problem.

Upvotes: 0

boformer
boformer

Reputation: 30103

To set the initial position of a ScrollController, use the initialScrollOffset property:

_scrollController = ScrollController(initialScrollOffset: 50.0);

Upvotes: 29

Related Questions