【问题标题】:Flutter persistent navigation bar with named routes?带有命名路线的颤振持久导航栏?
【发布时间】:2018-09-15 19:27:04
【问题描述】:

我一直在为 Flutter 寻找一个好的导航/路由器示例,但我没有找到。

我想要实现的很简单:

  1. 持久的底部导航栏,突出显示当前顶级路线
  2. 命名路线,以便我可以从应用程序内的任何位置导航到任何路线
  3. Navigator.pop 应始终将我带到我之前所在的视图

BottomNavigationBar 官方 Flutter demo 实现了 1 但后退按钮和路由不起作用。 PageView 和 TabView 也有同样的问题。还有许多其他教程通过实现 MaterialApp 路由来实现 2 和 3,但它们似乎都没有持久导航栏。

是否有可以满足所有这些要求的导航系统示例?

【问题讨论】:

  • 您的问题找到解决方案了吗?
  • @Daniel.V 不,我没有。我最终使用 BottomNavigationBar 来实现 1,放弃了要求。 2 并手动处理弹出,以便我始终可以导航回当前选项卡的根视图。从那时起,开源场景不断发展,因此可能会有新的库可以实现至少 1 和 2。

标签: dart flutter


【解决方案1】:

您的所有 3 个要求都可以通过使用自定义 Navigator 来实现。

Flutter 团队对此做了video,他们关注的文章在这里:https://medium.com/flutter/getting-to-the-bottom-of-navigation-in-flutter-b3e440b9386

基本上,您需要将 Scaffold 的主体包裹在自定义 Navigator 中:

class _MainScreenState extends State<MainScreen> {
  final _navigatorKey = GlobalKey<NavigatorState>();

  // ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Navigator(
        key: _navigatorKey,
        initialRoute: '/',
        onGenerateRoute: (RouteSettings settings) {
          WidgetBuilder builder;
          // Manage your route names here
          switch (settings.name) {
            case '/':
              builder = (BuildContext context) => HomePage();
              break;
            case '/page1':
              builder = (BuildContext context) => Page1();
              break;
            case '/page2':
              builder = (BuildContext context) => Page2();
              break;
            default:
              throw Exception('Invalid route: ${settings.name}');
          }
          // You can also return a PageRouteBuilder and
          // define custom transitions between pages
          return MaterialPageRoute(
            builder: builder,
            settings: settings,
          );
        },
      ),
      bottomNavigationBar: _yourBottomNavigationBar,
    );
  }
}

在底部导航栏中,要导航到新自定义 Navigator 中的新屏幕,您只需调用以下代码:

_navigatorKey.currentState.pushNamed('/yourRouteName');

要实现第三个要求,即Navigator.pop 将您带到上一个视图,您需要用WillPopScope 包装自定义Navigator

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: WillPopScope(
      onWillPop: () async {
        if (_navigatorKey.currentState.canPop()) {
          _navigatorKey.currentState.pop();
          return false;
        }
        return true;
      },
      child: Navigator(
        // ...
      ),
    ),
    bottomNavigationBar: _yourBottomNavigationBar,
  );
}

应该就是这样!无需手动处理弹出或管理自定义历史列表。

【讨论】:

  • 如何让每个标签保持活力。在上述方法中,当移动到下一个选项卡/底部导航项时,每个页面都会重新创建。
  • @AbdullahKhan 你不应该这样做。只需将状态与 UI 分开即可。一些有用的库选项:Redux、BLoC、MobX。
  • @czx 如何在子路由中隐藏底部导航栏?
  • 有没有一种方法可以让您在最顶层使用它?我在 web 上使用颤振,遗憾的是,这种导航在 url 选项卡中不起作用。
  • @CZX 我不太明白。这个解决方案意味着 Homepage()、Page1() 和 Page2() 不能自己返回 Scaffolds,因为不希望有嵌套的 Scaffolds。但是如果我的 Widgets 返回 Scaffolds 怎么办,例如因为他们有浮动操作按钮?
【解决方案2】:

CupertinoTabBar 的行为与您描述的完全相同,但采用 iOS 风格。不过也可以是used in MaterialApps

Sample Code

【讨论】:

    【解决方案3】:

    您要求的内容会违反material design specification

    在 Android 上,后退按钮不会在底部之间导航 导航栏视图。

    一个抽屉式导航会给你 2 和 3,但不是 1。这取决于对你来说更重要的是什么。

    您可以尝试使用 LocalHistoryRoute。这样就达到了你想要的效果:

    class MainPage extends StatefulWidget {
      @override
      State createState() {
        return new MainPageState();
      }
    }
    
    class MainPageState extends State<MainPage> {
      int _currentIndex = 0;
      List<int> _history = [0];
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text('Bottom Nav Back'),
          ),
          body: new Center(
            child: new Text('Page $_currentIndex'),
          ),
          bottomNavigationBar: new BottomNavigationBar(
            currentIndex: _currentIndex,
            items: <BottomNavigationBarItem>[
              new BottomNavigationBarItem(
                icon: new Icon(Icons.touch_app),
                title: new Text('keypad'),
              ),
              new BottomNavigationBarItem(
                icon: new Icon(Icons.assessment),
                title: new Text('chart'),
              ),
              new BottomNavigationBarItem(
                icon: new Icon(Icons.cloud),
                title: new Text('weather'),
              ),
            ],
            onTap: (int index) {
              _history.add(index);
              setState(() => _currentIndex = index);
              Navigator.push(context, new BottomNavigationRoute()).then((x) {
                _history.removeLast();
                setState(() => _currentIndex = _history.last);
              });
            },
          ),
        );
      }
    }
    
    class BottomNavigationRoute extends LocalHistoryRoute<void> {}
    

    【讨论】:

    • 这是我想要实现的,即使它违反了一些任意的设计规则。甚至官方 Youtube 应用程序在按下后退按钮时也会在底部导航栏视图之间导航。 Spotify 和几乎所有大型应用程序也是如此。
    • 每次点击都会推送一个导航堆栈,但从未移除。
    • 根据问题的要求,它们被 Android 后退按钮删除。
    • 我通过扩展 Route 而不是 LocalHistoryRoute 让它工作
    • 在 Android 上,点击包含 BottomNavigationBar 的活动中的后退按钮应该会弹出到第一个索引(第一页)并再次点击后退按钮应该完成活动。
    猜你喜欢
    • 2022-10-18
    • 1970-01-01
    • 1970-01-01
    • 2019-12-16
    • 2021-04-25
    • 1970-01-01
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多