繁体   English   中英

flutter 中的自定义标签栏

[英]Custom Tab bar in flutter

我需要实现一个粘性标签栏。 同一个标签栏可以有不同的长度,因为不同的商家可以有不同数量的类别。

我当前的商家页面代码如下:

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中实现标签栏,因为它有一个背景图像,并且在bottom: MerchantSliverAppbar将标签栏放在图像的顶部。

这是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!),
   ),
)

根据文档

包裹另一个的条子,迫使其布局范围被视为重叠。

rest 与构建普通的 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,
    );
  } 
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM