我目前正致力于构建一个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)
)
],
)
)
);
}
}
答案 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)
)
],
)
)
);
}
}
或者您可以使用PageView
或Stack
等小工具来实现相同目标。
希望有所帮助!
答案 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;
}