2

Context: I'll be having a couple of scrollable lists in my app and I always want to scroll them to the latest item whenever an item is added.

Problem: My ListView.builders and the places where items are added are going to be quite far apart in my widget tree. Passing around all those scroll controllers via constructors seems to be super awkward.

My Solution:As I'm practising with Provider at the moment, I came up with a working solution using Provider:

class ScrollControllerProvider with ChangeNotifier {
  ScrollController _paneController = ScrollController();
  //setting up all other controllers here later

  get paneController {
    return _paneController;
  }

  void scrollHistory() {
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      if (_paneController.hasClients) {
        _paneController.jumpTo(_paneController.position.maxScrollExtent);
      }
    });
  }
}

I'll add all scroll controllers to that provider and grab what I need, where I need it. It already works with one, but someone on reddit told me it's not a good idea, as scroll controllers should be disposed. Im not super knowledgeable on the topic of life cycle yet and find it difficult to assess this.

Questions: Is it really a bad idea to use Provider here? Can you help me to understand why? If yes, what is the best approach to solve this issue?

Joe
  • 311
  • 3
  • 17

2 Answers2

2

Provider is not the problem, using a disposable item inside a provider is. ScrollController is a disposable item related to its main Widget, or better to say its State.

If you want to notify your widgets about newly added items, create a variable inside the provider and listen to that variable in your widgets, then use your ScrollController to change the position.

To find out more about your question take a look at ScrollController class and Disposable class

Payam Asefi
  • 2,677
  • 2
  • 15
  • 26
  • Hey, thanks for answering! I don’t get the second paragraph yet. Let’s say a function triggers that add an item… what should happen in the provider? And how does that trigger the scrollcontroller? Really sorry to be a bit dense here… this is all a super new to me and I’m learning completely on my own – Joe Feb 13 '22 at 21:20
  • @Joe Generally, the provider is for sharing some kind of data between multiple pages, what you are doing is sharing a controller. for example, create a boolean variable showing if new data is added or not and then listen to that variable from your pages. – Payam Asefi Feb 13 '22 at 21:52
  • 1
    Thanks! Worked. I also added a more complete description of how to do it as my own answer. – Joe Feb 13 '22 at 22:46
1

For posterity, Payam Asefi pointed me in the right direction.

How I'm doing it now.

tldr; Provider contains a value that can be toggled and a method to toggle it. I provide the value where I can also access the scroll controler. If it is toggled, the scroll conroler is used. I provide the method to toggle the value where I add new items to the list.

item is added > value in provider is triggered > listeners realized the value has changed calling the build method > scroll controller is used to go to maxscrollextend.

Long answer with code:

Provider with a) a bool that can be toggled b) a method to toggle the bool c) a getter for the bool

Code:

class ScrollControllerToggles with ChangeNotifier {
  bool _historyPaneSwitch = true;

  get getTogglePaneSwitch {
    return _historyPaneSwitch;
  }

  void toggleHistoryPane() {
    _historyPaneSwitch = !_historyPaneSwitch;
    notifyListeners();
  }
}

In the widget I'm using the Listview.builder: a) I define a scroll controller, b) I use a function dependent on the _historyPaneSwitch inside that Provider. That funtion also uses the scroll controller to scroll the list to the end.

    void triggerScrollController() {
  bool scrollHistoryPane =
      Provider.of<ScrollControllerToggles>(context).getTogglePaneSwitch;
  WidgetsBinding.instance?.addPostFrameCallback((_) {
    if (paneController.hasClients) {
      paneController.jumpTo(paneController.position.maxScrollExtent);
    }
  });
}

In the widget adding new items to the list, I access the Provider again and grab the method to toggle "_historyPaneSwitch".

    Function scrollHistoryPane =
    Provider.of<ScrollControllerToggles>(context).toggleHistoryPane;

    void dayChange(Function scrollHistoryPane) {
    mainElementList.insert(0, MainElement(false, DateTime.now().toString()));
    scrollHistoryPane;
  }
Joe
  • 311
  • 3
  • 17