0

Drawer每次用户按下 中的按钮时,我都想关闭我的小部件Bottom Navigation Bar,但无法完全弄清楚这件事。我现在设置 BNB 的方式是通过应用程序记住所有屏幕的当前状态(使用IndexedStack),但是如果在按下 BNB 按钮之前在任何屏幕中打开抽屉,我想关闭抽屉. 我的每个屏幕都有自己的抽屉和 AppBars,所以我不能在 BNB 内制作一个抽屉(或者我可以并且我可以在点击特定屏幕时使用开关盒动态更改它们,但是抽屉将覆盖底部导航栏等),但我现在想让它像这样工作。所以这是代码,里面有一些注释来解释事情:

底部导航栏:

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'),
    );
  }

所以因为每个屏幕都有自己的 AppBars 和 Drawer,所以 Drawer 不会渲染在底部导航栏上,所以我的 BNB 按钮可以点击。如果我在 BNB 内为所有屏幕放置一个抽屉,那么除非你先关闭抽屉,否则你无法单击 BNB,这不是我现在要寻找的东西。

所以,我的最后一个问题是,当您按下底部导航栏时,如何关闭每个屏幕抽屉(如果它们以前打开过)?(即我在屏幕 1 上,打开抽屉,然后按 BNB 中的屏幕 2,我想pop()/关闭屏幕 1 中的抽屉,然后导航到屏幕 2。)

在此先感谢您的帮助!

4

1 回答 1

2

一个很好的方法是使用 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 中,您可以拥有所有的脚手架键,并且在 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;
    }
  }

由您决定传递密钥的最佳方式。例如,您可以在包含底部导航栏和不同支架的小部件中定义它们,并将其作为参数传递。您可以使用状态管理......任何适合您的用例。

这是您的代码的样子:

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 列表更改为后期变量,因此您可以在声明它们时将脚手架键传递给它们。

于 2022-01-23T10:13:24.167 回答