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

时间:2018-03-22 21:51:44

标签: 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)
              )
            ],
          )
      )
    );
  }
}

7 个答案:

答案 0 :(得分:49)

要使状态保持在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,
          ),
        );
      }

答案 1 :(得分:7)

晚会,但我有一个简单的解决方案。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);
        });
      },
  ...
}

单个标签:

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

我已经制作了有关here的视频。

答案 2 :(得分:4)

使用AutomaticKeepAliveClientMixin强制您的标签页内容不被处理。

StackLayout

要确保您的选项卡在不需要保留时会被释放,请使[assembly: ExportRenderer(typeof(StackLayout), typeof(ICustomStackLayoutRenderer))] namespace CustomStackLayoutRenderer.UWP { public class ICustomStackLayoutRenderer : LayoutRenderer { protected override void OnElementChanged(ElementChangedEventArgs<Layout> e) { base.OnElementChanged(e); } protected override void UpdateBackgroundColor() { base.UpdateBackgroundColor(); if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.AcrylicBrush")) { var brush = Windows.UI.Xaml.Application.Current.Resources["SystemControlAltHighAcrylicWindowBrush"] as AcrylicBrush; var tint = brush.TintColor; var opacity = brush.TintOpacity; var fallbackColor = brush.FallbackColor; var source = brush.BackgroundSource; this.Background = brush; } } } } 返回一个类变量。您必须致电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; } 来更新保持活动状态。

动态保存示例:

wantKeepAlive

答案 3 :(得分:3)

使用“ IndexedStack窗口小部件”和“ 底部导航栏窗口小部件”以保持屏幕/页面/窗口小部件的状态

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

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

Scaffold(
  body: IndexedStack(
    index: _selectedPage,
    children: _children,
  ),
  bottomNavigationBar: BottomNavigationBar(
    ........
    ........
  ), 
);

答案 4 :(得分:2)

每次运行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)
                )
              ],
            )
        )
    );
  }
}

或者您可以使用PageViewStack等小工具来实现相同目标。

希望有所帮助!

答案 5 :(得分:0)

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

仔细阅读这篇文章,以得到优美的解释-> https://steemit.com/utopian-io/@tensor/persisting-user-interface-state-and-building-bottom-navigation-bars-in-dart-s-flutter-framework

答案 6 :(得分:0)

不要使用IndexStack Widget,因为它会将所有选项卡一起实例化,并且假设如果所有选项卡都在发出网络请求,那么回调将被搞乱,最后一个API调用选项卡可能会控制回调。

将 AutomaticKeepAliveClientMixin 用于您的有状态小部件,这是实现它的最简单方法,无需将所有选项卡一起实例化。

我的代码具有向调用选项卡提供相应响应的接口,我通过以下方式实现了它。

创建您的有状态小部件

class FollowUpsScreen extends StatefulWidget {
  FollowUpsScreen();
        
  @override
  State<StatefulWidget> createState() {
    return FollowUpsScreenState();
  }
}
        
class FollowUpsScreenState extends State<FollowUpsScreen>
     with AutomaticKeepAliveClientMixin<FollowUpsScreen>
            implements OperationalControls {
    
  @override
  Widget build(BuildContext context) {
  //do not miss this line
    super.build(context);
    return .....;
  }

  @override
  bool get wantKeepAlive => true;
}