【问题标题】: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 中实现标签栏,因为它有一个背景图像,在MerchantSliverAppbarbottom: 中实现标签栏会将标签栏放在图像顶部。

这是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. 我无法在不同页面中使用相同的应用栏。我需要将其模块化。

我该如何解决这个问题?

【问题讨论】:

    标签: flutter dart


    【解决方案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,
        );
      } 
    }
    

    【讨论】:

      猜你喜欢
      • 2021-05-11
      • 2020-08-09
      • 2018-12-16
      • 1970-01-01
      • 2020-01-24
      • 1970-01-01
      • 2014-05-23
      • 2011-04-24
      • 1970-01-01
      相关资源
      最近更新 更多