Flutter:具有持久性BottomNavigationBar 的嵌套路由,但不会不必要地构建未选择的页面

标签 flutter dart

全程internetstackoverflow我已经搜索并看到了很多解决嵌套导航问题的解决方案,其中包含用于 Flutter 应用程序的持久底部导航栏。其中一些使用带有 IndexedStack 或 PageView 等的导航器。所有这些都很好除了 他们将不必要地构建未选择的选项卡(有时甚至在每次切换选项卡时重建所有选项卡),从而使解决方案无法执行。我终于想出了一个解决方案——因为我自己也在努力解决这个问题。

最佳答案

该解决方案非常基础,但希望您能够在此基础上进行构建和调整。它实现了以下目标:

  • 在保留BottomNavigationBar 的同时嵌套导航
  • 除非已选择,否则不会构建选项卡
  • 保留导航状态
  • 保留滚动状态(例如 ListView 的)

  • 导入 'package:flutter/material.dart';
    void main() {
      runApp(MaterialApp(
        home: MyApp(),
      ));
    }
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      List<Widget> _pages;
    
      List<BottomNavigationBarItem> _items = [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          label: "Home",
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.messenger_rounded),
          label: "Messages",
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.settings),
          label: "Settings",
        )
      ];
    
      int _selectedPage;
    
      @override
      void initState() {
        super.initState();
        _selectedPage = 0;
    
        _pages = [
          MyPage(
            1,
            "Page 01",
            MyKeys.getKeys().elementAt(0),
          ),
          // This avoid the other pages to be built unnecessarily
          SizedBox(),
          SizedBox(),
        ];
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: WillPopScope(
            onWillPop: () async {
              return !await Navigator.maybePop(
                MyKeys.getKeys()[_selectedPage].currentState.context,
              );
            },
            child: IndexedStack(
              index: _selectedPage,
              children: _pages,
            ),
          ),
          bottomNavigationBar: BottomNavigationBar(
            items: _items,
            currentIndex: _selectedPage,
            onTap: (index) {
              setState(() {
                // now check if the chosen page has already been built
                // if it hasn't, then it still is a SizedBox
                if (_pages[index] is SizedBox) {
                  if (index == 1) {
                    _pages[index] = MyPage(
                      1,
                      "Page 02",
                      MyKeys.getKeys().elementAt(index),
                    );
                  } else {
                    _pages[index] = MyPage(
                      1,
                      "Page 03",
                      MyKeys.getKeys().elementAt(index),
                    );
                  }
                }
    
                _selectedPage = index;
              });
            },
          ),
        );
      }
    }
    
    class MyPage extends StatelessWidget {
      MyPage(this.count, this.text, this.navigatorKey);
      final count;
      final text;
      final navigatorKey;
      @override
      Widget build(BuildContext context) {
        // You'll see that it will only print once
        print("Building $text with count: $count");
        return Navigator(
          key: navigatorKey,
          onGenerateRoute: (RouteSettings settings) {
            return MaterialPageRoute(
              builder: (BuildContext context) {
                return Scaffold(
                  appBar: AppBar(
                    title: Text(this.text),
                  ),
                  body: Center(
                    child: RaisedButton(
                      child: Text(this.count.toString()),
                      onPressed: () {
                        Navigator.of(context).push(MaterialPageRoute(
                            builder: (ctx) => MyCustomPage(count + 1, text)));
                      },
                    ),
                  ),
                );
              },
            );
          },
        );
      }
    }
    
    class MyCustomPage extends StatelessWidget {
      MyCustomPage(this.count, this.text);
      final count;
      final text;
      @override
      Widget build(BuildContext parentContext) {
        return Scaffold(
          appBar: AppBar(
            title: Text(this.text),
          ),
          body: Column(
            children: [
              Expanded(
                child: Container(
                  child: ListView.builder(
                    itemCount: 15,
                    itemBuilder: (context, index) {
                      return Container(
                        width: double.infinity,
                        child: Card(
                          child: Center(
                            child: RaisedButton(
                              child: Text(this.count.toString() + " pos($index)"),
                              onPressed: () {
                                Navigator.of(parentContext).push(MaterialPageRoute(
                                    builder: (ctx) =>
                                        MyCustomPage(count + 1, text)));
                              },
                            ),
                          ),
                        ),
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class MyKeys {
      static final first = GlobalKey(debugLabel: 'page1');
      static final second = GlobalKey(debugLabel: 'page2');
      static final third = GlobalKey(debugLabel: 'page3');
    
      static List<GlobalKey> getKeys() => [first, second, third];
    }
    

    关于Flutter:具有持久性BottomNavigationBar 的嵌套路由,但不会不必要地构建未选择的页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66404759/

    相关文章:

    flutter - Dart 为参数设置默认值

    keyboard - 如何使小部件粘在键盘顶部

    flutter - 将流与 rxdart 组合以在单个 StreamBuilder 中使用

    swift - 错误 : no such module 'FirebaseInstanceID' on ios

    Flutter 响应式文本字段匹配屏幕尺寸

    dart - "as"关键字在Dart语言中有什么作用?

    flutter - VsCode Flutter Dart 语法颜色,无法更改

    dart - 如何在 flutter 的http.MultipartRequest请求上上传文件时获取进度事件

    flutter - Flutter缓存管理器:清除特定URL的缓存

    flutter - 如何使变量成为最终变量并在 initState() 中为其赋值