3

I make error when using animateToPage

ScrollController attached to multiple scroll views. 'package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 109 pos 12: '_positions.length == 1'

I try to use _pageController.hasClients but it not working.

Please have a look in my code-

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

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

class _MyAppState extends State {
  PageController _pageController;
  ScrollController _scrollController;
  double offset = 0.0;

  @override
  void initState(){
    super.initState();
    _pageController = PageController(initialPage: 0);
    _scrollController = ScrollController();
    _scrollController.addListener(this.swapPageListener);
  }

  void swapPageListener() {
    offset = _scrollController.offset;
    _pageController.hasClients
    if (offset > _scrollController.position.maxScrollExtent + 100) {
      _pageController.animateToPage(1, duration: Duration(milliseconds: 500), curve: Curves.ease);
    }
    if (offset < _scrollController.position.minScrollExtent - 100) {
      _pageController.animateToPage(0, duration: Duration(milliseconds: 500), curve: Curves.ease);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: PageView.builder(
            controller: _pageController,
            scrollDirection: Axis.vertical,
            itemCount: 2,
            physics: PageScrollPhysics(),
            itemBuilder: (context, idx) {
              if(idx == 0) {
                return _screen1();
              } else {
                return _screen2();
              }
            }
        ),
      ),
    );
  }

  Widget _screen1() {
    return SingleChildScrollView(
      physics: BouncingScrollPhysics(),
      controller: _scrollController,
      child: Container(
        height: 1000.0,
        color: Colors.red,
        child: Center(
          child: Text('screen 1'),
        ),
      ),
    );
  }

  Widget _screen2() {
    return SingleChildScrollView(
      physics: BouncingScrollPhysics(),
      controller: _scrollController,
      child: Container(
        height: 1000.0,
        color: Colors.green,
        child: Center(
          child: Text('screen 2'),
        ),
      ),
    );
  }
}

Thanks in advance

Hello, I have found the solution to this problem

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

PageController _pageController;

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

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

class _MyAppState extends State<MyApp> {

  @override
  void initState(){
    super.initState();
    _pageController = PageController(initialPage: 0);
  }


  // @override
  // void dispose() {
  //   super.dispose();
  //   _pageController.dispose();
  // }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: PageView.builder(
          controller: _pageController,
          scrollDirection: Axis.vertical,
          itemCount: 2,
          physics: NeverScrollableScrollPhysics(),
          itemBuilder: (context, idx) {
            return Screen(pageKey: idx,);
          }
        ),
      ),
    );
  }
}

class Screen extends StatefulWidget {
  Screen({Key key, this.pageKey}) : super(key: key);
  final pageKey;

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

class _ScreenState extends State<Screen> {
  ScrollController _scrollController;

  @override
  void initState(){
    super.initState();
    _scrollController = ScrollController();
    _scrollController.addListener(this.swapPageListener);
  }

  void swapPageListener() {
    if (_scrollController.offset > _scrollController.position.maxScrollExtent + 100) {
      _pageController.animateToPage(1, duration: Duration(milliseconds: 400), curve: Curves.easeInOut);
    }
    if (_scrollController.offset < _scrollController.position.minScrollExtent - 100) {
      _pageController.animateToPage(0, duration: Duration(milliseconds: 400), curve: Curves.easeInOut);
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      physics: BouncingScrollPhysics(),
      controller: _scrollController,
      child: Container(
        height: 1000.0,
        color: Colors.green,
        child: Center(
          child: Text('screen ${widget.pageKey}'),
        ),
      ),
    );
  }
}
Jubei Yagyu
  • 121
  • 2
  • 11

2 Answers2

2

please check out your solution

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

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

class _MyAppState extends State {
  PageController _pageController;
  ScrollController _scrollController;
  double offset = 0.0;

  @override
  void initState(){
    super.initState();
    _pageController = PageController(initialPage: 0);
    _scrollController = ScrollController();
    _scrollController.addListener(this.swapPageListener);

  }

