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

Flutter实现带自定义曲线与选中态红色边框的自定义Tab问题求助

Flutter实现带自定义曲线与选中态红色边框的自定义Tab问题求助

我最近在尝试用Flutter复刻参考图里的Tab设计,这个设计的核心是Tab要有自定义的曲线形状,还有选中Tab时的红色边框效果(就像图里展示的那样)。

我已经写了一段代码尝试实现这个效果,但目前还没做出想要的曲线样式,想请各位帮忙看看问题出在哪,或者给点实现思路的指导,谢谢啦!

我当前的实现代码

import 'package:flutter/material.dart';

class TabShapePainter extends CustomPainter {
  final Color tabColor;
  final bool isSelected;
  final bool isFirst;
  final bool isLast;
  final double slantOffset;

  TabShapePainter({
    required this.tabColor,
    required this.isSelected,
    required this.isFirst,
    required this.isLast,
    required this.slantOffset,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = tabColor
      ..style = PaintingStyle.fill;

    final Path path = Path();
    final double borderRadius = 10.0;

    if (isFirst) {
      path.moveTo(borderRadius, 0);
      path.quadraticBezierTo(0, 0, 0, borderRadius);
      path.lineTo(0, size.height);
    } else {
      path.moveTo(0, 0);
      path.lineTo(slantOffset, size.height);
    }

    path.lineTo(size.width, size.height);
    path.lineTo(size.width - slantOffset, 0);
    path.close();

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant TabShapePainter oldDelegate) {
    return oldDelegate.tabColor != tabColor ||
        oldDelegate.isSelected != isSelected ||
        oldDelegate.isFirst != isFirst ||
        oldDelegate.isLast != isLast ||
        oldDelegate.slantOffset != slantOffset;
  }
}

class CustomTabItem extends StatelessWidget {
  final String text;
  final Color tabColor;
  final bool isSelected;
  final VoidCallback onTap;
  final bool isFirst;
  final bool isLast;
  final double tabWidth;
  final double slantOffset;

  CustomTabItem({
    required this.text,
    required this.onTap,
    this.tabColor = Colors.blue,
    this.isSelected = false,
    this.isFirst = false,
    this.isLast = false,
    required this.tabWidth,
    required this.slantOffset,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        width: tabWidth,
        height: 55,
        child: Stack(
          children: [
            Positioned.fill(
              child: CustomPaint(
                painter: TabShapePainter(
                  tabColor: isSelected ? tabColor : tabColor.withOpacity(0.7),
                  isSelected: isSelected,
                  isFirst: isFirst,
                  isLast: isLast,
                  slantOffset: slantOffset,
                ),
              ),
            ),
            Center(
              child: Padding(
                padding: EdgeInsets.only(
                  left: isFirst ? 0 : slantOffset * 0.5,
                  right: isLast ? 0 : slantOffset * 0.5,
                ),
                child: Text(
                  text,
                  style: TextStyle(
                    color: isSelected ? Colors.white : Colors.white70,
                    fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
                    fontSize: 16,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class CustomTabsScreen extends StatefulWidget {
  @override
  _CustomTabsScreenState createState() => _CustomTabsScreenState();
}

class _CustomTabsScreenState extends State<CustomTabsScreen> {
  int _selectedIndex = 0;
  final int _totalTabs = 3;
  final double _tabSlantOffset = 15.0;
  final double _baseTabWidth = 120.0;

  final List<Widget> _tabContents = [
    Center(child: Text('محتوى التبويب الأول', style: TextStyle(fontSize: 24, color: Colors.deepPurple))),
    Center(child: Text('محتوى التبويب الثاني هنا', style: TextStyle(fontSize: 24, color: Colors.teal))),
    Center(child: Text('التبويب الثالث ومحتواه', style: TextStyle(fontSize: 24, color: Colors.orange))),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('تبويبات متلاصقة ومائلة'),
        backgroundColor: Colors.blueGrey,
      ),
      body: Column(
        children: [
          Container(
            height: 60,
            color: Colors.grey[200],
            child: Row(
              children: List.generate(_totalTabs, (index) {
                double offsetX = 0.0;
                if (index > 0) {
                  offsetX = -(_tabSlantOffset * index);
                }
                return Transform.translate(
                  offset: Offset(offsetX, 0),
                  child: CustomTabItem(
                    text: 'تبويب ${index + 1}',
                    tabColor: [Colors.deepPurple, Colors.teal, Colors.orange][index],
                    isSelected: _selectedIndex == index,
                    onTap: () {
                      setState(() {
                        _selectedIndex = index;
                      });
                    },
                    isFirst: index == 0,
                    isLast: index == _totalTabs - 1,
                    tabWidth: _baseTabWidth,
                    slantOffset: _tabSlantOffset,
                  ),
                );
              }),
            ),
          ),
          Expanded(
            child: _tabContents[_selectedIndex],
          ),
        ],
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(home: CustomTabsScreen()));
}

目前我的代码还没能实现参考图里的自定义曲线效果,希望能得到大家的建议或者指导,帮我达成想要的Tab样式~


内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 12:20:28