Flutter自定义TabBar视图插件view_tabbar的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter 自定义 TabBar 视图插件 view_tabbar 的使用

ViewTabBar

ViewTabBar 基于 TabBarControllerPageController,实现了 TabBarPageView 之间的 UI 解耦及联动。

  • 可实现 TabBar + PageView (horizontal)
  • 可实现 TabBar + PageView (vertical)
  • 可实现 Carousel (轮播图)

如何安装

  1. pubspec.yaml 添加:

    dependencies:
      view_tabbar: ^1.3.2
    
  2. 在命令行运行如下:

    flutter pub get
    

如何使用

TabBar + PageView (pinned)
import 'package:flutter/material.dart';
import 'package:view_tabbar/view_tabbar.dart';

class HorizontalWithPinned extends StatelessWidget {
  HorizontalWithPinned({super.key});

  final pageController = PageController();
  final tabBarController = ViewTabBarController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    const tags = ['板块1', '板块2', '板块3', '板块4'];
    const duration = Duration(milliseconds: 300);

    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        // ViewTabBar
        ViewTabBar(
          pinned: true,
          itemCount: tags.length,
          direction: Axis.horizontal,
          pageController: pageController,
          tabBarController: tabBarController,
          animationDuration: duration, // 取消动画 -> Duration.zero
          builder: (context, index) {
            return ViewTabBarItem(
              index: index,
              transform: ScaleTransform(
                maxScale: 1.2,
                transform: ColorsTransform(
                  normalColor: const Color(0xff606266),
                  highlightColor: const Color(0xff436cff),
                  builder: (context, color) {
                    return Container(
                      alignment: Alignment.center,
                      padding: const EdgeInsets.only(
                        top: 8.0,
                        left: 10.0,
                        right: 10.0,
                        bottom: 8.0,
                      ),
                      child: Text(
                        tags[index],
                        style: TextStyle(
                          color: color,
                          fontWeight: FontWeight.w500,
                          fontSize: 14.0,
                        ),
                      ),
                    );
                  },
                ),
              ),
            );
          },

          // StandardIndicator
          indicator: StandardIndicator(
            color: const Color(0xff436cff),
            width: 27.0,
            height: 2.0,
            bottom: 0,
          ),
        ),

        // PageView
        Expanded(
          flex: 1,
          child: PageView.builder(
            itemCount: tags.length,
            controller: pageController,
            scrollDirection: Axis.horizontal,
            itemBuilder: (context, index) {
              return Container(
                padding: const EdgeInsets.only(
                  top: 16.0,
                  left: 16.0,
                  right: 16.0,
                  bottom: 16.0,
                ),
                child: Text(
                  '这里渲染显示 ${tags[index]} 的内容',
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.w400,
                    color: Color(0xff606266),
                  ),
                ),
              );
            },
          ),
        ),
      ],
    );
  }
}
Carousel (轮播图)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:view_tabbar/view_tabbar.dart';

class CarouselWithTarBar extends StatefulWidget {
  const CarouselWithTarBar({super.key});

  [@override](/user/override)
  CarouselWithTarBarState createState() => CarouselWithTarBarState();
}

class CarouselWithTarBarState extends State<CarouselWithTarBar> {
  final pageController = PageController(initialPage: 1);
  final tabBarController = ViewTabBarController();

  int _currentIndex = 1;
  Timer? _timer;

  // 定时轮播 - 每隔 3s
  void _setTimer() {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 3), (_) {
      int page = _currentIndex + 1;

      pageController.animateToPage(
        page,
        duration: const Duration(milliseconds: 400),
        curve: Curves.easeOut,
      );
    });
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    _setTimer();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.only(
        top: 10.0,
        bottom: 12.0,
      ),
      clipBehavior: Clip.antiAlias,
      decoration: ShapeDecoration(
        gradient: const LinearGradient(
          begin: Alignment(0.00, -1.00),
          end: Alignment(0, 1),
          stops: [0, 0.2, 1],
          colors: [
            Color(0xFFEEF3FF),
            Color(0xFFEEF3FF),
            Colors.white,
          ],
        ),
        shape: RoundedRectangleBorder(
          side: const BorderSide(width: 1.50, color: Colors.white),
          borderRadius: BorderRadius.circular(12.0),
        ),
      ),
      child: Column(
        children: [
          // 标题
          Container(
            height: 24.0,
            alignment: Alignment.centerLeft,
            margin: const EdgeInsets.only(top: 10.0),
            padding: const EdgeInsets.symmetric(horizontal: 20.0),
            child: const Text(
              "职业类型",
              style: TextStyle(
                color: Color(0xFF101828),
                fontSize: 18.0,
                fontFamily: 'PingFang SC',
                fontWeight: FontWeight.w500,
                height: 1,
              ),
            ),
          ),

          // PageView
          Container(
            height: 72.0,
            margin: const EdgeInsets.only(
              top: 16.0,
              bottom: 20.0,
            ),
            padding: const EdgeInsets.only(
              left: 16.0,
              right: 10.0,
            ),
            child: NotificationListener(
              onNotification: (notification) {
                if (notification is! ScrollNotification ||
                    notification.depth != 0) {
                  return false;
                }

                if (notification is ScrollUpdateNotification) {
                  // 关闭定时器
                  _timer?.cancel();
                }

                if (notification is ScrollStartNotification) {
                  if (notification.dragDetails != null) {
                    // 关闭定时器
                    _timer?.cancel();
                  }
                }

                if (notification is ScrollEndNotification) {
                  final page = pageController.page?.round();

                  // last, 处理 end 边界
                  if (page == 7) {
                    Future.delayed(const Duration(milliseconds: 10), () {
                      pageController.jumpToPage(1);
                      _setTimer();
                    });
                    return true;
                  }

                  // first, 处理 start 边界
                  if (page == 0) {
                    Future.delayed(const Duration(milliseconds: 10), () {
                      pageController.jumpToPage(3);
                      _setTimer();
                    });
                    return true;
                  }

                  // 延时启动定时器
                  Future.delayed(
                    const Duration(milliseconds: 20),
                    () {
                      setState(() {
                        _currentIndex = page ?? 1;
                        _setTimer();
                      });
                    },
                  );
                }

                return true;
              },
              child: PageView.builder(
                itemCount: 8,
                controller: pageController,
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) {
                  // first, 处理 start 边界
                  if (index == 0) {
                    index = 6;
                  }

                  // last, 处理 end 边界
                  if (index == 7) {
                    index = 1;
                  }

                  return renderPageViewContent(
                    context,
                    index,
                  );
                },
              ),
            ),
          ),

          // TarBar
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Expanded(
                flex: 0,
                child: ClipRect(
                  clipper: ViewTabBarClipper(),
                  child: ViewTabBar(
                    pinned: true,
                    height: 14.0,
                    width: 160.0,
                    direction: Axis.horizontal,
                    pageController: pageController,
                    tabBarController: tabBarController,
                    animationDuration: const Duration(milliseconds: 300),
                    indicator: StandardIndicator(
                      width: 15.0,
                      height: 4.0,
                      color: const Color(0xff436cff),
                      radius: const BorderRadius.all(Radius.circular(3.0)),
                      bottom: 5,
                    ),
                    itemCount: 8,
                    builder: (context, index) {
                      return ViewTabBarItem(
                        index: index,
                        child: Container(
                          width: 14.0,
                          height: 4.0,
                          margin: const EdgeInsets.only(
                            left: 2.0,
                            right: 2.0,
                          ),
                          decoration: const BoxDecoration(
                            borderRadius: BorderRadius.all(
                              Radius.circular(2),
                            ),
                            color: Color(0x66436cff),
                          ),
                        ),
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

// 截取 TabBar 容器大小
class ViewTabBarClipper extends CustomClipper<Rect> {
  [@override](/user/override)
  Rect getClip(Size size) {
    // 共 8 个 tab item (每个 tab -> 宽度: 20, 高度: 14)
    // 截取保留 第 2 - 7 tab 元素 -> Rect.fromLTWH(20, 0, 120, 14)
    return const Rect.fromLTWH(20, 0, 120, 14);
  }

  [@override](/user/override)
  bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
    return false;
  }
}

// 渲染 PageView 内容
Widget renderPageViewContent(context, index) {
  // PageView 内容 ....
}

更多关于Flutter自定义TabBar视图插件view_tabbar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义TabBar视图插件view_tabbar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用自定义TabBar视图插件view_tabbar的示例代码。请注意,由于view_tabbar可能不是一个广泛认知的官方或知名第三方插件,我假设它是一个自定义的或特定项目中的插件,并且可能具有特定的API。下面的代码示例将展示如何假设性地集成和使用这样一个插件。

首先,确保你的pubspec.yaml文件中已经包含了view_tabbar(如果它是一个可通过pub.dev获取的包):

dependencies:
  flutter:
    sdk: flutter
  view_tabbar: ^x.y.z  # 替换为实际的版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中创建一个使用ViewTabBar(假设这是插件中TabBar组件的名称)的示例页面。以下是一个假设性的实现:

import 'package:flutter/material.dart';
import 'package:view_tabbar/view_tabbar.dart'; // 假设这是插件的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter ViewTabBar Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ViewTabBar Demo'),
      ),
      body: Column(
        children: [
          // 使用假设的ViewTabBar组件
          Expanded(
            child: ViewTabBar(
              controller: _tabController,
              tabs: [
                Tab(icon: Icon(Icons.directions_car), text: 'Tab 1'),
                Tab(icon: Icon(Icons.directions_transit), text: 'Tab 2'),
                Tab(icon: Icon(Icons.directions_bike), text: 'Tab 3'),
              ],
              indicatorColor: Colors.blue,
              indicatorSize: TabBarIndicatorSize.tab,
              indicatorWeight: 2.0,
              labelColor: Colors.blue,
              labelStyle: TextStyle(fontWeight: FontWeight.bold),
              unselectedLabelColor: Colors.grey,
              // 假设ViewTabBar还有其他自定义属性,可以在这里设置
              // customProperty: value,
              onTabSelected: (index) {
                // 当标签被选中时触发的回调
                print('Tab $index selected');
              },
            ),
          ),
          // Tab的内容区域
          TabBarView(
            controller: _tabController,
            children: [
              Center(child: Text('Content of Tab 1')),
              Center(child: Text('Content of Tab 2')),
              Center(child: Text('Content of Tab 3')),
            ],
          ),
        ],
      ),
    );
  }
}

注意

  1. ViewTabBarTabBarIndicatorSize等类和方法在上面的代码中是基于假设的,因为view_tabbar插件的具体实现细节未知。
  2. 你需要根据view_tabbar插件的实际API文档来调整上述代码。
  3. 如果view_tabbar不是一个公开可用的包,你可能需要从源代码或私有仓库中引入它,并参考相应的文档或源代码来了解如何正确使用。

确保你查阅了view_tabbar插件的官方文档或源代码,以获取准确的API信息和用法示例。

回到顶部