我想在我的应用程序中有一个底部导航栏,其行为如下:
我能够实现 1 和 2,但我坚持第 3 点。这是我的构造:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bottom NavBar Demo',
home: BottomNavigationBarController(),
);
}
}
class BottomNavigationBarController extends StatefulWidget {
BottomNavigationBarController({Key key}) : super(key: key);
@override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
class _BottomNavigationBarControllerState
extends State<BottomNavigationBarController> {
int _selectedIndex = 0;
List<int> _history = [0];
GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
final List<BottomNavigationBarRootItem> bottomNavigationBarRootItems = [
BottomNavigationBarRootItem(
routeName: '/',
nestedNavigator: HomeNavigator(
navigatorKey: GlobalKey<NavigatorState>(),
),
bottomNavigationBarItem: BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
),
BottomNavigationBarRootItem(
routeName: '/settings',
nestedNavigator: SettingsNavigator(
navigatorKey: GlobalKey<NavigatorState>(),
),
bottomNavigationBarItem: BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Settings'),
),
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: WillPopScope(
onWillPop: () async {
final nestedNavigatorState =
bottomNavigationBarRootItems[_selectedIndex]
.nestedNavigator
.navigatorKey
.currentState;
if (nestedNavigatorState.canPop()) {
nestedNavigatorState.pop();
return false;
} else if (_navigatorKey.currentState.canPop()) {
_navigatorKey.currentState.pop();
return false;
}
return true;
},
child: Navigator(
key: _navigatorKey,
initialRoute: bottomNavigationBarRootItems.first.routeName,
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
builder = (BuildContext context) {
return bottomNavigationBarRootItems
.where((element) => element.routeName == settings.name)
.first
.nestedNavigator;
};
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
),
bottomNavigationBar: BottomNavigationBar(
items: bottomNavigationBarRootItems
.map((e) => e.bottomNavigationBarItem)
.toList(),
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
void _onItemTapped(int index) {
if (index == _selectedIndex) return;
setState(() {
_selectedIndex = index;
_history.add(index);
_navigatorKey.currentState
.pushNamed(bottomNavigationBarRootItems[_selectedIndex].routeName)
.then((_) {
_history.removeLast();
setState(() => _selectedIndex = _history.last);
});
});
}
}
class BottomNavigationBarRootItem {
final String routeName;
final NestedNavigator nestedNavigator;
final BottomNavigationBarItem bottomNavigationBarItem;
BottomNavigationBarRootItem({
@required this.routeName,
@required this.nestedNavigator,
@required this.bottomNavigationBarItem,
});
}
abstract class NestedNavigator extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey;
NestedNavigator({Key key, @required this.navigatorKey}) : super(key: key);
}
class HomeNavigator extends NestedNavigator {
HomeNavigator({Key key, @required GlobalKey<NavigatorState> navigatorKey})
: super(
key: key,
navigatorKey: navigatorKey,
);
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case '/':
builder = (BuildContext context) => HomePage();
break;
case '/home/1':
builder = (BuildContext context) => HomeSubPage();
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
);
}
}
class SettingsNavigator extends NestedNavigator {
SettingsNavigator({Key key, @required GlobalKey<NavigatorState> navigatorKey})
: super(
key: key,
navigatorKey: GlobalKey<NavigatorState>(),
);
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case '/':
builder = (BuildContext context) => SettingsPage();
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: RaisedButton(
onPressed: () => Navigator.of(context).pushNamed('/home/1'),
child: Text('Open Sub-Page'),
),
),
);
}
}
class HomeSubPage extends StatefulWidget {
const HomeSubPage({Key key}) : super(key: key);
@override
_HomeSubPageState createState() => _HomeSubPageState();
}
class _HomeSubPageState extends State<HomeSubPage> {
String _text;
@override
void initState() {
_text = 'Click me';
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Sub Page'),
),
body: Center(
child: RaisedButton(
onPressed: () => setState(() => _text = 'Clicked'),
child: Text(_text),
),
),
);
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings Page'),
),
body: Container(
child: Center(
child: Text('Settings Page'),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
运行此代码时,点击“打开子页面”-> 点击“单击我”,您应该会在“主页子页面”中看到“已点击”。如果您现在单击底部导航栏中的“设置”,然后使用 android 后退按钮,您将返回“主页”选项卡,显示与按钮显示“已点击”完全相同的页面。如果您单击底部导航栏中的“设置”,然后单击“主页”,您将再次位于按钮显示“单击”的完全相同的页面上。这正是我需要的行为,但是当您执行后者时,您还会收到一条错误消息,指出“在小部件树中检测到重复的 GlobalKey。”。如果您现在点击 android 后退按钮两次,您将进入一个空白页面(出于令人讨厌的原因)。
我希望我的解释有意义..
一个完美实现的示例应用程序是 Instagram。
abd*_*nem 10
你想让导航标签就像 twitter、Instagram、应用程序一样,所以每个标签都有自己的导航历史记录和勺子,
我想我明白你想要实现的目标,但是你以错误的方式来做,你应该使用“tabBarView”作为里面的标签内容ScoopWillPop' 并让每个选项卡管理自己的导航历史记录,在对我的一个项目进行了如此多的辛勤工作后,我找到了实现这一想法的最佳方法,
我对您的代码进行了许多更改,我希望清楚
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bottom NavBar Demo',
home: BottomNavigationBarController(),
);
}
}
class BottomNavigationBarController extends StatefulWidget {
BottomNavigationBarController({Key key}) : super(key: key);
@override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
class _BottomNavigationBarControllerState
extends State<BottomNavigationBarController> with SingleTickerProviderStateMixin{
int _selectedIndex = 0;
List<int> _history = [0];
GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
TabController _tabController;
List<Widget> mainTabs;
List<BuildContext> navStack = [null, null]; // one buildContext for each tab to store history of navigation
@override
void initState() {
_tabController = TabController(vsync: this, length: 2);
mainTabs = <Widget>[
Navigator(
onGenerateRoute: (RouteSettings settings){
return PageRouteBuilder(pageBuilder: (context, animiX, animiY) { // use page PageRouteBuilder instead of 'PageRouteBuilder' to avoid material route animation
navStack[0] = context;
return HomePage();
});
}),
Navigator(
onGenerateRoute: (RouteSettings settings){
return PageRouteBuilder(pageBuilder: (context, animiX, animiY) { // use page PageRouteBuilder instead of 'PageRouteBuilder' to avoid material route animation
navStack[1] = context;
return SettingsPage();
});
}),
];
super.initState();
}
final List<BottomNavigationBarRootItem> bottomNavigationBarRootItems = [
BottomNavigationBarRootItem(
bottomNavigationBarItem: BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
),
BottomNavigationBarRootItem(
bottomNavigationBarItem: BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
),
];
@override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
body: TabBarView(
controller: _tabController,
physics: NeverScrollableScrollPhysics(),
children: mainTabs,
),
bottomNavigationBar: BottomNavigationBar(
items: bottomNavigationBarRootItems.map((e) => e.bottomNavigationBarItem).toList(),
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
),
onWillPop: () async{
if (Navigator.of(navStack[_tabController.index]).canPop()) {
Navigator.of(navStack[_tabController.index]).pop();
setState((){ _selectedIndex = _tabController.index; });
return false;
}else{
if(_tabController.index == 0){
setState((){ _selectedIndex = _tabController.index; });
SystemChannels.platform.invokeMethod('SystemNavigator.pop'); // close the app
return true;
}else{
_tabController.index = 0; // back to first tap if current tab history stack is empty
setState((){ _selectedIndex = _tabController.index; });
return false;
}
}
},
);
}
void _onItemTapped(int index) {
_tabController.index = index;
setState(() => _selectedIndex = index);
}
}
class BottomNavigationBarRootItem {
final String routeName;
final NestedNavigator nestedNavigator;
final BottomNavigationBarItem bottomNavigationBarItem;
BottomNavigationBarRootItem({
@required this.routeName,
@required this.nestedNavigator,
@required this.bottomNavigationBarItem,
});
}
abstract class NestedNavigator extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey;
NestedNavigator({Key key, @required this.navigatorKey}) : super(key: key);
}
class HomeNavigator extends NestedNavigator {
HomeNavigator({Key key, @required GlobalKey<NavigatorState> navigatorKey})
: super(
key: key,
navigatorKey: navigatorKey,
);
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case '/':
builder = (BuildContext context) => HomePage();
break;
case '/home/1':
builder = (BuildContext context) => HomeSubPage();
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: RaisedButton(
onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => HomeSubPage())),
child: Text('Open Sub-Page'),
),
),
);
}
}
class HomeSubPage extends StatefulWidget {
const HomeSubPage({Key key}) : super(key: key);
@override
_HomeSubPageState createState() => _HomeSubPageState();
}
class _HomeSubPageState extends State<HomeSubPage> with AutomaticKeepAliveClientMixin{
@override
// implement wantKeepAlive
bool get wantKeepAlive => true;
String _text;
@override
void initState() {
_text = 'Click me';
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Sub Page'),
),
body: Center(
child: RaisedButton(
onPressed: () => setState(() => _text = 'Clicked'),
child: Text(_text),
),
),
);
}
}
/* convert it to statfull so i can use AutomaticKeepAliveClientMixin to avoid disposing tap */
class SettingsPage extends StatefulWidget {
@override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClientMixin{
@override
// implement wantKeepAlive
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings Page'),
),
body: Container(
child: Center(
child: Text('Settings Page'),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3913 次 |
| 最近记录: |