You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现PlayStation风格的底部TabBar?

实现PlayStation风格的Flutter底部TabBar

我希望在Flutter中实现PlayStation风格的底部TabBar,目前已编写了如下CustomClipper代码:

import 'package:flutter/material.dart';

class BottomBarClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    
    final notchRadius = 25.0;

    path.lineTo(size.width / 2 - notchRadius * 2, 0);
    path.relativeArcToPoint(
      Offset(notchRadius * 2, notchRadius),
      radius: Radius.circular(notchRadius),
      clockwise: false,
    );
    path.lineTo(size.width / 2 + notchRadius * 2, 0);
    path.lineTo(size.width, 0);
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);
    path.close();

    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

但我不确定如何修改这个CustomClipper,以实现中间按钮向上凸起的PlayStation风格底部TabBar效果。


修改后的CustomClipper(实现凸起效果)

要实现中间向上凸起的形状,需要调整路径绘制逻辑,把原来的凹口改成向外凸起的弧度:

import 'package:flutter/material.dart';

class PSBottomBarClipper extends CustomClipper<Path> {
  final double notchRadius;

  PSBottomBarClipper({this.notchRadius = 30.0});

  @override
  Path getClip(Size size) {
    final path = Path();
    final centerX = size.width / 2;
    final notchDiameter = notchRadius * 2;

    // 从左下角开始绘制路径
    path.moveTo(0, size.height);
    // 画到凸起左侧的起点
    path.lineTo(centerX - notchDiameter, size.height);
    // 绘制向上凸起的圆弧
    path.arcToPoint(
      Offset(centerX + notchDiameter, size.height),
      radius: Radius.circular(notchRadius),
      clockwise: false,
    );
    // 画到右下角
    path.lineTo(size.width, size.height);
    // 画到右上角
    path.lineTo(size.width, 0);
    // 画到左上角
    path.lineTo(0, 0);
    // 闭合路径
    path.close();

    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) =>
      oldClipper is PSBottomBarClipper && oldClipper.notchRadius != notchRadius;
}

如何在底部TabBar中使用

可以通过ClipPath包裹底部导航栏的容器,配合悬浮按钮实现完整的PlayStation风格效果,以下是完整示例:

class PSBottomTabBar extends StatelessWidget {
  final int selectedIndex;
  final ValueChanged<int> onItemTapped;

  const PSBottomTabBar({
    super.key,
    required this.selectedIndex,
    required this.onItemTapped,
  });

  @override
  Widget build(BuildContext context) {
    final barHeight = 60.0;
    final notchRadius = 30.0;

    return ClipPath(
      clipper: PSBottomBarClipper(notchRadius: notchRadius),
      child: Container(
        height: barHeight,
        color: Colors.black,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            _buildTabItem(Icons.home, 0),
            _buildTabItem(Icons.games, 1),
            // 中间留空,适配悬浮按钮位置
            SizedBox(width: notchRadius * 2),
            _buildTabItem(Icons.person, 2),
            _buildTabItem(Icons.settings, 3),
          ],
        ),
      ),
    );
  }

  Widget _buildTabItem(IconData icon, int index) {
    final isSelected = selectedIndex == index;
    return IconButton(
      icon: Icon(
        icon,
        color: isSelected ? Colors.blueAccent : Colors.grey,
        size: 28,
      ),
      onPressed: () => onItemTapped(index),
    );
  }
}

// 在Scaffold中集成使用
class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0;

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("PS风格底部导航")),
      body: const Center(child: Text("页面内容")),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.play_arrow),
        backgroundColor: Colors.blueAccent,
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      bottomNavigationBar: PSBottomTabBar(
        selectedIndex: _selectedIndex,
        onItemTapped: _onItemTapped,
      ),
    );
  }
}

关键说明

  • 修改后的PSBottomBarClipper从底部开始绘制路径,通过arcToPoint创建向上凸起的圆弧,完美匹配PlayStation风格的底部栏形状。
  • 使用ClipPath将容器裁剪为目标形状,内部放置Tab按钮,中间留空位置配合FloatingActionButton实现凸起按钮的效果。
  • 可通过调整notchRadius参数控制凸起大小,适配不同设计需求。

内容的提问来源于stack exchange,提问作者conjurers 22

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:31:19