Graham
Graham

Reputation: 5884

Terrible PageView performance

I have a PageView used with a BottomNavigationBar so that I can have swipeable and tappable tabs with a bottom bar rather than the normal navigation bar. Then I have two tabs/pages you can swipe between. One has a form with 4 UI elements and the other has no UI elements yet. Everything works fine but the performance of the PageView is very bad.

When I swipe between pages it is extremely slow and jumpy at first, definitely not the 60 frames per second promised by Flutter. Probably not even 30. After swiping several times though the performance gets better and better until its almost like a normal native app.

Below is my page class that includes the PageView, BottomNavigationBar, and logic connecting them. does anyone know how I can improve the performance of the PageView?

class _TabbarPageState extends State<TabbarPage> {

  int _index = 0;
  final controller = PageController(
    initialPage: 0,
    keepPage: true,
  );

  final _tabPages = <Widget>[
    StartPage(),
    OtherPage(),
  ];

  final _tabs = <BottomNavigationBarItem>[
    BottomNavigationBarItem(
      icon: Icon(Icons.play_arrow),
      title: Text('Start'),
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.accessibility_new),
      title: Text('Other'),
    )
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: PageView(
        controller: controller,
        children: _tabPages,
        onPageChanged: _onPageChanged,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: _tabs,
        onTap: _onTabTapped,
        currentIndex: _index,
      ),
      floatingActionButton: _index != 1
          ? null
          : FloatingActionButton(
              onPressed: () {},
              tooltip: 'Test',
              child: Icon(Icons.add),
            ),
    );
  }

  void _onTabTapped(int index) {
    controller.animateToPage(
      index,
      duration: Duration(milliseconds: 300),
      curve: Curves.ease,
    );
    setState(() {
      _index = index;
    });
  }

  void _onPageChanged(int index) {
    setState(() {
      _index = index;
    });
  }
}

Upvotes: 8

Views: 4731

Answers (4)

pranjul mishra
pranjul mishra

Reputation: 31

Try this hack, Apply viewportFraction to your controller with value 0.99(it can be 0.999 or 0.9999 hit and try until you get desired result)

final controller = PageController(
viewportFraction: 0.99 );

Upvotes: 3

GoInterface
GoInterface

Reputation: 95

I am new to flutter, please tell me if this is wrong.

Have the same problem, here is my effort. Wors for me.

class HomePage extends StatefulWidget {
  @override
  State createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var _currentIndex = 1;
  var _pageController = PageController(initialPage: 1);
  var _todoPage, _inProgressPage, _donePage, _userPage;

  @override
  void initState() {
    this._currentIndex = 1;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
        controller: this._pageController,
        onPageChanged: (index) {
          setState(() {
            this._currentIndex = index.clamp(0, 3);
          });
        },
        itemCount: 4,
        itemBuilder: (context, index) {
          if (index == 0) return this.todoPage();
          if (index == 1) return this.inProgressPage();
          if (index == 2) return this.donePage();
          if (index == 3) return this.userPage();
          return null;
        },
      ),
      bottomNavigationBar: buildBottomNavigationBar(),
    );
  }

  Widget buildBottomNavigationBar() {
    return BottomNavigationBar(
      showUnselectedLabels: false,
      items: [
        BottomNavigationBarItem(title: Text("待办"), icon: Icon(Icons.assignment)),
        BottomNavigationBarItem(title: Text("进行"), icon: Icon(Icons.blur_on)),
        BottomNavigationBarItem(title: Text("完成"), icon: Icon(Icons.date_range)),
        BottomNavigationBarItem(title: Text("我的"), icon: Icon(Icons.account_circle)),
      ],
      currentIndex: this._currentIndex,
      onTap: (index) {
        setState(() {
          this._currentIndex = index.clamp(0, 3);
        });
        _pageController.jumpToPage(this._currentIndex);
      },
    );
  }

  Widget todoPage() {
    if (this._todoPage == null) this._todoPage = TodoPage();
    return this._todoPage;
  }

  Widget inProgressPage() {
    if (this._inProgressPage == null) this._inProgressPage = InProgressPage();
    return this._inProgressPage;
  }

  Widget donePage() {
    if (this._donePage == null) this._donePage = DonePage();
    return this._donePage;
  }

  Widget userPage() {
    if (this._userPage == null) this._userPage = UserPage();
    return this._userPage;
  }
}

I just cache the pages that pageview hold. this REALLY smooth my pageview a lot, like native. but would prevent hotreload (ref: How to deal with unwanted widget build?),.

Upvotes: 0

Rebar
Rebar

Reputation: 1115

Sorry but Günter's answer didn't helped me! You have to set physics: AlwaysScrollableScrollPhysics() And your performance increases.

Worked for me 👍

Upvotes: 3

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657288

Ensure you performance test with profile or release builds only. Evaluating performance with debug builds is completely meaningless.

Upvotes: 3

Related Questions