如何在 Flutter 中创建这样的自定义可滑动标签栏?

How can I create a custom swipable tab bar like this in Flutter?

我想创建一个像上面那样的标签栏,它可以用动画滑动。我尝试使用 Flutter 中的默认 Tab 栏小部件来执行此操作,但失败了。我怎样才能让这个标签栏可以用动画滑动?

将所选标签动画从“朋友”标签下移到“家庭”标签背后的逻辑是什么?

我想出了 GestureDetector 小部件中的 onHorizontalDrag 属性 来进行滑动,但仍然无法用动画切换标签。

如果有人能帮助我,那就太好了。

这是使用 PageView 和 customAppBar 制作的。但是,如果你想在 tabBaritem 上使用 clickEvent,请用 GestureDetector 包装,使 callBackstateManagement 保持索引。

结果

MainWidget


class BU extends StatefulWidget {
  BU({Key? key}) : super(key: key);

  @override
  _BUState createState() => _BUState();
}

class _BUState extends State<BU> {
  int _selectedIndex = 0;

  final PageController controller = PageController(initialPage: 0);

  @override
  void initState() {
    super.initState();

    widgets = [
      ...List.generate(
        5,
        (index) => Container(
          alignment: Alignment.center,
          color: index.isEven ? Colors.cyanAccent : Colors.deepPurple,
          child: Text(
            "Item $index",
            style: TextStyle(fontSize: 44),
          ),
        ),
      )
    ];

    controller.addListener(() {
      if (controller.hasClients) {
        setState(() {
          /// you can ceil foor
          _selectedIndex = controller.page!.toInt();
        });
      }
    });
  }

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

  late List<Widget> widgets;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          AppBar(
            selectedItem: _selectedIndex,
          ),
          Expanded(
            child: Container(
                width: double.infinity,
                child: PageView(
                  controller: controller,
                  children: [...widgets],
                )),
          )
        ],
      ),
    );
  }
}

标签栏


class AppBar extends StatelessWidget {
  final int selectedItem;

  const AppBar({
    Key? key,
    required this.selectedItem,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(8),
      color: Colors.white,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              TabItemChip(
                isSelected: selectedItem == 0,
                text: "Profile",
              ),
              TabItemChip(
                isSelected: selectedItem == 1,
                text: "About",
              ),
              TabItemChip(
                isSelected: selectedItem == 2,
                text: "Friends",
              ),
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              TabItemChip(
                isSelected: selectedItem == 3,
                text: "Family",
              ),
              TabItemChip(
                isSelected: selectedItem == 4,
                text: "Settings",
              ),
            ],
          )
        ],
      ),
    );
  }
}

TabBarItemChip

class TabItemChip extends StatelessWidget {
  final bool isSelected;
  final String text;
  const TabItemChip({
    Key? key,
    required this.text,
    required this.isSelected,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: Duration(milliseconds: 400),
      height: 40,
      width: 140,
      alignment: Alignment.center,
      decoration: BoxDecoration(
        color: isSelected ? Colors.cyanAccent.shade700 : Colors.white,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Text(
        text,
        style: TextStyle(
          color: isSelected ? Colors.white : Colors.black,
          fontWeight: FontWeight.bold,
        ),
      ),
    );
  }
}