  void swapPageListener() {

    offset = _scrollController.offset;
    setState(() {
      _pageController.hasClients;
      if (offset > _scrollController.position.maxScrollExtent + 100) {
        _pageController.animateToPage(1, duration: Duration(milliseconds: 500), curve: Curves.ease);
      }
      if (offset <_scrollController.position.minScrollExtent - 100) {
        _pageController.animateToPage(0, duration: Duration(milliseconds: 500), curve: Curves.ease);
      }
    });

  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: PageView.builder(
            controller: _pageController,
            scrollDirection: Axis.vertical,
            pageSnapping: true,
            itemCount: 2,
            physics: PageScrollPhysics(),
            itemBuilder: (context, idx) {
              if(idx == 0) {
                return _screen1();
              } else {
                return _screen2();
              }
            }
        ),
      ),
    );
  }

  Widget _screen1() {
    return SingleChildScrollView(
      physics: BouncingScrollPhysics(),
      controller: _scrollController,
      child: Container(
        height: 1000.0,
        color: Colors.red,
        child: Center(
          child: Text('screen 1'),
        ),
      ),
    );
  }

  Widget _screen2() {
    return SingleChildScrollView(
      physics: BouncingScrollPhysics(),
      controller: _scrollController,
      child: Container(
        height: 1000.0,
        color: Colors.green,
        child: Center(
          child: Text('screen 2'),
        ),
      ),
    );
  }
}
Jahidul Islam
  • 11,435
  • 3
  • 17
  • 38
  • I tried your code and it still got the error. – Jubei Yagyu Apr 18 '21 at 08:15
  • copy my code and paste it I hope working. In my pc, it worked – Jahidul Islam Apr 18 '21 at 08:16
  • yes it works but it will still return an error – Jubei Yagyu Apr 18 '21 at 08:18
  • please uninstall your previous app and re-run it, from my end I have found no error – Jahidul Islam Apr 18 '21 at 08:20
  • And have look in your swapPageListener() method and wrap your code in set state – Jahidul Islam Apr 18 '21 at 08:22
  • i tried setState(() { _pageController.animateToPage(1, duration: Duration(milliseconds: 500), curve: Curves.ease); }); I tried uninstalling. not working too. – Jubei Yagyu Apr 18 '21 at 08:25
  • missed out please try with this, setState(() { offset = _scrollController.offset; _pageController.hasClients; if (offset > _scrollController.position.maxScrollExtent + 100) { _pageController.animateToPage(1, duration: Duration(milliseconds: 500), curve: Curves.ease); } if (offset <_scrollController.position.minScrollExtent - 100) { _pageController.animateToPage(0, duration: Duration(milliseconds: 500), curve: Curves.ease); } }); – Jahidul Islam Apr 18 '21 at 08:26
  • you just replace your swapPageLIstener code with this void swapPageListener() { setState(() { offset = _scrollController.offset; _pageController.hasClients; if (offset > _scrollController.position.maxScrollExtent + 100) { _pageController.animateToPage(1, duration: Duration(milliseconds: 500), curve: Curves.ease); } if (offset <_scrollController.position.minScrollExtent - 100) { _pageController.animateToPage(0, duration: Duration(milliseconds: 500), curve: Curves.ease); } }); } – Jahidul Islam Apr 18 '21 at 08:30
  • thanks you for support, i tried all your fix but it not working. i mean it working on screen , but it still shows the error at the debug console – Jubei Yagyu Apr 18 '21 at 08:34
  • i edited the answer please check out one more time – Jahidul Islam Apr 18 '21 at 08:52
  • not working too sir. I posted on github https://github.com/flutter/flutter/issues/80664. Hope someone will help with this issue. – Jubei Yagyu Apr 18 '21 at 09:06
  • please check my GitHub comment, i found a lot of inconsistency in your code – Jahidul Islam Apr 18 '21 at 09:10
0

In my case I reuse a same PageView in several pages, and whenever I called .animateToPage(), it showed the error, and the mistake I did is simple.

Mistake:

static final PageController _pageController = PageController();

Fix:

late PageController _pageController;

 @override
  void initState() {
     _pageController = PageController();
...

Here I just initialized new controllers for each page, which calls this common page view widget when it's initialized. The same applies for the ScrollControllers depend on your case.

Rajesh
  • 3,562
  • 7
  • 24
  • 43