4

I've built a PageView and want to control page swiping programmatically using a Bloc.

I'm using a PageController and listening for my bloc's ShowPage state. Then calling pageController.animateTo to animate the PageView to the desired page.

The bloc builder builds the PageView.

I suspect the problem I'm having is caused by the BlocBuilder building a new PageView on every state change and thus negating the pageController.animateTo.

My question is firstly ... can the BlockBuilder be prevented from firing/building for certain states (so it doesn't 'overwrite' my pageController changes). or Is there a better (correct) way of implementing a PageView with bloc?

I've copied the screen code the basic cubit below for info.

    class _TestScreenState extends State<TestScreen> {
  final PageController _pageController = PageController(initialPage: 4);
  double page = 0;

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<TestCubit, TestState>(listener: (context, state) async {
      if (state is DeviceLogsInitial) {
        await _animateTo(3);
      }
      if (state is ShowPage) {
        await _animateTo(state.page);
      }
    }, builder: (context, state) {
     // _log.info('Building dialog : page ${_pageController.page}');
      return Scaffold(
        appBar: AppBar(
          title: const Text('Test'),// ${_pageController.page}'),
          actions: [
            TextButton(
                onPressed: () {
                  page = _nextPage(page);
                  BlocProvider.of<TestCubit>(context).animate(page);
                },
                child: const Text('swipe'))
          ],
        ),
        body: PageView(
          physics: const NeverScrollableScrollPhysics(),
          controller: _pageController,
          children: const [
            Page0(),
            Page1(),
            Page2(),
            Page3(),
          ],
        ),
      );
    });
  }

  double _nextPage(double page) {
    if (page > 3) {
      page = 0;
    } else {
      page = page + 1;
    }
    return page;
  }

  Future<void> _animateTo(double _page) async {
    _pageController.animateTo(
      _page,
      duration: const Duration(milliseconds: 400),
      curve: Curves.easeInOut,
    );
  }

class TestCubit extends Cubit<TestState> {
  TestCubit() : super(TestInitial());

  Future<void> animate(double page) async {
    emit(ShowPage(page));
  }
}
Simon Hutton
  • 1,677
  • 3
  • 22
  • 35
  • I believe you are using the `flutter_bloc` package, it provides a build when method where you have control to rebuild or not, have you tried that ? – GoodSp33d Jan 24 '22 at 01:04
  • 1
    That's a great call thank you. A feature that I wasn't aware of and will use in the future. However my issue seems to have been caused simply by me not calling the correct `PageController` method. I should be calling `pageController.animateToPage` ! (The documentation and 'tutorials' are particularly bad on this) . Thanks for your help. – Simon Hutton Jan 24 '22 at 15:17
  • Ah ! glad you caught it. I do that myself a lot of times but when I start posting a question on this forum I will sometimes find a hint to the solution, guess its some sort of rubber duck programming :D – GoodSp33d Jan 25 '22 at 05:24

1 Answers1

2

This is simply a case of using the wrong PageController method for animating between pages.

The method you should be calling is pageController.animateToPage and not pageController.animateTo.

You could also use jumpToPage if you didn't require animation (and not jumpTo which would cause a similar issue).

Simon Hutton
  • 1,677
  • 3
  • 22
  • 35