Как создать ограниченный прокручиваемый TabBarView

Мне нужно реализовать во Flutter следующий макет.

«Макет»

Когда пользователь прокручивает, я хочу, чтобы прокручивался весь макет (скрывая заголовок и панель вкладок). Однако я не могу вложить TabBarView в ListView, поскольку TabBarView не имеет ограниченной высоты, а ListView не предоставляет ограниченную высоту своим дочерним элементам.

Я уже видел эти вопросы, но все они имеют неудовлетворительные ответы для этого варианта использования:


person Ozymas    schedule 12.12.2018    source источник


Ответы (3)


введите описание изображения здесь

class SliverWithTabBar extends StatefulWidget {
  @override
  _SliverWithTabBarState createState() => _SliverWithTabBarState();
}

class _SliverWithTabBarState extends State<SliverWithTabBar> with SingleTickerProviderStateMixin {
  TabController controller;

  @override
  void initState() {
    super.initState();
    controller = TabController(length: 3, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return [
            SliverAppBar(
              pinned: false,
              backgroundColor: Colors.white,
              flexibleSpace: FlexibleSpaceBar(
                collapseMode: CollapseMode.pin,
                background: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Container(
                      height: 200.0,
                      width: double.infinity,
                      color: Colors.grey,
                      child: FlutterLogo(),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Business Office',
                        style: TextStyle(fontSize: 25.0),
                        textAlign: TextAlign.left,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Open now\nStreet Address, 299\nCity, State',
                        style: TextStyle(fontSize: 15.0),
                        textAlign: TextAlign.left,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(right: 10.0),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          Icon(Icons.share),
                          Padding(
                            padding: const EdgeInsets.only(left: 10.0),
                            child: Icon(Icons.favorite),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ),
              expandedHeight: 380.0,
              bottom: TabBar(
                indicatorColor: Colors.black,
                labelColor: Colors.black,
                tabs: [
                  Tab(text: 'POSTS'),
                  Tab(text: 'DETAILS'),
                  Tab(text: 'FOLLOWERS'),
                ],
                controller: controller,
              ),
            )
          ];
        },
        body: ListView.builder(
          itemCount: 100,
          itemBuilder: (BuildContext context, int index) {
            return Card(
              color: index % 2 == 0 ? Colors.blue : Colors.green,
              child: Container(
                alignment: Alignment.center,
                width: double.infinity,
                height: 100.0,
                child: Text(
                  'Flutter is awesome',
                  style: TextStyle(fontSize: 18.0),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

Вам следует искать Sliver виджеты, чтобы достичь NestedScrollView.

Это дает вам свойство headerSliverBuilder, в котором вы можете разместить некоторые заголовки, которые вы можете скрыть или закрепить в верхней части экрана при прокрутке основного виджета, в данном конкретном примере это ListView.

Возможно, вы захотите ознакомиться с документацией RenderSliver.

person Miguel Ruivo    schedule 12.12.2018
comment
Большое спасибо, это сработало отлично! Мне было немного трудно обдумать осколки, но ваш пример сделал мне это намного понятнее. Хотелось бы, чтобы в документации были такие примеры! - person Ozymas; 13.12.2018
comment
Спасибо, брат. но я не могу прокрутить вкладку? я установил isScrollable: true - person Muhammad Imanudin; 10.06.2019
comment
Как пролистать виджет слева направо или наоборот? - person Vinoth Vino; 30.04.2020
comment
Чтобы вкладки TabBar работали идеально ... используйте TabBarView в теле NestedScrollView .... - person Darshan Rathod; 30.07.2020

В дополнение к ответу Мигеля Рувио, заменяющему ListView в теле на TabBarView, вы почти полностью получаете комментарий D.R. У меня возникли проблемы с переполнением, когда один из моих виджетов в был заключен в столбец. Замена этого на ListView в соответствии с этим примером

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: Tabs()));
}

class Tabs extends StatefulWidget {
  @override
  _RoomTabsState createState() => _RoomTabsState();
}

class _RoomTabsState extends State<Tabs> with TickerProviderStateMixin {
  var _scrollViewController;
  var _tabController;

  @override
  void initState() {
    super.initState();
    _scrollViewController = ScrollController();
    _tabController = TabController(vsync: this, length: 2);
  }

  @override
  Widget build(BuildContext context) {
    return NestedScrollView(
      controller: _scrollViewController,
      headerSliverBuilder: (context, bool) => [
        SliverAppBar(
          bottom: TabBar(
            controller: _tabController,
            tabs: [
              Tab(text: "All"),
              Tab(text: "Living room"),
            ],
          ),
        ),
      ],
      body: TabBarView(
        controller: _tabController,
        children: [
          ListView(children: [
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
          ]),
          Text("test"),
        ],
      ),
    );
  }
}

происходит из этой проблемы с github.

person Ryan Palmer    schedule 05.11.2020

Пожалуйста, попробуйте этот код ниже:

 NestedScrollView(
  headerSliverBuilder: (context, value) {
    return [
      SliverToBoxAdapter(
          child: Header()
      ),
      SliverToBoxAdapter(
        child: TabBar(
          controller: _controller,
          tabs: [
            Tab(icon: Icon(Icons.x)),
            Tab(icon: Icon(Icons.y)),
            Tab(icon: Icon(Icons.z)),
          ],
        ),
      ),
    ];
  },
body: Container(
  child: TabBarView(
    controller: _controller,
    children: <Widget>[
      page1(),
      page2(),
      page3(),
    ],
  ),
),

)

person Mehedi Hasan    schedule 08.02.2021