Flutter 如何实现点击切换凸起/凹陷状态的新拟态按钮设计
实现新拟态按钮点击切换凸起/凹陷效果的方案
核心问题原因
Flutter 原生的BoxShadow仅支持外阴影,没有内置内阴影属性,这是无法直接实现凹陷效果的根本原因。
实现方案
这里提供两种常用实现方式,第一种借助第三方包快速实现,第二种为无第三方依赖的手动实现方案:
方案1:借助inner_shadow第三方包快速实现
- 首先在
pubspec.yaml中添加依赖:
dependencies: flutter: sdk: flutter inner_shadow: ^1.0.0 # 可替换为当前最新版本
- 完整实现代码如下:
import 'package:flutter/material.dart'; import 'package:inner_shadow/inner_shadow.dart'; class NeumorphismButton extends StatefulWidget { const NeumorphismButton({super.key}); @override State<NeumorphismButton> createState() => _NeumorphismButtonState(); } class _NeumorphismButtonState extends State<NeumorphismButton> { final double _offset = 4.0; bool _isPressed = false; @override Widget build(BuildContext context) { return Center( child: GestureDetector( // 处理按压状态切换 onTapDown: (_) => setState(() => _isPressed = true), onTapUp: (_) => setState(() => _isPressed = false), onTapCancel: () => setState(() => _isPressed = false), onTap: () { // 这里添加按钮点击后的业务逻辑 }, // 根据按压状态返回不同样式 child: _isPressed // 凹陷状态:内阴影 ? InnerShadow( shadows: [ BoxShadow( color: Colors.grey.shade500, offset: Offset(_offset, _offset), blurRadius: 8.0, ), BoxShadow( color: Colors.grey.shade100, offset: Offset(_offset * -1, _offset * -1), blurRadius: 8.0, ), ], child: Container( height: 300.0, width: 300.0, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(50.0), ), ), ) // 凸起状态:原有的外阴影 : Container( height: 300.0, width: 300.0, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(50.0), boxShadow: <BoxShadow>[ BoxShadow( color: Colors.grey.shade100, offset: Offset(_offset * -1, _offset * -1), blurRadius: 8.0, ), BoxShadow( color: Colors.grey.shade500, offset: Offset(_offset, _offset), blurRadius: 8.0, ), ], ), ), ), ); } }
方案2:无第三方依赖手动实现
如果不想引入额外包,可以用渐变层模拟内阴影效果,核心逻辑是在凹陷状态下叠加渐变,分别模拟左上暗边和右下亮边:
// 凹陷状态的Container替换为以下代码 Container( height: 300.0, width: 300.0, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(50.0), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.grey.shade400, Colors.grey.shade300, Colors.grey.shade200, ], stops: const [0.1, 0.3, 1], ), ), )
该方案的还原度略低于第三方包,但不需要引入额外依赖,适合简单场景使用。
状态切换逻辑说明
- 用
_isPressed布尔变量存储按钮当前状态 - 通过
GestureDetector的onTapDown、onTapUp、onTapCancel三个回调监听用户按压动作,实时更新状态触发UI重绘 - 可以根据需求调整
_offset、阴影blurRadius、颜色深浅等参数,匹配设计稿效果
内容的提问来源于stack exchange,提问作者pratik97179
相关产品推荐
相关产品推荐

