如何创建有界可滚动的 TabBarView
Posted
技术标签:
【中文标题】如何创建有界可滚动的 TabBarView【英文标题】:How to create a bounded scrollable TabBarView 【发布时间】:2019-05-13 19:14:28 【问题描述】:我需要在 Flutter 中实现如下布局。
当用户滚动时,我希望整个布局滚动(隐藏标题和标签栏)。但是,我不能将 TabBarView 嵌套在 ListView 中,因为 TabBarView 没有限定高度,而且 ListView 也不为其子级提供限定高度。
我已经看到了这些问题,但是对于这个用例,他们都有不满意的答案:
How can I have a TabView with variable height content within a Scrollable View with Flutter? :正是我需要的,但唯一的答案没有提供任何关于如何实现它的具体代码,只是对我无法弄清楚如何实现的功能(SliverList)的引用。 how to implement a sliverAppBar with a tabBar :提供的代码不起作用,因为 SliverList 不接受具有这种形状的构造函数(我尝试将其调整为使用委托但没有成功)。 Getting 'Horizontal viewport was given unbounded height.' with TabBarView in flutter:这个用例对我不起作用,因为我需要滚动整个布局,并且在那个答案中,标题固定在顶部。【问题讨论】:
【参考方案1】: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 属性,您可以在其中实际放置一些标题,当正文小部件滚动时,您可能会隐藏或固定在屏幕顶部,在此特定示例中,@987654327 @。
您可能需要查看RenderSliver 文档。
【讨论】:
非常感谢,这非常有效!我发现有点难以将我的头包裹在条子上,但你的例子让我更清楚。我希望文档有这样的例子! 谢谢兄弟。但我不能做滚动标签?我设置了 isScrollable: true 如何从左向右滑动小部件,反之亦然? 为了让 TabBar 的 Tabs 完美运行,...在 NestedScrollView 的主体中使用 TabBarView .... 这实际上是不对的IMO!您已经在 NestedScrollView 的主体中放置了一个 ListView。在您拥有 TabBarView 之前,标题部分中的 TabBar 是无用的。但是:如果你把 TabBarView 作为 body -> 你会遇到这些问题: TabBarView 需要一个有界的高度! (它不会自动占用剩余空间,您甚至不能使用 Extended 小部件来扩展列中的高度)。每个 Tab 内容都有不同的长度(高度),因此您甚至无法选择特定的高度。【参考方案2】:在 Miguel Ruvio 的回答之上,将正文中的 ListView 替换为 TabBarView 几乎可以让您按照 D.R. 的评论进行操作。当我在其中的一个小部件被包装在一列中时,我确实遇到了一些溢出问题。根据这个用 ListView 替换它 example
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 issue。
【讨论】:
【参考方案3】:请尝试以下代码:
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(),
],
),
),
)
【讨论】:
以上是关于如何创建有界可滚动的 TabBarView的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Flutter 中使用函数创建动态 TabBarView/渲染新 Tab?
如何在具有颤动的可滚动视图中使用具有可变高度内容的TabView?
快速创建一个对象的新实例并将其放入一个带有 TabBarView 按钮的数组中
如何在TabBarView中实现SingleChildScrollView(带有水平列表视图和垂直gridview)