Home > front end >  How best to make my scroll controllers available throughout the app?
How best to make my scroll controllers available throughout the app?

Time:02-14

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?

CodePudding user response:

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

CodePudding user response:

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;
  }
  • Related