如何使用 tabBar 实现 sliverAppBar
Posted
技术标签:
【中文标题】如何使用 tabBar 实现 sliverAppBar【英文标题】:how to implement a sliverAppBar with a tabBar 【发布时间】:2018-11-17 09:34:01 【问题描述】:flutter 文档显示一个demo for SliverAppBar
+ TabBar
+ TabBarView with ListView
使用NestedScrollView
,而且有点复杂,不知道有没有简单明了的实现方式。我试过这个:
CustomScrollView
slivers:
SliverAPPBar
bottom: TabBar
TabBarView
children: MyWidget(list or plain widget)
出现错误:
flutter:在构建 Scrollable(axisDirection: right, physical: 时抛出了以下断言 颤动:RenderViewport 期望 RenderSliver 类型的子级,但收到 _RenderExcludableScrollSemantics 类型的子级。 Flutter:RenderObjects 期望特定类型的子对象,因为它们在布局和绘制过程中与子对象协调。例如,RenderSliver 不能是 RenderBox 的子级,因为 RenderSliver 不理解 RenderBox 布局协议。
和
flutter:引发了另一个异常:'package:flutter/src/widgets/framework.dart':断言失败:第 3497 行 pos 14:'owner._debugCurrentBuildTarget == this':不正确。
这是我的代码:
import 'package:flutter/material.dart';
main(List<String> args)
runApp(MyScrollTabListApp());
class MyScrollTabListApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(title: "aa", home: MyScrollTabListHomePage());
class MyScrollTabListHomePage extends StatefulWidget
@override
MyScrollTabListHomePageState createState()
return new MyScrollTabListHomePageState();
class MyScrollTabListHomePageState extends State<MyScrollTabListHomePage>
with SingleTickerProviderStateMixin
final int _listItemCount = 300;
final int _tabCount = 8;
TabController _tabController;
@override
void initState()
_tabController = TabController(length: _tabCount, vsync: this);
super.initState();
@override
Widget build(BuildContext context)
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 240.0,
title: Text("Title"),
pinned: true,
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: List<Tab>.generate(_tabCount, (int i)
return Tab(text: "TAB$i");
),
),
),
TabBarView(
controller: _tabController,
children: List<Widget>.generate(_tabCount, (int i)
return Text('line $i');
),
),
],
),
);
对于官方演示,它使用这样的结构
DefaultTabController
NestedScrollView
headerSliverBuilder
SliverOverlapAbsorber
handle
SliverAppBar
TabBarView
CustomScrollView
SliverOverlapInjector
handle
SliverPadding
【问题讨论】:
在这里查看答案:***.com/questions/50433885/sliverappbar-with-tabbar/… 【参考方案1】:使用NestedScrollView
,这是工作代码。
@override
Widget build(BuildContext context)
return Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder: (context, value)
return [
SliverAppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: "Call"),
Tab(icon: Icon(Icons.message), text: "Message"),
],
),
),
];
,
body: TabBarView(
children: [
CallPage(),
MessagePage(),
],
),
),
),
);
【讨论】:
像魅力一样工作......谢谢。【参考方案2】:这是一个带有 SilverAppBar 的 TabView 示例
class SilverAppBarWithTabBarScreen extends StatefulWidget
@override
_SilverAppBarWithTabBarState createState() => _SilverAppBarWithTabBarState();
class _SilverAppBarWithTabBarState extends State<SilverAppBarWithTabBarScreen>
with SingleTickerProviderStateMixin
TabController controller;
@override
void initState()
super.initState();
controller = new TabController(length: 3, vsync: this);
@override
Widget build(BuildContext context)
return new Scaffold(
body: new CustomScrollView(
slivers: <Widget>[
new SliverAppBar(
title: Text("Silver AppBar With ToolBar"),
pinned: true,
expandedHeight: 160.0,
bottom: new TabBar(
tabs: [
new Tab(text: 'Tab 1'),
new Tab(text: 'Tab 2'),
new Tab(text: 'Tab 3'),
],
controller: controller,
),
),
new SliverList(
new SliverFillRemaining(
child: TabBarView(
controller: controller,
children: <Widget>[
Text("Tab 1"),
Text("Tab 2"),
Text("Tab 3"),
],
),
),
],
),
);
【讨论】:
是的,正如@Negora 所说,没有TabBarView
,并且当标题底部的标签发生变化时,页面正文不会改变
@DhirajSharma 作为代码new SliverList( new SliverFillRemaining(...)
,似乎SliverList
没有这样的构造函数,我们可以直接将SliverFillRemaining
作为参数传递
@DhirajSharma 我的问题是每个TabView
都包含一个长条列表(这意味着一个可滚动的小部件),而不是一个简单的小部件。【参考方案3】:
是的。您可以使用 NestedScrollView 来实现选项卡。这是一些附加代码。
class AppView extends StatelessWidget
final double _minValue = 8.0;
@override
Widget build(BuildContext context)
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: MyAppBar(),
drawer: DrawerDialog(),
body: DefaultTabController(
length: 3,
child: SafeArea(
child: NestedScrollView(
body: TabBarView(
children: [Text("Page 1"), Text("Page 2"), Text("Page 3")],
),
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => [
SliverPadding(
padding: EdgeInsets.all(_minValue * 2.5),
sliver: SliverToBoxAdapter(
child: Text(
"Hiding Header",
style: textTheme.headline6,
textAlign: TextAlign.center,
),
),
),
SliverAppBar(
backgroundColor: Colors.grey[100],
pinned: true,
elevation: 12.0,
leading: Container(),
titleSpacing: 0.0,
toolbarHeight: 10,
bottom: TabBar(tabs: [
Tab(
child: Text(
"All",
style: textTheme.subtitle2,
),
),
Tab(
child: Text(
"Categories",
style: textTheme.subtitle2,
),
),
Tab(
child: Text(
"Upcoming",
style: textTheme.subtitle2,
),
),
]),
),
],
),
),
),
);
【讨论】:
【参考方案4】:您也可以通过将 _tabController 提供给 TabBar() 和 TabBarView 来实现它,这样它就会绑定。 而对于 TabBarView 的孩子,如果您使用 ListView 则给它物理:NeverScrollablePhysics() 所以它不会移动,请不要您必须为 ListView 的容器提供动态高度所以它会加载所有孩子的。
class _HomeState extends State<Home> with SingleTickerProviderStateMixin
TabController _tabController;
@override
void initState()
_tabController = TabController(length: 2, vsync: this);
super.initState();
@override
Widget build(BuildContext context)
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
floating: true,
expandedHeight: 50,
title: Column(
children: [
Row(
children: [
Text('Hello, User'),
Spacer(),
InkWell(
child: Icon(Icons.map_rounded),
),
],
),
],
),
),
SliverList(
delegate: SliverChildListDelegate([
_tabSection(context),
])),
],
));
Widget _tabSection(BuildContext context)
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
double mainAxisHeight = height > width ? height : width;
return DefaultTabController(
length: 2,
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Container(
height: 48,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10))),
child: TabBar(
indicatorColor: Colors.white,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: Colors.white, width: 5.0),
insets: EdgeInsets.symmetric(horizontal: 40),
),
labelColor: Colors.white,
unselectedLabelColor: Colors.grey[300],
tabs: [
Tab(
iconMargin: EdgeInsets.only(top: 5),
text: "Tab Bar 1",
),
Tab(
iconMargin: EdgeInsets.only(top: 5),
text: "Tab bar 2",
),
]),
),
Container(
height: 200 * 6 // 200 will be Card Size and 6 is number of Cards
child: TabBarView(controller: _tabController, children: [
tabDetails(),
tabDetails(),
]))
]));
tabDetails()
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
double mainAxisHeight = height > width ? height : width;
return Container(
padding: EdgeInsets.symmetric(horizontal: 15),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.red[100],
Colors.red[200],
])),
child: ListView(
physics: NeverScrollableScrollPhysics(), // This will disable LitView'Scroll so only Scroll is possible by TabBarView Scroll.
children: [
SizedBox(height: 10),
Container(
height:140,
width: width,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: 6,
itemBuilder: (BuildContext context, int indexChild)
return Row(
children: [
MyListTile(
name: "Name",
),
SizedBox(width: 5),
],
);
,
),
),
SizedBox(height: 1000),
],
),
);
【讨论】:
【参考方案5】:如果你想使用CustomScrollView
,你可以使用SliverToBoxAdapter
:
Widget build(BuildContext context)
return Scaffold(
body: DefaultTabController(
length: 2,
child: CustomScrollView(
slivers: [
SliverAppBar(
title: Text('SliverAppBar'),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: "Call"),
Tab(icon: Icon(Icons.message), text: "Message"),
],
),
),
SliverToBoxAdapter(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: TabBarView(
children: [
Container(color: Colors.red),
Container(color: Colors.blue),
],
),
),
),
],
),
),
);
【讨论】:
以上是关于如何使用 tabBar 实现 sliverAppBar的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Tabbar 上的 Single viewController 中使用两个 tableView
React Native 中 StackNavigation 内的 TabBar