如何在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
相关产品推荐
相关产品推荐

