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

Mar*_*erl 2 navigation dart flutter bottomnavigationview

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

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,
            ),
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您想从一个页面内部导航到另一个页面并更新底部导航栏的选定索引,该怎么办?

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

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

Mar*_*ura 5

您将需要更好的状态管理方式。我建议您使用 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();
}
Run Code Online (Sandbox Code Playgroud)

该块类公开一个流输出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();
  }
}
Run Code Online (Sandbox Code Playgroud)

例如,由于您想在单击其他小部件中的特定项目时更改主页的主体小部件,Overview因此您需要将块传递给这个新小部件,并且当您单击项目时,您将新数据放入主体的流中将会被刷新。请注意,这种将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);
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道很多代码,这是一种复杂的方法,但就是这样。setState 调用并不能解决您的所有需求。看看这篇文章,这是最好的、规模最大的文章之一,它以最少的细节讨论了我在这里写的所有内容。

祝你好运!