Using PageView with Bloc to programmatically 'swipe' pages. Prevent the bloc builder from building on certain states?

Issue

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));
  }
}

Solution

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).

Answered By – Simon Hutton

Answer Checked By – Marilyn (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *