繁体   English   中英

Flutter 自定义 TabBar 指示器

[英]Flutter Customizing TabBar Indicator

如何自定义Flutter TabBar中的Tab指示器,达到如下目标效果?

当前 STATE 当前标签栏状态

目标目标 TabBar 结果

我怎样才能做到以下几点:

  1. 在目标图片中更改未选中的选项卡
  2. 调整波纹边框以适合内容并具有圆角边缘。

这是我的代码

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var categoryTabs = <Tab>[...];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: categoryTabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('My App'),
          centerTitle: true,
          bottom: PreferredSize(
            preferredSize: Size(100, 70),
            child: Column(
              children: [
                TabBar(
                  indicatorSize: TabBarIndicatorSize.tab,
                  indicatorColor: Colors.transparent,
                  labelColor: colorPrimaryDark,
                  isScrollable: true,
                  unselectedLabelColor: Colors.white,
                  indicator: BoxDecoration(
                    borderRadius: BorderRadius.circular(50),
                    color: Colors.white,
                  ),
                  tabs: categoryTabs,
                ),
                SizedBox(height: 10)
              ],
            ),
          ),
        ),
        body: SafeArea(...),
      ),
    );
  }
}

检查此代码

  class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.red,
          bottom: PreferredSize(
            preferredSize: Size(100,20),
            child: Column(
              children: [
                TabBar(
                 // indicatorSize: TabBarIndicatorSize.tab,
                  indicatorColor: Colors.transparent,
                  labelColor: Colors.white,
                  unselectedLabelColor: Colors.black,
                  indicator: BoxDecoration(
                      borderRadius: BorderRadius.circular(50),
                      color: Colors.redAccent),
                  tabs: [
                    Tab(icon: Icon(Icons.directions_car)),
                    Tab(icon: Icon(Icons.report_problem)),
                    Tab(icon: Icon(Icons.report_problem)),
                  ],
                ),
                SizedBox(height: 10,)
              ],
            ),
          ),
        ),
        body: TabBarView(
          children: [
            Icon(Icons.directions_car),
            Icon(Icons.directions_transit),
            Icon(Icons.directions_bike),
          ],
        )
      ),
    );
  }
}

当Tab Controller从一个索引中的标签控制到另一个索引时,您需要使用AnimatedBuilder来收听选项卡控制器动画。 使用这种方法,您可以自定义TabBar项的过渡动画。 例如:将您的TabBar实现更改为以下内容

TabBar(
    tabs: tabs
      .asMap().entries
      .map((entry) => AnimatedBuilder(
        animation: _tabController.animation,
        builder: (ctx, snapshot) {
          
          final forward = _tabController.offset > 0;
          final backward = _tabController.offset < 0;
          int _fromIndex;
          int _toIndex;
          double progress;

          // This value is true during the [animateTo] animation that's triggered when the user taps a [TabBar] tab. 
          // It is false when [offset] is changing as a consequence of the user dragging the [TabBarView].
          if (_tabController.indexIsChanging) {
            _fromIndex = _tabController.previousIndex;
            _toIndex = _tabController.index;
            _cachedFromIdx = _tabController.previousIndex;
            _cachedToIdx = _tabController.index;
            progress = (_tabController.animation.value - _fromIndex).abs() / (_toIndex - _fromIndex).abs();
          } else {
            if (_cachedFromIdx == _tabController.previousIndex && _cachedToIdx == _tabController.index) {
              // When user tap on a tab bar and the animation is completed, it will execute this block
              // This block will not be called when user draging the TabBarView
              _fromIndex = _cachedFromIdx;
              _toIndex = _cachedToIdx;
              progress = 1;
              _cachedToIdx = null;
              _cachedFromIdx = null;
            } else {
              _cachedToIdx = null;
              _cachedFromIdx = null;
              _fromIndex = _tabController.index;
              _toIndex = forward
                ? _fromIndex + 1
                  : backward
                    ? _fromIndex - 1
                    : _fromIndex;
              progress = (_tabController.animation.value - _fromIndex).abs();
            }
          }
          
          return Container(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            decoration: BoxDecoration(
              color: entry.key == _fromIndex
                ? Color.lerp(Colors.white, Colors.red.shade900, progress)
                : entry.key == _toIndex
                  ? Color.lerp(Colors.red.shade900, Colors.white, progress)
                  : Color.lerp(Colors.red.shade900, Colors.red.shade900, progress),
              borderRadius: BorderRadius.circular(200),
            ),
            child: Text(
              entry.value.toUpperCase(),
              style: TextStyle(
                fontSize: 10,
                letterSpacing: 0.4,
                fontWeight: FontWeight.w700,
              ),
            ),
          );
          
        },
      ))
      .toList(),
    controller: _tabController,
    isScrollable: true,
    indicatorSize: TabBarIndicatorSize.label,
    indicatorWeight: 0,
    indicator: BoxDecoration(
      borderRadius: BorderRadius.circular(100),
    ),
    physics: const ClampingScrollPhysics(),
    unselectedLabelColor: Colors.white,
    labelColor: Colors.red,
    labelPadding: EdgeInsets.only(left: 12),
  ),

请注意,在上面的示例中,我使用 2 个局部变量来跟踪当前和以前的索引。 这专门用于处理用户在TabBar选项卡上的选项卡在一个索引与另一个索引之间设置动画的情况。 将 log 命令放在AnimatedBuilder的 builder 方法中,以更好地了解它是如何工作的。

 int _cachedFromIdx;
 int _cachedToIdx;

这是结果

在此处输入图片说明

使用这个包 -> Buttons_tabbar...这个包为您提供选择或未选择的标签栏指示器。

https://pub.dev/packages/buttons_tabbar

暂无
暂无

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

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