[英]Flutter how to add a tabbarview using SliverToBoxAdapter?
[英]How to add Grids into TabbarView in Flutter?
所以基本上我有这个小部件:
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
// ...
SliverToBoxAdapter(
child: TabBar(
controller: this._controller,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 1.0, color: Colors.red),
),
tabs: [
Tab(icon: Icon(CupertinoIcons.camera)),
Tab(icon: Icon(CupertinoIcons.photo)),
Tab(icon: Icon(CupertinoIcons.video_camera)),
],
),
),
SliverFillRemaining(
child: TabBarView(
controller: this._controller,
children: [
// Want Scrollable Grid here
// Want Scrollable Grid here
Center(
child: Text("Hello Reader🙂"),
),
],
),
),
// ...
],
),
);
}
}
我想在TabBarView
中添加 2 个可滚动网格作为子项,但是当我使用GridView.builder(...)
时,网格顶部有一个令人讨厌的间隙,即使使用shrinkWrap: true
和physics: NeverScrollableScrollPhysics()
。
但是,当我使用SliverGrid(...)
时,会出现此错误
RenderObjects 期望特定类型的子对象,因为它们在布局和绘制过程中与子对象协调。 例如,RenderSliver 不能是 RenderBox 的子级,因为 RenderSliver 不理解 RenderBox 布局协议。
这显然是有道理的,因为TabBarView
不是 sliver 小部件。 我已经看过这篇文章,但它并没有真正的帮助。
我怎么能实现这个? 有没有一种方法可以创建自己的小部件构建器来构建自定义布局?
谢谢你!
您需要使用 SliverOverlapAbsorber/SliverOverlapInjector,以下代码适用于我(在 dart 垫上工作的完整代码):
在这里,我使用SliverFixedExtentList
但您可以将其替换为SliverGrid
。
@override
State<StatefulWidget> createState() => _NewsScreenState();
}
class _NewsScreenState extends State<NewsScreen> {
final List<String> listItems = [];
final List<String> _tabs = <String>[
"Featured",
"Popular",
"Latest",
];
@override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
body: DefaultTabController(
length: _tabs.length, // This is the number of tabs.
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
// These are the slivers that show up in the "outer" scroll view.
return <Widget>[
SliverOverlapAbsorber(
// This widget takes the overlapping behavior of the SliverAppBar,
// and redirects it to the SliverOverlapInjector below. If it is
// missing, then it is possible for the nested "inner" scroll view
// below to end up under the SliverAppBar even when the inner
// scroll view thinks it has not been scrolled.
// This is not necessary if the "headerSliverBuilder" only builds
// widgets that do not overlap the next sliver.
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverSafeArea(
top: false,
sliver: SliverAppBar(
title: const Text('Books'),
floating: true,
pinned: true,
snap: false,
primary: true,
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
// These are the widgets to put in each tab in the tab bar.
tabs: _tabs
.map((String name) => Tab(text: name))
.toList(),
),
),
),
),
];
},
body: TabBarView(
// These are the contents of the tab views, below the tabs.
children: _tabs.map((String name) {
return SafeArea(
top: false,
bottom: false,
child: Builder(
// This Builder is needed to provide a BuildContext that is "inside"
// the NestedScrollView, so that sliverOverlapAbsorberHandleFor() can
// find the NestedScrollView.
builder: (BuildContext context) {
return CustomScrollView(
// The "controller" and "primary" members should be left
// unset, so that the NestedScrollView can control this
// inner scroll view.
// If the "controller" property is set, then this scroll
// view will not be associated with the NestedScrollView.
// The PageStorageKey should be unique to this ScrollView;
// it allows the list to remember its scroll position when
// the tab view is not on the screen.
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
// This is the flip side of the SliverOverlapAbsorber above.
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
// In this example, the inner scroll view has
// fixed-height list items, hence the use of
// SliverFixedExtentList. However, one could use any
// sliver widget here, e.g. SliverList or SliverGrid.
sliver: SliverFixedExtentList(
// The items in this example are fixed to 48 pixels
// high. This matches the Material Design spec for
// ListTile widgets.
itemExtent: 60.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
// This builder is called for each child.
// In this example, we just number each list item.
return Container(
color: Color((math.Random().nextDouble() *
0xFFFFFF)
.toInt() <<
0)
.withOpacity(1.0));
},
// The childCount of the SliverChildBuilderDelegate
// specifies how many children this inner list
// has. In this example, each tab has a list of
// exactly 30 items, but this is arbitrary.
childCount: 30,
),
),
),
],
);
},
),
);
}).toList(),
),
),
),
),
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.