颤动中的自定义标签栏
Posted
技术标签:
【中文标题】颤动中的自定义标签栏【英文标题】:Custom Tab bar in flutter 【发布时间】:2022-01-23 23:30:39 【问题描述】:我需要实现一个粘性标签栏。同一个标签栏可以有不同的长度,因为不同的商家可以有不同数量的类别。
我当前的商家页面代码如下:
class _MerchantPageState extends State<MerchantPage>
with TickerProviderStateMixin
ScrollController _scrollController = ScrollController();
double _scrollPosition = 0;
late TabController _tabController;
_scrollListener()
setState(()
_scrollPosition = _scrollController.position.pixels;
);
GridContainerData? _products;
GridContainerData _getProductData()
return productGridContainerData;
@override
void initState()
super.initState();
_products = _getProductData();
_scrollController.addListener(_scrollListener);
_tabController = TabController(length: 2, vsync: this);
@override
Widget build(BuildContext context)
return SafeArea(
child: Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled)
return [
MerchantSliverAppbar(
merchant: widget.merchant!,
tabController: _tabController,
),
];
,
body: Column(
children: [
TabBar(
controller: _tabController,
unselectedLabelColor: Colors.redAccent,
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.redAccent, Colors.orangeAccent],
),
borderRadius: BorderRadius.circular(50),
color: Colors.redAccent,
),
isScrollable: true,
tabs: [
Tab(
child: Align(
alignment: Alignment.center,
child: Text("Category 1"),
),
),
Tab(
child: Align(
alignment: Alignment.center,
child: Text("Category 2"),
),
),
],
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
_buildProductFeed(),
_buildProductFeed(),
],
),
),
],
),
),
),
floatingActionButton: ChatButton(),
),
);
Widget _buildProductFeed()
return Container(
padding: EdgeInsets.all(10),
child: GridContainer(
data: _products,
isScrollable: true,
),
);
我无法在我的MerchantSliverAppbar
中实现标签栏,因为它有一个背景图像,在MerchantSliverAppbar
的bottom:
中实现标签栏会将标签栏放在图像顶部。
这是MerchantSliverAppbar的完整代码。
我的一个解决方案是:
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(
controller: _tabController,
unselectedLabelColor: Colors.redAccent,
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.redAccent, Colors.orangeAccent],
),
borderRadius: BorderRadius.circular(50),
color: Colors.redAccent,
),
physics: NeverScrollableScrollPhysics(),
isScrollable: true,
tabs: [
Tab(
child: Align(
alignment: Alignment.center,
child: Text("Category 1"),
),
),
Tab(
child: Align(
alignment: Alignment.center,
child: Text("Category 2"),
),
),
],
),
),
pinned: true,
),
在同一个文件中:
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate
_SliverAppBarDelegate(this._tabBar);
final TabBar _tabBar;
@override
double get minExtent => _tabBar.preferredSize.height;
@override
double get maxExtent => _tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent)
return new Container(
child: _tabBar,
);
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate)
return false;
这个实现的问题是:
-
我无法在不同页面中使用相同的应用栏。我需要将其模块化。
我该如何解决这个问题?
【问题讨论】:
【参考方案1】:事实证明我不需要这些。由于我使用的是名为 MerchantSliverAppbar
的 CustomSliverAppbar,因此我使用 SliverOverlapAbsorber 以下列方式将其包裹起来:
Widget build(BuildContext context)
return SafeArea(
child: Scaffold(
backgroundColor: CustomColors.white,
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled)
return [
SliverOverlapAbsorber(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverSafeArea(
sliver: MerchantSliverAppbar(merchant: widget.merchant!),
),
)
];
,
body: FeedSliderContainer(),
),
),
);
注意:
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverSafeArea(
sliver: MerchantSliverAppbar(merchant: widget.merchant!),
),
)
根据docs:
包裹另一个的条子,强制将其布局范围视为重叠。
其余部分与构建普通的 TabBar 非常相似,只需创建一个新的有状态小部件并放入以下内容:
List<String> categories = ['All', 'Women', 'Men', 'Kids', 'Gadgets', 'Shoes'];
late TabController _tabController;
@override
void initState()
super.initState();
_tabController = TabController(vsync: this, length: categories.length);
@override
void dispose()
_tabController.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return Container(
child: Builder(
builder: (BuildContext context)
_tabController.addListener(()
if (!_tabController.indexIsChanging)
int index = _tabController.index;
setState(() );
);
return Column(
children: <Widget>[
getTabBar(context, _tabController),
Expanded(
child: Container(
child: TabBarView(
controller: _tabController,
children: [
... data you wanna show
],
),
),
),
],
);
,
),
);
List<Widget> getTabs(TabController tabController)
return categories.map((category)
return Tab(
..customize the tabs
);
).toList();
Widget getTabBar(BuildContext context, TabController tabController)
return TabBar(
tabs: getTabs(tabController),
labelColor: CustomColors.primary,
unselectedLabelColor: CustomColors.secondary.withOpacity(0.5),
indicatorColor: Colors.transparent,
isScrollable: true,
padding: const EdgeInsets.all(3),
controller: tabController,
);
【讨论】:
以上是关于颤动中的自定义标签栏的主要内容,如果未能解决你的问题,请参考以下文章
iOS7标签栏自定义图标高度 - 高度减小直到图标变得不可见