flutterdartsetstateflutter-pageview

Why does Flutter PageView resets to page 0 after setState


Above the first Page that shows, I show a message that the user can swipe left to go to the next page. In onPageChanged I set a bool to dismiss the message and call setState. When I swipe to page 1, at the moment setState is called, the page resets to page 0.

When I remove the conditional text, the page swipes normally.

DartPad gist: https://dartpad.dev/?id=e30a98e4bc531d9cdc93780b6e47f972

Is this a bug or do I miss something?

Full example code below:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

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

class PageViewTestScreen extends StatefulWidget {
  PageViewTestScreen({super.key});

  @override
  State<PageViewTestScreen> createState() => _PageViewTestScreenState();
}

class _PageViewTestScreenState extends State<PageViewTestScreen> {
  PageController pageController = PageController(initialPage: 0);
  bool lastPage = false;
  bool userHasSlided = false;
  int numberOfPages = 5;

  @override
  void initState() {
    super.initState();
  }

  void onPageChanged(int index) {
    userHasSlided = true;

    lastPage = index == levensgebieden.length-1;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        floatingActionButton: lastPage
            ? FloatingActionButton(
                onPressed: () {},
                child: Icon(Icons.check),
              )
            : null,
        appBar: AppBar(
          foregroundColor: Colors.white,
          title: const Text("PageView bug?"),
        ),
        body: ListView(
          children: [
            const Text("Here are some pages"),
            const SizedBox(height: 6),
            if (!userHasSlided) const Text("Swipe left to continue."),
            SizedBox(
              height: 500,
              child: PageView(
                scrollBehavior: const ScrollBehavior().copyWith(dragDevices: {
                  PointerDeviceKind.touch,
                  PointerDeviceKind.mouse,
                  PointerDeviceKind.stylus,
                }),
                controller: pageController,
                onPageChanged: (int index) {
                  onPageChanged(index);
                },
                children: [
                  for (int i = 0; i < 5; i++) card(i),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }

  Widget card(int index) {
    return Card(
      child: SizedBox(
          height: 500, width: 200, child: Center(child: Text("Page: $index"))),
    );
  }
}

Solution

  • For cases like this, providing key helps the element tree .

    SizedBox(
      key: const Key("my_pageView"),
      height: 500,
      child: PageView(
    

    More about Keys.