#flutter #flutter-navigation
#flutter #flutter-навигация
Вопрос:
Есть ли у кого-нибудь какие-либо рекомендации по определению вложенной навигации в Flutter?
Я хочу сохранить постоянную нижнюю панель навигации даже при перенаправлении на новые экраны. Похоже на YouTube, где нижняя панель всегда присутствует, даже когда вы просматриваете меню глубже.
Я не могу понять это из документов.
Единственный учебник, который я смог найти до сих пор, который подробно описывает именно мое требование: https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf ( исходный код). Однако это очень запутанно.
Прямо сейчас я использую
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return Container()
Тем не менее, он просто перемещает новый виджет по всему стеку, охватывая нижнюю панель навигации.
Любые советы будут с благодарностью!
Ответ №1:
Вот простой пример, который даже поддерживает переход на первый экран с панелью вкладок.
import 'package:flutter/material.dart';
import '../library/screen.dart';
import '../playlists/screen.dart';
import '../search/screen.dart';
import '../settings/screen.dart';
class TabsScreen extends StatefulWidget {
@override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
int _currentIndex = 0;
final _libraryScreen = GlobalKey<NavigatorState>();
final _playlistScreen = GlobalKey<NavigatorState>();
final _searchScreen = GlobalKey<NavigatorState>();
final _settingsScreen = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
Navigator(
key: _libraryScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => LibraryScreen(),
),
),
Navigator(
key: _playlistScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => PlaylistsScreen(),
),
),
Navigator(
key: _searchScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => SearchScreen(),
),
),
Navigator(
key: _settingsScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => SettingsScreen(),
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (val) => _onTap(val, context),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.library_books),
title: Text('Library'),
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text('Playlists'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
);
}
void _onTap(int val, BuildContext context) {
if (_currentIndex == val) {
switch (val) {
case 0:
_libraryScreen.currentState.popUntil((route) => route.isFirst);
break;
case 1:
_playlistScreen.currentState.popUntil((route) => route.isFirst);
break;
case 2:
_searchScreen.currentState.popUntil((route) => route.isFirst);
break;
case 3:
_settingsScreen.currentState.popUntil((route) => route.isFirst);
break;
default:
}
} else {
if (mounted) {
setState(() {
_currentIndex = val;
});
}
}
}
}
Комментарии:
1. Это сработало очень хорошо для меня — я обобщил этот подход и добавил несколько помощников, таких как
tabNavigatorOf(context, { int tabIndex })
2. У меня возникла проблема с сохранением состояния вкладки, но индексированный стек работал отлично, спасибо
3. После этого, как мы можем нажать страницу без нижней панели с любой внутренней страницы?
4. У меня возникают проблемы с этим подходом — когда я переключаюсь с одной вкладки на другую, а затем нажимаю кнопку «Назад», приложение закрывается вместо того, чтобы переключаться обратно на первую вкладку. Кроме того, если я выполняю вложенную навигацию, например, переключаюсь на другую страницу с навигатором внутри, например, на страницу библиотеки, нажатие назад также закрывает приложение. Какие-нибудь советы?
5. если кнопка переключается обратно на первую страницу, попробуйте использовать WillPopScope на странице навигаторов со значением ключа.. @schneida
Ответ №2:
Вот пример кода для постоянной нижней панели навигации в качестве начального:
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatelessWidget {
final navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Navigator(
key: navigatorKey,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => PageOne(),
),
),
),
BottomNavigationBar(navigatorKey)
],
),
);
}
}
class BottomNavigationBar extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey;
BottomNavigationBar(this.navigatorKey) : assert(navigatorKey != null);
Future<void> push(Route route) {
return navigatorKey.currentState.push(route);
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: ButtonBar(
alignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text("PageOne"),
onPressed: () {
push(MaterialPageRoute(builder: (context) => PageOne()));
},
),
RaisedButton(
child: Text("PageTwo"),
onPressed: () {
push(MaterialPageRoute(builder: (context) => PageTwo()));
},
)
],
),
);
}
}
class PageOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page One"),
RaisedButton(
onPressed: (){
Navigator.of(context).pop();
},
child: Text("Pop"),
),
],
),
);
}
}
class PageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page Two"),
RaisedButton(
onPressed: (){
Navigator.of(context).pop();
},
child: Text("Pop"),
),
],
),
);
}
}
Вот как это выглядит на экране
Комментарии:
1.Я попробовал что-то, основанное на вашем решении. Я создал
Expanded
Column
вScaffold
теле. Я помещаю aBottomNavigationBar
внутрь и использую aGlobalKey<NavigatorState>()
для продвижения новых страниц. Однако анимация междуBottomNavigationBar
страницами крайне неудобна. Он не используетBottomNavigationBar
анимацию. Это просто накладывает одну страницу поверх другой (и портитgo back
)2. Я не понимаю, какую анимацию вы хотите? Скользящий переход влево-вправо?
3. @HarshBhikadia Я попробовал ваше решение и работает хорошо, но, когда на определенной странице, например, на странице 1, а на странице 1 есть виджет TextField (), при нажатии на этот виджет текстового поля автоматически открывается маршрут по умолчанию, который равен «/». По сути, он повторно открывает страницу, на которой возвращается этот маршрут по умолчанию. Есть способ решить эту проблему?
4. Кажется, что вложенный навигатор теряет свое состояние при горячей перезагрузке, и я начинаю с начального маршрута. У кого-нибудь еще такое поведение? Это довольно неприятно при разработке…
5. @HarshBhikadia После этого, если я хочу не показывать нижнюю панель на внутренней странице (например, на странице сведений), как мне этого добиться?