【问题标题】:Hide the TabBar like a SliverAppBar像 SliverAppBar 一样隐藏 TabBar
【发布时间】:2019-05-29 17:10:26
【问题描述】:

因此,网络上有很多示例,您可以使用隐藏在滚动条上的SliverAppBar,而下面的TabBar 仍在显示。我找不到任何相反的方法:当我向上滚动时我只想隐藏TabBar,始终保持AppBar 持续显示。有谁知道如何做到这一点?

Here is a example with AppBar hiding(这不是我想要的,只是帮助更好地理解我想要的)。

更新

这是我迄今为止尝试过的,我认为它有效,但问题是我无法让 Positioned 字段中的 AppBar 具有正确的高度(例如 iPhone X 它的高度更大并与标签栏重叠)。

// this sliver app bar is only use to hide/show the tabBar, the AppBar  
// is invisible at all times. The to the user visible AppBar is below
return Scaffold(
  body: Stack(
    children: <Widget>[
      NestedScrollView(
        headerSliverBuilder:
            (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              floating: true,
              snap: true,
              pinned: false,
              bottom: TabBar(
                tabs: [
                  Tab(
                    child: Text(
                      "1",
                      textAlign: TextAlign.center,
                    ),
                  ),
                  Tab(
                    child: Text(
                      "2",
                      textAlign: TextAlign.center,
                    ),
                  ),
                  Tab(
                    child: Text(
                      "3",
                      textAlign: TextAlign.center,
                    ),
                  ),
                ],
                controller: _tabController,
              ),
            ),
          ];
        },
        body: TabBarView(
          children: [
            MyScreen1(),
            MyScreen2(),
            MyScreen3(),
          ],
          controller: _tabController,
          physics: new NeverScrollableScrollPhysics(),
        ),
      ),


      // Here is the AppBar the user actually sees. The SliverAppBar 
      // above will slide the TabBar underneath this one. However,
      // I can´t figure out how to give it the correct height.
      Container(
        child: Positioned(
          top: 0.0,
          left: 0.0,
          right: 0.0,
          child: AppBar(
            iconTheme: IconThemeData(
              color: Colors.red, //change your color here
            ),
            automaticallyImplyLeading: true,
            elevation: 0,
            title: Text("My Title"),
            centerTitle: true,

          ),
        ),
      ),

    ],

  ),
);

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    这里是如何做到这一点的,想法是GlobalKey的帮助下使用postframecallback来预先计算appBar height并添加exapandedHeight 如下所示,

    import 'package:flutter/material.dart';
    import 'package:flutter/scheduler.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
    
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin  {
    
      TabController _tabController;
      GlobalKey _appBarKey;
      double _appBarHight;
      @override
      void initState() {
        _appBarKey = GlobalKey();
        _tabController = TabController(length: 3, vsync: this);
        SchedulerBinding.instance.addPostFrameCallback(_calculateAppBarHeight);
        super.initState();
      }
      _calculateAppBarHeight(_){
        final RenderBox renderBoxRed = _appBarKey.currentContext.findRenderObject();
         setState(() {
      _appBarHight = renderBoxRed.size.height;
    });
        print("AppbarHieght = $_appBarHight");
      }
    
      @override
      Widget build(BuildContext context) {
        // this sliver app bar is only use to hide/show the tabBar, the AppBar
        // is invisible at all times. The to the user visible AppBar is below
        return Scaffold(
          body: Stack(
            children: <Widget>[
              NestedScrollView(
                headerSliverBuilder:
                    (BuildContext context, bool innerBoxIsScrolled) {
                  return <Widget>[
                    SliverAppBar(
                      floating: true,
                      expandedHeight: _appBarHight,
                      snap: true,
                      pinned: false,
                      bottom: TabBar(
                        tabs: [
                          Tab(
                            child: Text(
                              "1",
                              textAlign: TextAlign.center,
                            ),
                          ),
                          Tab(
                            child: Text(
                              "2",
                              textAlign: TextAlign.center,
                            ),
                          ),
                          Tab(
                            child: Text(
                              "3",
                              textAlign: TextAlign.center,
                            ),
                          ),
                        ],
                        controller: _tabController,
                      ),
                    ),
                  ];
                },
                body: TabBarView(
                  children: [
                    MyScreen1(),
                    MyScreen2(),
                    MyScreen3(),
                  ],
                  controller: _tabController,
                  physics: new NeverScrollableScrollPhysics(),
                ),
              ),
    
    
              // Here is the AppBar the user actually sees. The SliverAppBar
              // above will slide the TabBar underneath this one. However,
              // I can¥t figure out how to give it the correct height.
              Container(
                key: _appBarKey,
                child: Positioned(
                  top: 0.0,
                  left: 0.0,
                  right: 0.0,
                  child: AppBar(
    
                    backgroundColor: Colors.red,
                    iconTheme: IconThemeData(
                      color: Colors.red, //change your color here
                    ),
                    automaticallyImplyLeading: true,
                    elevation: 0,
                    title: Text("My Title"),
                    centerTitle: true,
    
                  ),
                ),
              ),
    
            ],
    
          ),
        );
      }
    
    }
    
    class MyScreen1 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Center(
            child: Text("My Screen 1"),
          ),
        );
      }
    }
    class MyScreen2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Center(
            child: Text("My Screen 2"),
          ),
        );
      }
    }
    class MyScreen3 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Center(
            child: Text("My Screen 3"),
          ),
        );
      }
    }
    

    编辑:

    经过更多调查,我发现了一个没有键或 MediaQuery“东西”的解决方案,只使用 SafeArea Widget 。请检查以下完整代码:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
    
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin  {
    
      TabController _tabController;
    
      @override
      void initState() {
    
        _tabController = TabController(length: 3, vsync: this);
    
        super.initState();
      }
    
    
      @override
      Widget build(BuildContext context) {
        // this sliver app bar is only use to hide/show the tabBar, the AppBar
        // is invisible at all times. The to the user visible AppBar is below
        return Scaffold(
          body: Stack(
            children: <Widget>[
              NestedScrollView(
    
                headerSliverBuilder:
                    (BuildContext context, bool innerBoxIsScrolled) {
                  return <Widget>[
                    SliverAppBar(
                      primary: true,
                      floating: true,
                      backgroundColor: Colors.blue,//.withOpacity(0.3),
                      snap: true,
                      pinned: false,
                      bottom: TabBar(
                        tabs: [
                          Tab(
                            child: Text(
                              "1",
                              textAlign: TextAlign.center,
                            ),
                          ),
                          Tab(
                            child: Text(
                              "2",
                              textAlign: TextAlign.center,
                            ),
                          ),
                          Tab(
                            child: Text(
                              "3",
                              textAlign: TextAlign.center,
                            ),
                          ),
                        ],
                        controller: _tabController,
                      ),
                    ),
                  ];
                },
                body: TabBarView(
                  children: [
                    MyScreen1(),
                    MyScreen2(),
                    MyScreen3(),
                  ],
                  controller: _tabController,
                  physics: new NeverScrollableScrollPhysics(),
                ),
              ),
    
    
              // Here is the AppBar the user actually sees. The SliverAppBar
              // above will slide the TabBar underneath this one. 
              // by using SafeArea it will.
              Positioned(
                top: 0.0,
                left: 0.0,
                right: 0.0,
                child: Container(
                  child: SafeArea(
                    top: false,
                    child: AppBar(
                      backgroundColor: Colors.blue,
    //                iconTheme: IconThemeData(
    //                  color: Colors.red, //change your color here
    //                ),
                      automaticallyImplyLeading: true,
                      elevation: 0,
                      title: Text("My Title",),
                      centerTitle: true,
                    ),
                  ),
                ),
              ),
    
            ],
    
          ),
        );
      }
    
    }
    
    class MyScreen1 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.yellow,
          child: Center(
            child: Text("My Screen 1"),
          ),
        );
      }
    }
    class MyScreen2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Center(
            child: Text("My Screen 2"),
          ),
        );
      }
    }
    class MyScreen3 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Center(
            child: Text("My Screen 3"),
          ),
        );
      }
    }
    

    【讨论】:

    • 抱歉暂时没有回复。与此同时,我找到了完全相同的解决方案:SafeArea 解决了我所有的问题。干得好!
    【解决方案2】:

    屏幕截图(Android)

    屏幕截图 (iPhone X)


    您非常接近,我刚刚修改了几行。我没有使用GlobalKey 和其他东西(postFrameCallback 等)就做到了。这是非常简单直接的方法。

    您只需将FlutterLogo 替换为您自己的小部件MyScreen1MyScreen2MyScreen3


    代码

    void main() => runApp(MaterialApp(home: HomePage()));
    
    class HomePage extends StatefulWidget {
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
      TabController _tabController;
    
      @override
      void initState() {
        super.initState();
        _tabController = TabController(length: 3, vsync: this);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Stack(
            children: <Widget>[
              NestedScrollView(
                headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                  return <Widget>[
                    SliverAppBar(
                      floating: true,
                      snap: true,
                      pinned: true,
                      bottom: PreferredSize(
                        preferredSize: Size(0, kToolbarHeight),
                        child: TabBar(
                          controller: _tabController,
                          tabs: [
                            Tab(child: Text("1")),
                            Tab(child: Text("2")),
                            Tab(child: Text("3")),
                          ],
                        ),
                      ),
                    ),
                  ];
                },
                body: TabBarView(
                  controller: _tabController,
                  children: [
                    FlutterLogo(size: 300, colors: Colors.blue), // use MyScreen1()
                    FlutterLogo(size: 300, colors: Colors.orange), // use MyScreen2()
                    FlutterLogo(size: 300, colors: Colors.red), // use MyScreen3()
                  ],
                  physics: NeverScrollableScrollPhysics(),
                ),
              ),
              Positioned(
                top: 0.0,
                left: 0.0,
                right: 0.0,
                child: MediaQuery.removePadding(
                  context: context,
                  removeBottom: true,
                  child: AppBar(
                    iconTheme: IconThemeData(color: Colors.red),
                    automaticallyImplyLeading: true,
                    elevation: 0,
                    title: Text("My Title"),
                    centerTitle: true,
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    

    【讨论】:

    • 干得好:) 但是,如果我们知道应用栏的高度是 56,那么从一开始就没有问题。如果您尝试了我的解决方案,您会发现 iPhone X 中的高度是 134 而不是 56,因此这个问题,我想我们曾经有过类似的讨论!。
    • 很高兴再次见到你,好吧我没有尝试你的解决方案,但据我所知,iPhone X 的高度不会是134,而是10044 用于状态栏 + 56 用于导航栏),但不确定您在说134 时考虑的高度,
    • 我也是,您的解决方案仅在完美世界中有效:),iPhone X 具有不同的设计尺寸,应用栏的 56 比应有的短得多,请检查此问题stackoverflow.com/questions/46197660/…
    • 我不确定pt 那里的术语,我来自Android 背景,我们有dp,(不确定这是否是iOS 中的pt),但现在,我可以检查 iPhone XR(和 iPhone X 不应该不同)状态栏为44 逻辑像素高,导航栏为56 逻辑像素,这使其成为100
    • @SaedNabil 我在 iPhone X 上运行了相同的代码,看起来你所谓的“完美世界的解决方案”在这里与56 完美配合。
    【解决方案3】:

    我认为使用嵌套脚手架非常容易。您不需要计算任何高度的地方。只需将标签栏放在 SilverAppBar 不在 SilverAppBar 下方。

    如果这不能解决您的问题,请随时发表评论。

    例子:

    return Scaffold(
         appBar: AppBar(), //your appbar that doesnt need to hide
         body: Scaffold(
               appBar: SilverAppBar(
    
                pinned: false,
                floating: false,
    
                flexibleSpace: new Column(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  new TabBar() //your tabbar that need to hide when scrolling
                 ])
                 )
    
                 body: //your content goes here
    
                 )
    
    );
    

    【讨论】:

    • 你自己试过了吗?它给了我错误The argument type 'SliverAppBar' can't be assigned to the parameter type 'PreferredSizeWidget?'.
    猜你喜欢
    • 2019-08-06
    • 2018-10-30
    • 2011-08-17
    • 1970-01-01
    • 2021-06-04
    • 2019-05-12
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    相关资源
    最近更新 更多