Закрытие ящика при щелчке нижней панели навигации, флаттер
я хочу закрыть свой
Нижняя панель навигации:
class BottomNavBar extends StatefulWidget {
static const String id = 'bottom_navbar_screen';
@override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _selectedIndex = 0;
/// list of screen that will render inside the BNB
List<Navigation> _items = [
Navigation(
widget: Screen1(), navigationKey: GlobalKey<NavigatorState>()),
Navigation(
widget: Screen2(), navigationKey: GlobalKey<NavigatorState>()),
Navigation(
widget: Screen3(), navigationKey: GlobalKey<NavigatorState>()),
Navigation(
widget: Screen4(), navigationKey: GlobalKey<NavigatorState>()),
];
/// function that renders components based on selected one in the BNB
void _onItemTapped(int index) {
if (index == _selectedIndex) {
_items[index]
.navigationKey
.currentState
.popUntil((route) => route.isFirst);
} else {
setState(() {
_selectedIndex = index;
});
}
/// when the index is selected, on the button press do some actions
switch (_selectedIndex) {
case 0:
// Do some actions
break;
case 1:
// Do some actions
break;
case 2:
// Do some actions
break;
case 3:
// Do some actions
break;
}
}
/// navigation Tab widget for a list of all the screens and puts them in a Indexed Stack
Widget _navigationTab(
{GlobalKey<NavigatorState> navigationKey, Widget widget}) {
return Navigator(
key: navigationKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(builder: (context) => widget);
},
);
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab =
!await _items[_selectedIndex].navigationKey.currentState.maybePop();
if (isFirstRouteInCurrentTab) {
if (_selectedIndex != 0) {
_onItemTapped(1);
return false;
}
}
/// let system handle back button if we're on the first route
return isFirstRouteInCurrentTab;
},
child: Scaffold(
body: IndexedStack(
index: _selectedIndex,
children: _items
.map((e) => _navigationTab(
navigationKey: e.navigationKey, widget: e.widget))
.toList(),
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Screen 1,
),
BottomNavigationBarItem(
label: 'Screen 2,
),
BottomNavigationBarItem(
label: 'Screen 3,
),
BottomNavigationBarItem(
label: 'Screen 4,
),
],
currentIndex: _selectedIndex,
showUnselectedLabels: true,
onTap: _onItemTapped,
),
),
);
}
}
Допустим, все 4 экрана одинаковы и у них есть свои AppBar и Drawer:
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
drawer: Drawer(), // so this is what I want to close on BNB button press in each of the 4 screens
appBar: AppBar() // each screen has its own app bar
title: Text('Screens 1-4),
),
body: Text('Body of Screens 1-4)
),
);
}
Так как каждый из экранов имеет свои собственные панели приложений и ящики, ящик не отображается поверх нижней панели навигации, поэтому мои кнопки BNB можно нажимать. Если я поставлю один ящик для всех экранов внутри BNB, то вы не сможете щелкнуть BNB, если сначала не закроете ящик, чего я сейчас не ищу.
Итак, мой последний вопрос: как мне закрыть каждый из экранных ящиков (если они ранее были открыты, то есть) при нажатии на нижнюю панель навигации? (т.е. я на экране 1, я открываю ящик, затем нажимаю на экран 2 в BNB и хочу
Заранее спасибо за помощь!
1 ответ
Хороший способ сделать это — использовать GlobalKey для вашего скаффолда. Итак, для всех ваших лесов вы определяете их, используя:
class SomeClass extends StatelessWidget {
final scaffoldKey = GlobalKey<ScaffoldState>()
Widget build(BuildContext context) {
Scaffold(
backgroundColor: Colors.white,
drawer: Drawer(), // so this is what I want to close on BNB button press in each of the 4 screens
appBar: AppBar( // each screen has its own app bar
title: Text('Screens 1-4),
),
body: Text('Body of Screens 1-4),
key: scaffoldKey,
),
);
}
}
И затем вы можете передать этот ключ в свой BottomNavigationBar. В вашем BottomNavigationBar вы можете иметь все scaffoldKeys и в функции onItemTap:
void _onItemTapped(int index) {
for (scaffoldKey in scaffoldKeys) {
// If the drawer is open
if (scaffoldKey.currentState.isDrawerOpen) {
// Closes the drawer
scaffoldKey.currentState?.openEndDrawer();
}
}
if (index == _selectedIndex) {
_items[index]
.navigationKey
.currentState
.popUntil((route) => route.isFirst);
} else {
setState(() {
_selectedIndex = index;
});
}
/// when the index is selected, on the button press do some actions
switch (_selectedIndex) {
case 0:
// Do some actions
break;
case 1:
// Do some actions
break;
case 2:
// Do some actions
break;
case 3:
// Do some actions
break;
}
}
Вам решать, как найти лучший способ передачи ключей. Например, вы можете определить их в виджетах, содержащих как нижнюю панель навигации, так и различные каркасы, и передать их в качестве параметров. Вы можете использовать State Management... все, что подходит для вашего варианта использования.
Вот как может выглядеть ваш код:
class BottomNavBar extends StatefulWidget {
static const String id = 'bottom_navbar_screen';
@override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _selectedIndex = 0;
late final List<GlobalKey<ScaffoldState>> scaffoldKeys;
/// list of screen that will render inside the BNB
late final List<Navigation> _items;
@override
initState() {
super.initState()
scaffoldKeys = [GlobalKey<ScaffoldState>(), GlobalKey<ScaffoldState>(), GlobalKey<ScaffoldState>(), GlobalKey<ScaffoldState>()];
_items = [
Navigation(
widget: Screen1(scaffoldKey: scaffoldKeys[0]), navigationKey: GlobalKey<NavigatorState>()),
Navigation(
widget: Screen2(scaffoldKey: scaffoldKeys[1]), navigationKey: GlobalKey<NavigatorState>()),
Navigation(
widget: Screen3(scaffoldKey: scaffoldKeys[2]), navigationKey: GlobalKey<NavigatorState>()),
Navigation(
widget: Screen4(scaffoldKey: scaffoldKeys[3]), navigationKey: GlobalKey<NavigatorState>()),
];
}
/// function that renders components based on selected one in the BNB
void _onItemTapped(int index) {
for (scaffoldKey in scaffoldKeys) {
// If the drawer is open
if (scaffoldKey.currentState.isDrawerOpen) {
// Closes the drawer
scaffoldKey.currentState?.openEndDrawer();
}
}
if (index == _selectedIndex) {
_items[index]
.navigationKey
.currentState
.popUntil((route) => route.isFirst);
} else {
setState(() {
_selectedIndex = index;
});
}
/// when the index is selected, on the button press do some actions
switch (_selectedIndex) {
case 0:
// Do some actions
break;
case 1:
// Do some actions
break;
case 2:
// Do some actions
break;
case 3:
// Do some actions
break;
}
}
/// navigation Tab widget for a list of all the screens and puts them in a Indexed Stack
Widget _navigationTab(
{GlobalKey<NavigatorState> navigationKey, Widget widget, GlobalKey<ScaffoldState> scaffoldKey}) {
return Navigator(
key: navigationKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(builder: (context) => widget);
},
);
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab =
!await _items[_selectedIndex].navigationKey.currentState.maybePop();
if (isFirstRouteInCurrentTab) {
if (_selectedIndex != 0) {
_onItemTapped(1);
return false;
}
}
/// let system handle back button if we're on the first route
return isFirstRouteInCurrentTab;
},
child: Scaffold(
body: IndexedStack(
index: _selectedIndex,
children: _items
.map((e) => _navigationTab(
navigationKey: e.navigationKey, widget: e.widget))
.toList(),
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Screen 1,
),
BottomNavigationBarItem(
label: 'Screen 2,
),
BottomNavigationBarItem(
label: 'Screen 3,
),
BottomNavigationBarItem(
label: 'Screen 4,
),
],
currentIndex: _selectedIndex,
showUnselectedLabels: true,
onTap: _onItemTapped,
),
),
);
}
}
А вы экранируете:
class Screen1 extends StatelessWidget {
final GlobalKey<ScaffoldState> scaffoldKey;
Screen1({required this.scaffoldKey});
@override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
backgroundColor: Colors.white,
drawer: Drawer(), // so this is what I want to close on BNB button press in each of the 4 screens
appBar: AppBar( // each screen has its own app bar
title: Text('Screens 1-4'),
),
body: Text('Body of Screens 1-4'),
);
}
}
Я изменил список экранов _items на поздние переменные, чтобы вы могли передавать им scaffoldKeys при их объявлении.