Flutter :使用导航器推送至新屏幕时保持底部导航栏

byqmnocz  于 2023-01-14  发布在  Flutter
关注(0)|答案(8)|浏览(169)

在iOS中,我们有一个UITabBarController,当我们推到一个新的ViewController时,它会永久停留在屏幕底部。
在Flutter中,我们有一个脚手架的bottomNavigationBar,但是与iOS不同的是,当我们Navigator.push到一个新的屏幕时,这个bottomNavigationBar会消失。
在我的应用程序中,我希望满足以下要求:主屏幕有一个bottomNavigationBar,其中有2个项目(a & B)显示屏幕 A & B。默认情况下,显示屏幕 A。屏幕 A 内有一个按钮。点击该按钮,Navigator.push到屏幕 C。现在在屏幕 C 中,我们仍然可以看到bottomNavigationBar。点击项目 b,我转到屏幕 B。现在在屏幕 B 中,点击bottomNavigationBar中的项目 a,我返回屏幕 C(不是 AA 当前在导航层次结构中位于 C 下方)。
我该怎么做?谢谢,伙计们。
编辑:我在这里加入了一些图片作为演示:
屏幕A Screen A
点击 * 转到C* 按钮,按下屏幕C Screen C
点击底部导航栏内的“右”项,转到屏幕B Screen B

67up9zun

67up9zun1#

tl;dr:将CupertinoTabBarCupertinoTabScaffold一起使用
问题不在于Flutter,而在于UX,就像Rémi Rousselet提到的那样。
结果是Material Design不建议使用层次结构中的子页面访问底部导航栏。
然而,iOS人机界面指南推荐了这个功能。所以,为了使用这个功能,我不得不修改库比蒂诺小部件而不是Material小部件。具体来说,在main中,返回一个WidgetsApp/MaterialApp,其中包含一个CupertinoTabScaffold。用CupertinoTabBar实现标签栏,每个屏幕都是一个CupertinoTabView

clj7thdc

clj7thdc2#

选项1:如果您只想保留BottomNavigationBar,那么尝试使用this
选项2:对静态BottomNavigationBar使用CupertinoTabBar,如下所示。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mqttdemo/Screen2.dart';
import 'package:mqttdemo/Screen3.dart';

import 'Screen1.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  int _currentIndex;
  List<Widget> _children;

  @override
  void initState() {
    _currentIndex = 0;
    _children = [
      Screen1(),
      Screen2(),
      Screen3(),
    ];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      tabBar: CupertinoTabBar(
        currentIndex: _currentIndex,
        onTap: onTabTapped,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text("Screen 1"),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text("Screen 2"),
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.home), title: Text("Screen 3")),
        ],

      ),
        tabBuilder: (BuildContext context, int index) {
          return CupertinoTabView(
            builder: (BuildContext context) {
              return SafeArea(
                top: false,
                bottom: false,
                child: CupertinoApp(
                  home: CupertinoPageScaffold(
                    resizeToAvoidBottomInset: false,
                    child: _children[_currentIndex],
                  ),
                ),
              );
            },
          );
        }
    );
  }

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }
}

从屏幕3导航到屏幕4,如下所示:

class Screen3 extends StatefulWidget {
      @override
      _Screen3State createState() => _Screen3State();
    }
    
    class _Screen3State extends State<Screen3> {
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.black,
          child: Center(
            child: RaisedButton(
              child: Text("Click me"),
              onPressed: () {
                Navigator.of(context, rootNavigator: false).push(MaterialPageRoute(
                    builder: (context) => Screen4(), maintainState: false));
              },
            ),
          ),
        );
      }

}
inb24sb2

inb24sb23#

如果有人想保持bottomNavigationBar,只需从主页主体返回一个导航器,并为每条路线传递一个唯一的键。

import 'package:bottomnavigation_sample/page1.dart';
import 'package:bottomnavigation_sample/page2.dart';
import 'package:bottomnavigation_sample/page3.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;
  Map<int, GlobalKey<NavigatorState>> navigatorKeys = {
     0: GlobalKey<NavigatorState>(),
     1: GlobalKey<NavigatorState>(),
     2: GlobalKey<NavigatorState>(),
  };
    final List<Widget> _widgetOptions = <Widget>[
    const page1(),
    const page2(),
    const page3()
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
       bottomNavigationBar: BottomNavigationBar(
         items: const <BottomNavigationBarItem>[
           BottomNavigationBarItem(
             icon: Icon(Icons.home),
             label: 'Home',
           ),
           BottomNavigationBarItem(
             icon: Icon(Icons.business),
             label: 'Business',
           ),
           BottomNavigationBarItem(
             icon: Icon(Icons.school),
             label: 'School',
           ),
         ],
         currentIndex: _selectedIndex,
         selectedItemColor: Colors.amber[800],
         onTap: _onItemTapped,
       ),
      body:  buildNavigator(),
    );
  }

   buildNavigator() {
     return Navigator(
       key: navigatorKeys[_selectedIndex],
       onGenerateRoute: (RouteSettings settings){
         return MaterialPageRoute(builder: (_) => _widgetOptions.elementAt(_selectedIndex));
       },
     );
  }
}
qltillow

