PageController not set properly within the PageView module in Flutter. Positions not empty error popping up

1,177

Solution 1

The proper way to fix this issue for me was to wrap this part:

ShareBloc.getInstance().pageViewIndexStream.listen((index) {
      _controller.animateToPage(index,
          duration: Duration(milliseconds: 200), curve: Curves.easeInOut);
    });

With:

ShareBloc.getInstance().pageViewIndexStream.listen((index) {
      if (_controller.hasClients) { // <-- this is the fix!
        _controller.animateToPage(index,
            duration: Duration(milliseconds: 200), curve: Curves.easeInOut);
      }
    });

Solution 2

//This will run just once
@override
void initState() {
    super.initState();
    //This code is the same as your _onLoad method, you don't neet to run it on the build method anymore
    _controller = PageController();
    ShareBloc.getInstance().pageViewIndexStream.listen((index) {
      _controller.animateToPage(index,
          duration: Duration(milliseconds: 200), curve: Curves.easeInOut);
    });
}

just like the dispose method, the StatefulWidget has an initState method which runs once and only once (when it's inserted into the tree). After that when the build method runs again this code will not run.

Share:
1,177
Logan
Author by

Logan

Updated on December 21, 2022

Comments

  • Logan
    Logan over 1 year

    I started programming using the Flutter framework and immediately noticed an odd issue popping up (see below for more info).

    The problem occurs when: After returning to the MainScreen and trying to change from the PageView index by pressing on a button to switch from screens within the PageView, I get this error:

    [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: 'package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 110 pos 12: '_positions.isNotEmpty': ScrollController not attached to any scroll views.
    

    This is my MainScreen:

    class MainScreen extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => MainScreenState();
    }
    
    class MainScreenState extends State<MainScreen> {
      PageController _controller;
    
      void _onLoad() {
        _controller = PageController();
        ShareBloc.getInstance().pageViewIndexStream.listen((index) {
          _controller.animateToPage(index,
              duration: Duration(milliseconds: 200), curve: Curves.easeInOut);
        });
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        _onLoad();
    
        return PageView(
          controller: _controller,
          physics: NeverScrollableScrollPhysics(),
          children: [
            FirstScreen(),
            SecondScreen(),
          ],
        );
      }
    }
    

    This is my RxDart Sharable file:

    import 'package:rxdart/rxdart.dart';
    
    class ShareBloc {
      static ShareBloc _instance;
      BehaviorSubject<dynamic> _subjectCounter;
    
      static ShareBloc getInstance() {
        if (_instance == null) _instance = ShareBloc();
        return _instance;
      }
    
      ShareBloc() {
        _subjectCounter = new BehaviorSubject<dynamic>();
      }
    
      Stream<dynamic> get getStream => pageViewIndexStream.stream;
    
      void onShare(dynamic data) {
        _subjectCounter.sink.add(data);
      }
    
      void dispose() {
        if (_subjectCounter != null) {
          _subjectCounter.close();
          _instance = null;
        }
      }
    }
    

    Can someone tell me what I am doing wrong here?

    • EdwynZN
      EdwynZN almost 4 years
      The build method can rebuild several times (like when popping up and returning to the page). When that happens the _onLoad() runs again creating another page controller and creating a new listener without disposing the old one first, try moving the _onLoad to the initState and check if the error disappear
    • Logan
      Logan almost 4 years
      Yes, I also tried that approach as well. It seems that the previous controller is still in the PageView module. So, how can I replace it without getting the error popping up every single time when I enter my page?
  • Logan
    Logan almost 4 years
    I already found the fix for it! You need to check if the controller has clients before continuing. And I did put it in the initState method.
  • EdwynZN
    EdwynZN almost 4 years
    yeah if you run the code several times you need to check if there are any clients before adding another listener, in init state that can be redundant because you're creating the controller there and never adding a listener again. Also one question, where do you create pageViewIndexStream.stream? I see you used it in both the widget and the bloc but I never see where does that comes from
  • Logan
    Logan almost 4 years
    It is in the bloc file: Stream<dynamic> get getStream => pageViewIndexStream.stream;