Flutter - 使用底部导航栏图标进行多页面导航

9
我试图使用底部导航栏中的图标导航到应用程序内的不同页面。我尝试了许多教程,似乎找不到最好的方法来实现这一点。我已经创建了我的主页(下面是代码)和另外两个页面,收件箱和登录,都返回简单的脚手架。
首先我想知道这是否是我尝试实现的最佳方法,其次,如何更改我的代码以允许我根据点击的图标而导航到不同的页面。我知道下面的代码不会执行,我只是想展示我尝试过的内容。
我的代码:
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  _onTap(int index) {
    Navigator.of(context)
        .push(MaterialPageRoute<Null>(builder: (BuildContext context) {
      return _children[_currentIndex];
    }));}

  final List<Widget> _children = [
    HomePage(),
    InboxPage(),
    SignInPage()
  ];

  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
      SizeConfig().init(context);
      return Scaffold(
        appBar: PreferredSize(preferredSize: Size(double.infinity, 75),
          child: AppBar(
              elevation: 0.0,
              centerTitle: false,
              title: Column(
                children: <Widget>[
                  Align(
                    alignment: Alignment.centerLeft,
                    child: Text(
                      currentDate,
                      textAlign: TextAlign.left,
                      style: TextStyle(
                          color: titleTextColor,
                          fontWeight: subTitleFontWeight,
                          fontFamily: titleFontFamily,
                          fontSize: subTitleFontSize),
                    ),
                  ),
                  SizedBox(
                    height: 15,
                  ),
                  Align(
                    alignment: Alignment.centerLeft,
                    child: Text(
                      'Some text here',
                      style: TextStyle(
                          color: titleTextColor,
                          fontWeight: titleTextFontWeight,
                          fontFamily: titleFontFamily,
                          fontSize: titleFontSize),
                    ),
                  ),
                ],
              ),
              backgroundColor: kPrimaryColor,
              shape: titleBarRounding
          ),
        ),
        body: BodyOne(),
        bottomNavigationBar: BottomNavigationBar(
            currentIndex: _currentIndex,
            type: BottomNavigationBarType.fixed,
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text('Home'),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.mail),
                title: Text('Inbox'),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.account_circle),
                title: Text('Account'),


              )
            ],
          onTap: () => _onTap(_currentIndex),
        ),);
    }
  }

提前感谢。


这真的是你想做的吗?通常使用BottomNavigationBar的方法是替换包含BottomNavigationBar的Scaffold的主体,而不使用Navigator。因此,在onTap中,您设置了_currentIndex,就像Mobina的答案中提到的那样,然后主体仅包含_children [_currentIndex]。 - NiklasLehnfeld
也许这不是我要找的。如果我的问题不够清晰,我很抱歉。我基本上有3个页面,每个页面都需要相同的底部导航栏(当前页面突出显示),但应用程序栏和主体内容会根据我所在的页面而改变。您认为我应该采取不同的方法吗?谢谢。 - TJMitch95
是的。我也这么认为!DidierPeran Ganthier 给出的答案非常好。这是通常的做法。 - NiklasLehnfeld
3个回答

19

您当前所在的屏幕不能成为您要导航到的屏幕的一部分,您无需每次都推送一个新屏幕,只需更改selectedPage即可,以下是示例:

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int selectedPage = 0;

  final _pageOptions = [
    HomeScreen(),
    InboxScreen(),
    SignInScreen()
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.white,
        body: _pageOptions[selectedPage],
        bottomNavigationBar: BottomNavigationBar(
          items: [
            BottomNavigationBarItem(icon: Icon(Icons.home, size: 30), title: Text('Home')),
            BottomNavigationBarItem(icon: Icon(Icons.mail, size: 30), title: Text('Inbox')),
            BottomNavigationBarItem(icon: Icon(Icons.account_circle, size: 30), title: Text('Account')),
          ],
          selectedItemColor: Colors.green,
          elevation: 5.0,
          unselectedItemColor: Colors.green[900],
          currentIndex: selectedPage,
          backgroundColor: Colors.white,
          onTap: (index){
            setState(() {
              selectedPage = index;
            });
          },
        )
    );
  }
}

如果您需要更多解释,请告诉我。


当我使用这种方法浏览屏幕时,我在之前屏幕上的进度会丢失。例如,我向下滚动->导航并返回->页面从顶部开始。如何确保它存储进度? - Aleksandre Bregadze
@AleksandreBregadze 你尝试过使用SharedPreferences吗? - Didier Peran Ganthier
一个有趣的建议,我用它来做其他事情,但我也可以用它来解决我的问题。谢谢! - Aleksandre Bregadze

1
  1. _onTap函数的输入参数未被使用,需要删除。
_onTap() {
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (BuildContext context) => _children[_currentIndex])); // this has changed
  }
  1. BottomNavigationBaronTap 中,您需要更改 _currentIndex,然后调用 _onTap 函数,该函数导航到所选屏幕。
onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
          _onTap();
        },

你可以将这个 BottomNavigationBar 添加到所有屏幕中,但要注意 _currentIndex 的初始值会根据你放置 BottomNavigationBar 的屏幕而改变。
完整代码:
_onTap() { // this has changed
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (BuildContext context) => _children[_currentIndex])); // this has changed
  }

  final List<Widget> _children = [
    HomePage(),
    InboxPage(),
    SignInPage()
  ];

  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);
    return Scaffold(
      appBar: PreferredSize(
        preferredSize: Size(double.infinity, 75),
        child: AppBar(
          elevation: 0.0,
          centerTitle: false,
          title: Column(
            children: <Widget>[
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  currentDate,
                  textAlign: TextAlign.left,
                    style: TextStyle(
                        color: titleTextColor,
                        fontWeight: subTitleFontWeight,
                        fontFamily: titleFontFamily,
                        fontSize: subTitleFontSize),
                ),
              ),
              SizedBox(
                height: 15,
              ),
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  'Some text here',
                    style: TextStyle(
                        color: titleTextColor,
                        fontWeight: titleTextFontWeight,
                        fontFamily: titleFontFamily,
                        fontSize: titleFontSize),
                ),
              ),
            ],
          ),
            backgroundColor: kPrimaryColor,
            shape: titleBarRounding
        ),
      ),
      body: BodyOne(),
      body: Container(),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('Home'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.mail),
            title: Text('Inbox'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.account_circle),
            title: Text('Account'),
          )
        ],
        onTap: (index) { // this has changed
          setState(() {
            _currentIndex = index;
          });
          _onTap();
        },
      ),
    );
  }

0

最好的方法是创建一个屏幕包装器。像这样:

class Wrapper extends StatefulWidget {
  Wrapper();

  _WrapperState createState() => _WrapperState();
}

class _WrapperState extends State<Wrapper> {
  int _currentIndex = 0;
  final List<Widget> _children = [
    HomePage(),
    InboxPage(),
    SignInPage()
  ];

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _children[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        onTap: onTabTapped,
        currentIndex: _currentIndex,
        items:[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('Home'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.mail),
            title: Text('Inbox'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.account_circle),
            title: Text('Account'),
          )
        ],
      ),
    );
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接