qltillow4#

你可以在body里面放一个占位符这样结构就像这样

- AppBar
- body (dynamic content from placeholder)
- BottomNavigationBar

然后,您将有另一个类作为占位符。因此,每次您点击BottomNavigationBar时,它都会刷新正文的内容
我找到的一个例子是https://willowtreeapps.com/ideas/how-to-use-flutter-to-build-an-app-with-bottom-navigation
这里,但有点太复杂了,对我不起作用
这个https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf

q5lcpyga

q5lcpyga5#

你可以在Stack widget中创建Navigator widget来使用BottomNavigationBar和标签页的内部导航。你可以使用WillPopScope来处理Android的后退按钮来弹出标签页的内部屏幕。同样,双击底部导航项来弹出标签页的所有内部屏幕。
我为此创建了一个Sample app
希望这能有所帮助!

ntjbwcob

ntjbwcob6#

另一种实现这一点的方法(虽然不是好的做法)是在你的支架体中嵌套一个材料应用,并在那里处理所有的“子导航”。
因此,您的层次结构将如下所示

Material App
  - home
     - Scaffold
       - body
         - Material App
              - Scaffold
                  - AppBar
                  - body
                  ...
         - routes (internal)
       - bottomNavigationBar
  - routes (external)

我已经尝试过了,它的工作完美。不幸的是,我不能张贴的源代码了。

n3schb8v

n3schb8v7#

我认为#正确的方法是将BottomNavigationBar Package 在Hero中,在两种情况下使用相同的标记。这样,当页面之间的动画发生时,它们将被排除。
这是我所能做的最简短的例子,但我强烈建议清理它,即传递英雄字符串,使用小部件而不是一个巨大的构建块,为BottomNavigationBar制作自己的小部件。
请注意,在英雄过渡期间,它至少在我的手机上运行overflow by 0.0000191 pixels,但在发布模式下,我不认为这是一个问题。
进口“ Package :扑/料.镖”;

void main() => runApp(new MaterialApp(
      home: new Builder(
        builder: (context) => new Scaffold(
              bottomNavigationBar: new Hero(
                tag: "bottomNavigationBar",
                child: new BottomNavigationBar(items: [
                  new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text("Home")),
                  new BottomNavigationBarItem(icon: new Icon(Icons.ac_unit), title: new Text("AC Unit"))
                ]),
              ),
              body: new SafeArea(
                child: new Container(
                  constraints: new BoxConstraints.expand(),
                  color: Colors.green,
                  child: new Column(
                    children: <Widget>[
                      new RaisedButton(
                          child: new Text("Press me"),
                          onPressed: () {
                            Navigator.push(
                                context,
                                new MaterialPageRoute(
                                    builder: (context) => new Scaffold(
                                          bottomNavigationBar: new Hero(
                                            tag: "bottomNavigationBar",
                                            child: new BottomNavigationBar(items: [
                                              new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text("Home")),
                                              new BottomNavigationBarItem(icon: new Icon(Icons.ac_unit), title: new Text("AC Unit"))
                                            ]),
                                          ),
                                          body: new SafeArea(
                                            child: new Container(
                                              constraints:
                                                  new BoxConstraints.expand(),
                                              color: Colors.red,
                                              child: new Column(
                                                children: <Widget>[
                                                  new RaisedButton(
                                                    onPressed: () =>
                                                        Navigator.pop(context),
                                                    child: new Text("Back"),
                                                  )
                                                ],
                                              ),
                                            ),
                                          ),
                                        )));
                          })
                    ],
                  ),
                ),
              ),
            ),
      ),
    ));

我不知道hero系统处理多个英雄等有多好,如果你说想动画导航栏,这可能不会工作得太好。
还有另一种方法可以让你的底部导航栏动画化;这其实是一个已经被解答的问题抖音:英雄转场+小程序动画同时进行?

gpfsuwkq

gpfsuwkq8#

    • 截图:**

起点:

void main() => runApp(MaterialApp(home: HomePage()));
    • 一月一日**[一月一日+一月二日]
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: BottomNavigationBar(
        backgroundColor: Colors.orange,
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.call), label: 'Call'),
          BottomNavigationBarItem(icon: Icon(Icons.message), label: 'Message'),
        ],
      ),
      body: Navigator(
        onGenerateRoute: (settings) {
          Widget page = Page1();
          if (settings.name == 'page2') page = Page2();
          return MaterialPageRoute(builder: (_) => page);
        },
      ),
    );
  }
}
    • 第1页:**
class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Page1')),
      body: Center(
        child: RaisedButton(
          onPressed: () => Navigator.pushNamed(context, 'page2'),
          child: Text('Go to Page2'),
        ),
      ),
    );
  }
}
    • 第2页:**
class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Scaffold(appBar: AppBar(title: Text('Page2')));
}

相关问题