【问题标题】:How to navigate in a bottom navigation bar from another page?如何从另一个页面在底部导航栏中导航?
【发布时间】:2019-09-08 13:59:23
【问题描述】:

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

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)... 无法使用,因为各个页面是导航栏的项目,因此不是路线。

【问题讨论】:

    标签: dart flutter navigation bottomnavigationview


    【解决方案1】:

    您将需要更好的状态管理方式。我建议您使用 BLoC 模式来管理此小部件中的导航更改。我将在此处放置一个简化示例,说明如何使用一些 cmets 和外部参考改进来做到这一点。

    // 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();
    }
    

    这个 bloc 类公开了一个流输出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,那么您需要将块传递给这个新小部件,当您单击项目时,您需要放置新的数据到 Stream 中,主体将被刷新。请注意,这种将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 这是最好的和更大的之一,它以最少的细节谈论了我在这里写的所有内容。

    祝你好运!

    【讨论】:

    • 感谢详细的回答,对我帮助很大!我最近对 ​​Bloc 模式有所了解,但在考虑如何在某些情况下使用它时总是遇到问题,例如这种情况。
    【解决方案2】:

    您可以使用Provider 来管理BottomNavigationBar 的索引。 创建一个类来处理 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();
      }
    }
    

    【讨论】:

      【解决方案3】:

      做一些改变,比如..

        int _currentIndex = 0;
      
       void onTabTapped(int index) {
      setState(() {
        _currentIndex = index;
      });
       }
      
      
        body: _widgetOptions[_currentIndex],
      

      标签点击..

      onTap: onTabTapped,
      

      【讨论】:

      • 这正是代码的真实样子,我只是为示例简化了它以保持整洁。但这如何解决我的问题?我无法从任何地方访问该实例。
      猜你喜欢
      • 2023-01-19
      • 2022-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-29
      • 1970-01-01
      • 1970-01-01
      • 2022-01-15
      相关资源
      最近更新 更多