当前位置: 首页 > 知识库问答 >
问题:

关闭底部导航栏上的抽屉单击,颤动

刘建中
2023-03-14

每当用户按下< code >底部导航栏中的一个按钮时,我都想关闭我的< code >抽屉小部件,但是我不太明白这一点。我现在设置BNB的方式是通过应用程序记住所有屏幕的当前状态(使用< code>IndexedStack),但我想在按下BNB按钮之前关闭任何屏幕中打开的抽屉。我的每个屏幕都有自己的抽屉和应用程序栏,所以我不能在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个屏幕都是一样的,它们有自己的应用程序栏和抽屉:

@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上,打开抽屉,然后在BNB中按屏幕2,我想在导航到屏幕2之前< code > pop()/关闭屏幕1中的抽屉。)

提前感谢您的帮助!

共有1个答案

王才
2023-03-14

这样做的一个好方法是使用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;
    }
  }

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

以下是您的代码可能如下所示:

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

 类似资料:
  • 我尝试在我的应用程序中实现导航抽屉(材料设计)。我的活动包含带有片段的FrameLayout。当用户在导航抽屉FrameLayout中选择项目时,重新加载新片段: 当我点击项目时,一切正常。导航抽屉关闭不顺利,但冻结(抽搐,抽搐),因为片段在后台重新加载。 如何顺利关闭导航抽屉?

  • 我是android的初学者。我想创建一个导航菜单抽屉。当我点击图标时,它会打开。当我点击菜单项时,它会显示片段。但是当我点击菜单项时,我的导航抽屉不会自动关闭。每次我关上导航抽屉看我的片段。我不知道在哪里更改代码?随信附上我的主要活动。java,activity_main。xml。任何人都可以帮助我。 activity_main.xml 主要活动。Java语言

  • 我正试图把导航抽屉放在动作栏上,当它像这个应用程序一样向右滑动时:[已删除] 这是我的主要活动布局: 关于stackoverflow的其他一些问题与此类似,但建议所有答案都使用滑动菜单库。但这个应用程序他们仍然使用Android。支持v4.widget。抽屉式布局,他们成功了。不要问我怎么知道他们使用标准的导航抽屉,但我肯定。 非常感谢你的帮助。 这是最终的解决方案:多亏了@Peter Cai,这

  • 出于某种原因,在抽屉外轻触时,导航抽屉不会关闭。它是MainActivity中带有ListView的另一个片段: 当点击右边的片段时,它不会关闭抽屉,相反,它的行为就像片段占据了整个屏幕,而点击侦听器在片段中仍然处于活动状态。 activity_main.xml: MainActivity.java: 不确定我做错了什么,因为在抽屉区域外敲击时关闭抽屉应该是抽屉布局中的默认行为。

  • 我在我的应用程序中使用抽屉菜单,并实现了在NavigationDrawer片段类中切换片段的逻辑。我最近读到片段切换只能从托管活动中发生。 有一个从NavigationDrawerFragment调用的接口,用于通知导航抽屉列表视图中所选位置的MainActivity。 我感到困惑的是,在Mainactive中有一个静态片段,它是使用NavigationDrawer片段中接口提供的位置调用的。 P

  • 在我的片段中,我通过编程将片段添加到导航抽屉中,然后从右侧打开抽屉。注意,我无法直接访问抽屉布局的xml文件,因此需要一个编程解决方案。抽屉从右侧打开并正确关闭,我保留抽屉的打开/关闭状态,并在配置根据该状态更改后打开它。 问题是,在配置更改时,抽屉关闭(我可以看到抽屉动画到关闭状态),即使我根据保存的InstanceState布尔值恢复其打开/关闭状态。我在中的所有方法中放置了调试器,但它们没有