当使用BottomNavigationBar导航时,如何在颤动中保留窗口小部件状态?

Art*_*lch 22 dart flutter

我正在开发一个Flutter应用程序,当使用BottomNavigationBar时,它会在从一个屏幕导航到另一个屏幕时保留状态,然后再返回.就像它适用于Spotify移动应用程序一样; 如果您在其中一个主屏幕上导航层次结构中导航到某个级别,通过底部导航栏更改屏幕,然后更改回旧屏幕,将保留用户在该层次结构中的位置,包括保留国家.

我把头靠在墙上,尝试了各种不同的事情而没有成功.

我想知道如何阻止页面pageChooser(),当用户点击BottomNavigationBar项目时切换,重建自己,而是保留他们已经找到自己的状态(页面都是有状态的小部件).

import 'package:flutter/material.dart';
import './page_plan.dart';
import './page_profile.dart';
import './page_startup_namer.dart';

void main() => runApp(new Recipher());

class Recipher extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Pages();
  }
}

class Pages extends StatefulWidget {
  @override
  createState() => new PagesState();
}

class PagesState extends State<Pages> {
  int pageIndex = 0;


  pageChooser() {
    switch (this.pageIndex) {
      case 0:
        return new ProfilePage();
        break;

      case 1:
        return new PlanPage();
        break;

      case 2:
        return new StartUpNamerPage(); 
        break;  

      default:
        return new Container(
          child: new Center(
            child: new Text(
              'No page found by page chooser.',
              style: new TextStyle(fontSize: 30.0)
              )
            ),
          );     
    }
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        body: pageChooser(),
        bottomNavigationBar: new BottomNavigationBar(
          currentIndex: pageIndex,
          onTap: (int tappedIndex) { //Toggle pageChooser and rebuild state with the index that was tapped in bottom navbar
            setState(
              (){ this.pageIndex = tappedIndex; }
              ); 
            },
          items: <BottomNavigationBarItem>[
            new BottomNavigationBarItem(
              title: new Text('Profile'),
              icon: new Icon(Icons.account_box)
              ),
              new BottomNavigationBarItem(
                title: new Text('Plan'),
                icon: new Icon(Icons.calendar_today)
              ),
                new BottomNavigationBarItem(
                title: new Text('Startup'),
                icon: new Icon(Icons.alarm_on)
              )
            ],
          )
      )
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

N.K*_*K.T 82

为了保持状态BottomNavigationBar,您可以使用IndexedStack

    @override
      Widget build(BuildContext context) {
        return Scaffold(
          bottomNavigationBar: BottomNavigationBar(
            onTap: (index) {
              setState(() {
                current_tab = index;
              });
            },
            currentIndex: current_tab,
            items: [
              BottomNavigationBarItem(
                ...
              ),
              BottomNavigationBarItem(
                ...
              ),
            ],
          ),
          body: IndexedStack(
            children: <Widget>[
              PageOne(),
              PageTwo(),
            ],
            index: current_tab,
          ),
        );
      }
Run Code Online (Sandbox Code Playgroud)

  • 但它有性能问题。IndexedStack 最初加载所有选项卡,这是不必要的。 (15认同)
  • 工作起来就像一个魅力,几乎没有样板。解决这种行为的好办法 (6认同)
  • 这应该是公认的答案。效果很好。@ArtBelch:如果您将此答案标记为正确答案,将会帮我们大家一个忙。:) (3认同)
  • @JayaPrakash 您是否找到了不立即构建所有堆栈的解决方案? (2认同)

Tay*_*yan 31

聚会迟到了,但我有一个简单的解决方案。PageView小部件与AutomaticKeepAliveClinetMixin.

它的美妙之处在于它在您单击它之前不会加载任何选项卡。


包含以下内容的页面BottomNavigationBar

var _selectedPageIndex;
List<Widget> _pages;
PageController _pageController;

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

  _selectedPageIndex = 0;
  _pages = [
    //The individual tabs.
  ];

  _pageController = PageController(initialPage: _selectedPageIndex);
}

@override
void dispose() {
  _pageController.dispose();

  super.dispose();
}

