如何从另一个页面在底部导航栏中导航?

How to navigate in a bottom navigation bar from another page?

我使用的是普通底部导航栏,其本地状态包含当前所选页面的索引。当点击其中一个导航项时,将显示点击的页面:

class _HomePageState extends State<HomePage> {
    int _selectedIndex = 1;

    final _widgetOptions = [
        Text('Index 0'),
        Overview(),
        Details(),
        Text('Index 3'),
    ];

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            body: _widgetOptions.elementAt(_selectedIndex),
            bottomNavigationBar: BottomNavigationBar(
                ...
                currentIndex: _selectedIndex,
                onTap: onTap: (index) => _selectedIndex = index,
            ),
        );
    }
}

如果要从一个页面内部导航到另一个页面并更新底部导航栏的选定索引,如何进行?

示例: 第 2 页是一个概览列表,其中包含列表项的 ID。如果您点击列表中的一个项目,它应该被导航到详细信息页面(底部导航栏中的 3)显示所选项目的详细信息,因此应该传递所选项目的 ID。

Navigator.of(context)... 无法使用,因为各个页面是导航栏的项目,因此不是路线。

做一些改变,比如..

  int _currentIndex = 0;

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


  body: _widgetOptions[_currentIndex],

标签点击..

onTap: onTabTapped,

您将需要更好的状态管理方式。我建议您使用 BLoC 模式来管理此小部件中的导航更改。我将在此处放置一个关于如何通过一些评论和外部改进参考来执行此操作的简化示例。

// An enum to identify navigation index
enum Navigation { TEXT, OVERVIEW, DETAILS, OTHER_PAGE}

class NavigationBloc {
  //BehaviorSubject is from rxdart package
  final BehaviorSubject<Navigation> _navigationController 
    = BehaviorSubject.seeded(Navigation.TEXT);
  // seeded with inital page value. I'am assuming PAGE_ONE value as initial page.

  //exposing stream that notify us when navigation index has changed
  Observable<Navigation> get currentNavigationIndex => _navigationController.stream;

  // method to change your navigation index
  // when we call this method it sends data to stream and his listener
  // will be notified about it.
  void changeNavigationIndex(final Navigation option) => _navigationController.sink.add(option);

 void dispose() => _navigationController?.close();
}

这个集团 class 公开了一个流输出 currentNavigationIndexHomeScreen 将成为此输出的侦听器,它提供有关必须创建和显示在 Scaffold 小部件主体上的小部件的信息。请注意,流以 Navigation.TEXT.

的初始值开始

您的主页需要进行一些更改。现在我们正在使用 StreamBuilder 小部件来创建小部件并将其提供给 body 属性。换句话说,StreamBuilder 正在监听来自 bloc 的流输出,当收到一些数据时,这将是一个 Navigation enum 值,我们决定应该在主体上显示什么小部件。

class _HomePageState extends State<HomePage> {
  final NavigationBloc bloc = new NavigationBloc();
  int  _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<Navigation>(
        stream: bloc.currentNavigationIndex,
        builder: (context, snapshot){
          _selectedIndex = snapshot.data.index;

          switch(snapshot.data){
            case Navigation.TEXT:
              return Text('Index 0');

            case Navigation.OVERVIEW:
              // Here a thing... as you wanna change the page in another widget
             // you pass the bloc to this new widget for it's capable to change
             // navigation values as you desire.
              return Overview(bloc: bloc);
              //... other options bellow
            case Navigation.DETAILS:
              return Details(/* ... */);
          }
        },
      ),

      bottomNavigationBar: BottomNavigationBar(
      //...
      currentIndex: _selectedIndex,
      onTap: (index) => bloc.changeNavigationIndex(Navigation.values[index]),
      ),
    );
  }
  @override
  void dispose(){
    bloc.dispose();// don't  forgot this.
    super.dispoe();
  }
}

由于您想在单击其他小部件中的特定项目时更改主页的正文小部件,例如 Overview,那么您需要将 bloc 传递给这个新小部件并在您单击时item 你把新数据放到 Stream 中,body 会被刷新。请注意,这种将 BLoC 实例发送到另一个小部件的方法并不是更好的方法。我建议您看一下 InheritedWidget 模式。我在这里以一种简单的方式进行操作,以免写出更大的答案,而答案已经是...

class Overview extends StatelessWidget {
  final NavigationBloc bloc;

  Overview({this.bloc});

  @override
  Widget build(BuildContext context) {
    return YourWidgetsTree(
      //...
      // assuming that when you tap on an specific item yout go to Details page.
      InSomeWidget(
        onTap: () => bloc.changeNavigationIndex(Navigation.DETAILS);
      ),
    );
  }
}

我知道很多代码,这是一种复杂的方法,但就是这样。 setState 调用不会解决您的所有需求。看看这个 article 它是最好的和最大的之一,它讨论了我在这里写的所有内容,但细节最少。

祝你好运!

您可以使用Provider 来管理BottomNavigationBar 的索引。 创建一个 class 来处理 indexPage 并使用 Consumer 在您的小部件中使用它。不要忘记在

中注册 ChangeNotifierProvider
ChangeNotifierProvider<NavigationModel> (create: (context) => NavigationModel(),),

class NavigationModel extends ChangeNotifier {
  int _pageIndex = 0;
  int get page => _pageIndex;

  changePage(int i) {
    _pageIndex = i;
    notifyListeners();
  }
}