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

如何在Flutter中实现侧边半圆且自适应的图片自定义按钮?

解决方案

要实现满足所有需求的自定义按钮,核心思路是避免整图拉伸,将按钮背景拆分为左半圆、中间可拉伸区域、右半圆三个独立部分组合布局,同时让内容覆盖整个按钮区域。以下是两种可行方案:

方案一:手动拆分图片实现(无第三方依赖)

1. 准备拆分资源

将原按钮背景图拆成3张独立图片:

  • 左侧半圆:仅保留原图片的左半圆部分,高度与原图一致
  • 中间拉伸条:仅保留原图片的中间矩形区域,宽度可设为1px(保证仅横向拉伸)
  • 右侧半圆:仅保留原图片的右半圆部分,高度与原图一致

2. 重写按钮Widget

使用Stack让文本覆盖在背景之上,通过IntrinsicHeight确保背景三部分高度与内容匹配,中间区域用Expanded实现宽度自适应:

import 'package:flutter/material.dart';

class MyButton extends StatelessWidget {
  final String text;
  final double fontSize;
  final VoidCallback? onTap;

  const MyButton({
    Key? key,
    required this.text,
    required this.fontSize,
    this.onTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap ?? () {},
      child: Stack(
        alignment: Alignment.center,
        children: [
          // 按钮背景组合
          IntrinsicHeight(
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                // 左半圆,保持原尺寸
                Image.asset('assets/images/btn-left.png'),
                // 中间可拉伸区域
                Expanded(
                  child: Image.asset(
                    'assets/images/btn-middle.png',
                    fit: BoxFit.fill,
                  ),
                ),
                // 右半圆,保持原尺寸
                Image.asset('assets/images/btn-right.png'),
              ],
            ),
          ),
          // 按钮内容,覆盖整个按钮
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
            child: Text(
              text,
              style: TextStyle(fontSize: fontSize, color: Colors.white),
              textAlign: TextAlign.center,
            ),
          ),
        ],
      ),
    );
  }
}

3. 使用示例

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: const [
    MyButton(text: 'Play', fontSize: 32.0),
    SizedBox(height: 16),
    MyButton(text: 'Settings', fontSize: 32.0),
    SizedBox(height: 16),
    MyButton(text: 'Very very very very very very long text', fontSize: 20.0),
    SizedBox(height: 16),
    MyButton(text: 'Small', fontSize: 12.0),
  ],
)

方案二:第三方9切片工具实现(无需拆分图片)

使用nine_box包实现9切片拉伸效果,无需手动拆分图片:

1. 添加依赖

在pubspec.yaml中添加:

dependencies:
  nine_box: ^1.0.0

2. 实现按钮Widget

import 'package:flutter/material.dart';
import 'package:nine_box/nine_box.dart';

class MyButton extends StatelessWidget {
  final String text;
  final double fontSize;
  final VoidCallback? onTap;

  const MyButton({
    Key? key,
    required this.text,
    required this.fontSize,
    this.onTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap ?? () {},
      child: NineBox(
        image: const AssetImage('assets/images/my-button.png'),
        // 根据原图片尺寸设置切片区域:左右半圆半径等于图片高度的一半
        // 假设原图片高度为80,则left/right设为40,top/bottom设为0
        slice: const NineBoxSlice(left: 40, top: 0, right: 40, bottom: 0),
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
          child: Text(
            text,
            style: TextStyle(fontSize: fontSize, color: Colors.white),
            textAlign: TextAlign.center,
          ),
        ),
      ),
    );
  }
}

关键说明

  • 两种方案都能保证侧边半圆不被拉伸,仅中间区域适配内容宽度
  • IntrinsicHeight/NineBox会自动匹配内容高度,满足按钮自适应需求
  • Stack布局确保文本内容可以覆盖半圆区域

内容的提问来源于stack exchange,提问作者Scooby-Doo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:55:31