@override
Widget build(BuildContext context) {
  ...
    body: PageView(
      controller: _pageController,
      physics: NeverScrollableScrollPhysics(),
      children: _pages,
    ),
   bottomNavigationBar: BottomNavigationBar(
      ...
      currentIndex: _selectedPageIndex,
      onTap: (selectedPageIndex) {
        setState(() {
          _selectedPageIndex = selectedPageIndex;
          _pageController.jumpToPage(selectedPageIndex);
        });
      },
  ...
}
Run Code Online (Sandbox Code Playgroud)

个人标签:

class _HomeState extends State<Home> with AutomaticKeepAliveClientMixin<Home> {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    //Notice the super-call here.
    super.build(context);
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

我在这里制作了一个关于它的视频。

  • 效果真的很棒谢谢!所有_pages并不像IndexedStack那样首先加载,而是只是保存。 (4认同)
  • 工作完美...每个页面单独加载并维护状态。 (2认同)

小智 9

使用AutomaticKeepAliveClientMixin强制不处理选项卡内容。

class PersistantTab extends StatefulWidget {
  @override
  _PersistantTabState createState() => _PersistantTabState();
}

class _PersistantTabState extends State<PersistantTab> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    return Container();
  }

  // Setting to true will force the tab to never be disposed. This could be dangerous.
  @override
  bool get wantKeepAlive => true;
}
Run Code Online (Sandbox Code Playgroud)

为了确保不需要持久化选项卡时,请使其wantKeepAlive返回一个类变量。您必须致电updateKeepAlive()以更新保持活动状态。

动态保持活动的示例:

// class PersistantTab extends StatefulWidget ...

class _PersistantTabState extends State<PersistantTab>
    with AutomaticKeepAliveClientMixin {
  bool keepAlive = false;

  @override
  void initState() {
    doAsyncStuff();
  }

  Future doAsyncStuff() async {
    keepAlive = true;
    updateKeepAlive();
    // Keeping alive...

    await Future.delayed(Duration(seconds: 10));

    keepAlive = false;
    updateKeepAlive();
    // Can be disposed whenever now.
  }

  @override
  bool get wantKeepAlive => keepAlive;

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 从文档中:“他们的构建方法必须调用 super.build”,您的答案中缺少该内容。 (5认同)

Tec*_*are 7

使用“ IndexedStack Widget ”和“ Bottom Navigation Bar Widget ”来保持屏幕/页面/小部件的状态

IndexedStack提供小部件列表和要显示的小部件索引,因为IndexedStack 一次显示列表中的单个小部件。

final List<Widget> _children = [
    FirstClass(),
    SecondClass()
  ];

Scaffold(
  body: IndexedStack(
    index: _selectedPage,
    children: _children,
  ),
  bottomNavigationBar: BottomNavigationBar(
    ........
    ........
  ), 
);
Run Code Online (Sandbox Code Playgroud)


Hem*_*Raj 6

不必每次运行pageChooser都返回新实例,而是创建一个实例并将其返回。

例:

class Pages extends StatefulWidget {
  @override
  createState() => new PagesState();
}

class PagesState extends State<Pages> {
  int pageIndex = 0;

  // Create all the pages once and return same instance when required
  final ProfilePage _profilePage = new ProfilePage(); 
  final PlanPage _planPage = new PlanPage();
  final StartUpNamerPage _startUpNamerPage = new StartUpNamerPage();


  Widget pageChooser() {
    switch (this.pageIndex) {
      case 0:
        return _profilePage;
        break;

      case 1:
        return _planPage;
        break;

      case 2:
        return _startUpNamerPage;
        break;

      default:
        return new Container(
          child: new Center(
              child: new Text(
                  'No page found by page chooser.',
                  style: new TextStyle(fontSize: 30.0)
              )
          ),
        );
    }
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: new Scaffold(
            body: pageChooser(),
            bottomNavigationBar: new BottomNavigationBar(
              currentIndex: pageIndex,
              onTap: (int tappedIndex) { //Toggle pageChooser and rebuild state with the index that was tapped in bottom navbar
                setState(
                        (){ this.pageIndex = tappedIndex; }
                );
              },
              items: <BottomNavigationBarItem>[
                new BottomNavigationBarItem(
                    title: new Text('Profile'),
                    icon: new Icon(Icons.account_box)
                ),
                new BottomNavigationBarItem(
                    title: new Text('Plan'),
                    icon: new Icon(Icons.calendar_today)
                ),
                new BottomNavigationBarItem(
                    title: new Text('Startup'),
                    icon: new Icon(Icons.alarm_on)
                )
              ],
            )
        )
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用像PageView或一样的小部件Stack来实现相同的目的。

希望有帮助!

  • 谢谢您的回答。我通过将Stack与Offstage和Tickermode结合使用来解决了该问题。:-)) (2认同)
  • @ArtBelch您可以分享您的解决方案吗? (2认同)

Ani*_*wal 5

我发现最方便的方法是使用 PageStorage 小部件和 PageStorageBucket,后者充当键值持久层。

阅读这篇文章以获得精彩的解释 -> https://steemit.com/utopian-io/@tensor/persisting-user-interface-state-and-building-bottom-navigation-bars-in-dart-s-flutter -框架