【问题标题】:Flutter pop to root when bottom navigation tapped当底部导航被点击时颤振弹出到根目录
【发布时间】:2018-09-12 15:24:42
【问题描述】:

我想要实现功能,当用户点击BottomNavigationBarItem 弹出到根目录时,如果当前页面索引等于像普通 iOS 应用程序那样点击的索引。 我尝试如下。我在MaterialApp 和HomeScreen 中设置所有根页面路由,如果currentIndex 等于index,popUntil 到根页面。 但是错误说

flutter:处理手势时引发以下 StateError:
颤振:坏状态:未来已经完成

我怎样才能做到这一点?

代码:

// MyApp class 
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          routes: <String, WidgetBuilder>{
            '/Page1': (BuildContext context) => new Page1(),
            '/Page2': (BuildContext context) => new Page2(),
            '/Page3': (BuildContext context) => new Page3(),
            '/Page4': (BuildContext context) => new Page4(),
          },
          title: 'Flutter Example',
          home: new HomeScreen(),
        );
      }
    }


  // MyHome class
    class HomeScreen extends StatefulWidget {
      @override
      _HomeScreenState createState() => new _HomeScreenState();
    }

    class _HomeScreenState extends State<HomeScreen> {
      final List<StatelessWidget> pages = [
        new Page1(),
        new Page2(),
        new Page3(),
        new Page3(),
      ];
      final List<String> routes = [
        '/Page1',
        '/Page2',
        '/Page3',
        '/Page4',
      ];

      int currentIndex = 0;

      @override
      Widget build(BuildContext context) {
        return new WillPopScope(
          onWillPop: () => new Future<bool>.value(true),
          child: new CupertinoTabScaffold(
            tabBar: new CupertinoTabBar(
              onTap: (index) {
                if (index == currentIndex) {
                  Navigator.popUntil(context, ModalRoute.withName(routes[index]));
                }
                currentIndex = index;
              },
              backgroundColor: Colors.white,
              activeColor: Colors.blue,
              inactiveColor: Colors.grey,
              items: const <BottomNavigationBarItem>[
                BottomNavigationBarItem(
                  icon: Icon(Icons.looks_one),
                  title: Text('Page1'),
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.looks_two),
                  title: Text('Page2'),
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.looks_3),
                  title: Text('Page3'),
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.looks_4),
                  title: Text('Page4'),
                ),
              ],
            ),
            tabBuilder: (BuildContext context, int index) {
              return new DefaultTextStyle(
                style: const TextStyle(
                  fontFamily: '.SF UI Text',
                  fontSize: 17.0,
                  color: CupertinoColors.black,
                ),
                child: new CupertinoTabView(
                  builder: (BuildContext context) {
                    return pages[index];
                  },
                ),
              );
            },
          ),
        );
      }
    }

【问题讨论】:

  • 如果您将onWillPop 更改为async () =&gt; true 会怎样?
  • 我都做了,但结果相同。我在做你的意思吗? onWillPop: () async { new Future&lt;bool&gt;.value(true); // return true; }
  • 我用这个运行你的代码:return new WillPopScope( onWillPop: () async { return await Future&lt;bool&gt;.value(true); },没有收到任何错误。
  • 你确定吗?点击相同的索引后,仍然会黑屏。

标签: flutter


【解决方案1】:

自从 Flutter v1.1.1 将 navigatorKey 添加到 CupertinoTabView 以来,现在可以实现此行为 (https://github.com/flutter/flutter/commit/65df90d8b5e1145e1022c365bb0465aa8c30dcdf)

首先,您必须为每个选项卡声明一个GlobalKey&lt;NavigatorState&gt;,然后将其传递给相应的CupertinoTabView 构造函数。 然后,在CupertinoTabBar 的onTap 方法中,如果用户在同一选项卡中点击,您可以使用firstTabNavKey.currentState.popUntil((r) =&gt; r.isFirst) 弹出到根目录。

下面的完整示例:

void main() {
  runApp(MyApp());
}
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
  int currentIndex = 0;

  final GlobalKey<NavigatorState> firstTabNavKey = GlobalKey<NavigatorState>();
  final GlobalKey<NavigatorState> secondTabNavKey = GlobalKey<NavigatorState>();
  final GlobalKey<NavigatorState> thirdTabNavKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: CupertinoTabScaffold(
        tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                navigatorKey: firstTabNavKey,
                builder: (BuildContext context) => FirstTab(),
              );
              break;
            case 1:
              return CupertinoTabView(
                navigatorKey: secondTabNavKey,
                builder: (BuildContext context) => SecondTab(),
              );
              break;
            case 2:
              return CupertinoTabView(
                navigatorKey: thirdTabNavKey,
                builder: (BuildContext context) => ThirdTab(),
              );
              break;
          }
          return null;
        },
        tabBar: CupertinoTabBar(
          items: <BottomNavigationBarItem> [
            BottomNavigationBarItem(
              title: Text('Tab 1'),
            ),
            BottomNavigationBarItem(
              title: Text('Tab 2'),
            ),
            BottomNavigationBarItem(
              title: Text('Tab 3'),
            ),
          ],
          onTap: (index){
            // back home only if not switching tab
            if(currentIndex == index) {
              switch (index) {
                case 0:
                  firstTabNavKey.currentState.popUntil((r) => r.isFirst);
                  break;
                case 1:
                  secondTabNavKey.currentState.popUntil((r) => r.isFirst);
                  break;
                case 2:
                  thirdTabNavKey.currentState.popUntil((r) => r.isFirst);
                  break;
              }
            }
            currentIndex = index;
          },
        ),
      ),
    );
  }
}

【讨论】:

  • 帮了我很大的忙,其实你可以创建一个GlobalKey&lt;NavigatorState&gt;()的列表并直接在onTap中访问正确的项目..navigators[index].currentState.popUntil((r) =&gt; r.isFirst)???✈️
  • 对于仍在为此苦苦挣扎的任何人,请将 CupertinoTabScaffold 包装在 WillPopScope 中。见github.com/flutter/flutter/issues/24105#issuecomment-530222677。将maybePop 替换为popUntil。这使上述答案对我有用。
  • 另外由于某种原因,使用@AllanStepps 建议的列表对我不起作用。我不得不使用多个变量。
【解决方案2】:

您可以使用pushAndRemoveUntil 达到相同的效果(如果我正确理解您的需求)。

final PageRouteBuilder _homeRoute = new PageRouteBuilder(
  pageBuilder: (BuildContext context, _, __) {
    return HomeScreen();
  }, 
);

void _goHome() {
  Navigator.pushAndRemoveUntil(context, _homeRoute, (Route<dynamic> r) => false);
}

这具有能够利用 PageRouteBuilder 的其他属性的额外好处。

【讨论】:

  • 感谢您的评论,它在弹出到根目录方面起作用,但它总是返回到索引 0,这意味着第一个选项卡。我将HomeScreen() 更改为pages[currentIndex] 并返回到正确的索引。但是底部导航会消失,因此不会有底部标签。知道如何解决吗?
【解决方案3】:

这个简单的代码对我来说很有效

onPressed: () {
     Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/'));
            }

【讨论】:

  • 这应该是一个相当大的答案
【解决方案4】:

以下工作,在我的设置中,所有路线都被命名:

Navigator.of(context)
         .pushNamedAndRemoveUntil('homeScreen', (route) => false);

感谢@blaneyneil 和@Juanma Menendez 的回答

【讨论】:

    猜你喜欢
    • 2019-12-25
    • 1970-01-01
    • 2021-03-14
    • 2023-01-11
    • 2017-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多