Скрыть TabBar как SliverAppBar

Так что в Интернете есть много примеров, где вы можете использовать SliverAppBar, который скрывается при прокрутке, а TabBar ниже все еще отображается. Я не могу найти ничего, что делало бы это наоборот: когда я прокручиваю Я хочу скрыть только TabBar вверх, постоянное отображение AppBar постоянно отображается. Кто-нибудь знает, как этого добиться?

Вот пример со скрытым AppBar (Это не то, что я хочу, просто помогает лучше понять, чего я хочу).

ОБНОВИТЬ

Это то, что я пробовал до сих пор, и я думал, что это работает, но проблема в том, что я не могу получить AppBar в поле Positioned, чтобы иметь правильную высоту (например, 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,

          ),
        ),
      ),

    ],

  ),
);
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
9
0
5 218
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Здесь - это то, как вы можете это сделать, идея в том использовать postframecallback с помощью GlobalKey для предварительного расчета 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. проверьте следующий полный код:

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 решает все мои проблемы. Молодец!

最白目 12.06.2019 15:07

Я думаю, что это довольно легко использовать вложенные леса. где вам не нужно вычислять любую высоту. Просто поместите вкладку в 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?'.

RDK 06.08.2021 09:49

Скриншот (Android)

Скриншот (iPhone X)


Вы были очень близки, я только что изменил пару строк. Я сделал это без использования GlobalKey и прочего (postFrameCallback и т.д.). Это очень простой и понятный подход.

Все, что вам нужно сделать, это заменить FlutterLogo своими собственными виджетами, а именно MyScreen1, MyScreen2 и MyScreen3.


Код

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, отсюда и вопрос: я думаю, что однажды у нас была похожая дискуссия!

Saed Nabil 08.06.2019 23:21

Рад видеть вас снова, ну, я не пробовал ваше решение, но, насколько я могу судить, высота в iPhone X не будет 134, а будет 100 (44 для строки состояния + 56 для панели навигации), но не уверен, какой рост вы имеете в виду, когда говорите 134,

CopsOnRoad 09.06.2019 08:56

Я тоже, ваше решение действительно только в идеальном мире :), iPhone X имеет другие размеры дизайна, и 56 намного короче для панели приложений, чем должно, проверьте этот вопрос stackoverflow.com/questions/46197660/…

Saed Nabil 09.06.2019 11:27

Я не уверен по термину pt там, я пришел из Android, где у нас есть dp, (не уверен, что это то, что pt в iOS), но сейчас я могу проверить в iPhone XR (и iPhone X не должен отличаться ) строка состояния имеет высоту 44 логических пикселей, а панель навигации имеет высоту 56 логических пикселей, что делает ее 100

CopsOnRoad 09.06.2019 11:36

@SaedNabil Я запустил тот же код в iPhone X, и кажется, что ваше так называемое «решение для идеального мира» сработало и здесь безупречно с 56.

CopsOnRoad 09.06.2019 14:14

пожалуйста, сравните эти два изображения image1 imgur.com/CNZjfSG image two imgur.com/w2Iey55 вы можете заметить разницу в высоте панели приложений, красная панель приложений является стандартной высотой для iPhone X, и я знаю, что это работает, но проблема в том, что это не стандартная высота, как я объяснял ранее .

Saed Nabil 09.06.2019 14:46

Да, вы правы, разница есть, и я не тестировал его на iOS, когда писал код, я запускал его на Android (скриншот которого я загрузил первым), но опять же, вам не нужно иметь дело с 134, Я сделал тот же эффект с 100 в обновленном коде, и теперь он работает и для iOS.

CopsOnRoad 09.06.2019 15:10

Я вообще не имею дело с какими-либо константами, ни 134, ни 56, код динамически вычисляет высоту и адаптируется к любым размерам устройства, я проверил ваш код, и, похоже, ничего не изменилось в отношении высоты, пожалуйста, проверьте это изображение imgur.com/ciXHjC7

Saed Nabil 09.06.2019 16:55

Даже я больше не играл с 56, я использовал kToolbarHeight (надеюсь, вы не будете указывать на это сейчас), и скриншот, который вы прислали, такой же, как и при использовании appBar: AppBar() , так что в этом нет ничего плохого.

CopsOnRoad 09.06.2019 16:57

Это было очень интересно :), я думаю, что нашел правильное решение, пожалуйста, проверьте раздел «Правка» моего ответа.

Saed Nabil 10.06.2019 03:44

Я в отпуске пару дней и не могу понять ни одной части кода (использую телефон для комментариев), если вам понравилось мое решение, я буду ждать +1 с вашей стороны :)

CopsOnRoad 10.06.2019 10:10

Другие вопросы по теме