Flutter for openharmony盲盒抽奖App应用实战+支付功能实现
支付功能是整个App最核心的环节之一。用户选好商品、确认订单后,就到了支付这一步。这个页面看起来简单,就是显示金额、选择支付方式、点击确认,但实际上要考虑的细节很多。支付金额的展示、支付方式的选择、支付按钮的状态,每个环节都要做到位。我在做这个页面时,特别注意了视觉的聚焦。用户进来后,第一眼应该看到的是支付金额,其次是支付方式,最后是确认按钮。整个页面的信息流要顺畅,不能让用户思考。// 0: 微

写在前面
支付功能是整个App最核心的环节之一。用户选好商品、确认订单后,就到了支付这一步。这个页面看起来简单,就是显示金额、选择支付方式、点击确认,但实际上要考虑的细节很多。支付金额的展示、支付方式的选择、支付按钮的状态,每个环节都要做到位。
我在做这个页面时,特别注意了视觉的聚焦。用户进来后,第一眼应该看到的是支付金额,其次是支付方式,最后是确认按钮。整个页面的信息流要顺畅,不能让用户思考。
支付页的功能规划
在动手之前,我先想清楚了支付页要做什么。作为支付流程的最后一步,这个页面的核心是:让用户清楚地知道要付多少钱,用什么方式付。
主要功能:
- 显示实付金额(大字号,醒目)
- 支付方式选择(微信、支付宝)
- 确认支付按钮
- 支付成功后跳转到结果页
功能不多,但每个都要做到极致。特别是支付金额的展示,必须让用户一眼就能看到。
页面参数设计
接收支付金额
class PaymentPage extends StatefulWidget {
final double amount;
const PaymentPage({super.key, required this.amount});
State<PaymentPage> createState() => _PaymentPageState();
}
为什么金额是必传参数?
因为支付页必须知道要付多少钱。这个金额从订单确认页传过来,不能在支付页里计算,避免数据不一致。
用
required标记,确保调用时必须传入金额。
状态变量定义
class _PaymentPageState extends State<PaymentPage> {
int _selectedPaymentMethod = 0; // 0: 微信支付, 1: 支付宝
用一个整数表示选中的支付方式,0是微信,1是支付宝。这样比用字符串更简洁,也更容易扩展。
如果以后要加更多支付方式,只需要增加数字就行了。
页面结构搭建
顶部导航栏
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppColors.black, size: 20),
onPressed: () => Navigator.pop(context),
),
title: const Text(
'收银台',
style: TextStyle(color: AppColors.black, fontSize: 18),
),
centerTitle: true,
),
导航栏的设计:
- 标题用"收银台"而不是"支付",更有仪式感
- 白色背景,
elevation: 0去掉阴影,简洁- 返回按钮用iOS风格的箭头,更现代
整个导航栏很简洁,不会分散用户注意力。
页面布局结构
body: Column(
children: [
// 支付金额
_buildAmountSection(),
const SizedBox(height: 12),
// 支付方式选择
_buildPaymentMethods(),
const Spacer(),
// 确认支付按钮
_buildConfirmButton(),
],
),
布局思路:
- 顶部显示支付金额
- 中间显示支付方式选择
- 用
Spacer()把按钮推到底部- 底部是确认支付按钮
这种布局让页面看起来很舒展,不会挤在一起。
支付金额展示
金额区域的容器
Widget _buildAmountSection() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 30),
child: Column(
children: [
const Text(
'实付金额',
style: TextStyle(
fontSize: 14,
color: AppColors.grey,
),
),
金额区域用
padding: EdgeInsets.symmetric(vertical: 30)留出足够的空间,让金额有呼吸感。"实付金额"用小字号和灰色,作为标签,不抢眼。
金额数字的展示
const SizedBox(height: 12),
Text(
'¥ ${widget.amount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 36,
color: AppColors.black,
fontWeight: FontWeight.bold,
),
),
金额展示的设计:
- 字号36,非常大,用户一眼就能看到
- 黑色加粗,醒目
- 用
toStringAsFixed(2)保留两位小数,显示完整的金额为什么不用红色?因为红色在支付场景下可能让用户紧张。黑色更中性,更专业。
支付方式选择
支付方式区域的容器
Widget _buildPaymentMethods() {
return Container(
color: const Color(0xFFF5F5F5),
child: Container(
margin: const EdgeInsets.only(top: 12),
color: Colors.white,
用一个浅灰色背景(
0xFFF5F5F5)包裹,里面是白色的支付方式列表。这样能形成视觉层次,让支付方式区域更突出。
支付方式的标题
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text(
'选择支付方式',
style: TextStyle(
fontSize: 14,
color: AppColors.grey,
),
),
),
标题用小字号和灰色,作为提示文字,不会太抢眼。
微信支付选项
// 微信支付
_buildPaymentOption(
index: 0,
icon: _buildWechatIcon(),
label: '微信支付',
),
const Divider(height: 1, indent: 56),
微信支付是第一个选项,索引为0。两个选项之间用
Divider分割,indent: 56让分割线从图标后面开始,更精致。
支付宝选项
// 支付宝
_buildPaymentOption(
index: 1,
icon: _buildAlipayIcon(),
label: '支付宝',
),
支付宝是第二个选项,索引为1。
支付选项的实现
Widget _buildPaymentOption({
required int index,
required Widget icon,
required String label,
}) {
final bool isSelected = _selectedPaymentMethod == index;
return GestureDetector(
onTap: () => setState(() => _selectedPaymentMethod = index),
behavior: HitTestBehavior.opaque,
选项的交互:
点击选项时,调用
setState更新_selectedPaymentMethod,触发重新构建。
behavior: HitTestBehavior.opaque让整个选项区域都可以点击,不只是文字和图标。这样用户点击空白处也能选中,体验更好。
选项的布局
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Row(
children: [
icon,
const SizedBox(width: 12),
Expanded(
child: Text(
label,
style: const TextStyle(
fontSize: 15,
color: AppColors.black,
),
),
),
选项的布局:
- 左边是支付方式的图标
- 中间是支付方式的名称,用
Expanded占满剩余空间- 右边是单选按钮
上下padding是14,左右padding是16,让选项有足够的点击区域。
单选按钮的实现
Container(
width: 22,
height: 22,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected ? const Color(0xFF07C160) : Colors.transparent,
border: Border.all(
color: isSelected ? const Color(0xFF07C160) : AppColors.grey,
width: 1.5,
),
),
child: isSelected
? const Icon(Icons.check, color: Colors.white, size: 14)
: null,
),
单选按钮的状态:
- 选中时:绿色背景(
0xFF07C160,微信的绿色),白色对勾- 未选中时:透明背景,灰色边框
为什么用绿色?因为绿色代表"确认"、“通过”,在支付场景下能给用户信心。
支付方式图标
微信图标
Widget _buildWechatIcon() {
return Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: const Color(0xFF07C160),
borderRadius: BorderRadius.circular(4),
),
child: const Icon(
Icons.chat_bubble,
color: Colors.white,
size: 18,
),
);
}
微信图标的设计:
- 用微信的品牌色(
0xFF07C160)作为背景- 用聊天气泡图标代表微信
- 圆角4,不是完全的圆形,更有设计感
虽然不是微信的官方图标,但用户一看到绿色就能联想到微信。
支付宝图标
Widget _buildAlipayIcon() {
return Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: const Color(0xFF1677FF),
borderRadius: BorderRadius.circular(4),
),
child: const Center(
child: Text(
'支',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
);
}
支付宝图标的设计:
- 用支付宝的品牌色(
0xFF1677FF)作为背景- 用"支"字代表支付宝
- 白色加粗文字,醒目
这种简化的图标设计在很多App里都能看到,简洁又有辨识度。
确认支付按钮
按钮的容器
Widget _buildConfirmButton() {
return Container(
padding: const EdgeInsets.all(16),
child: SafeArea(
用
SafeArea包裹,确保按钮不会被底部的Home指示器遮挡。padding是16,给按钮留出足够的空间。
按钮的样式
child: GestureDetector(
onTap: _confirmPayment,
child: Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(25),
),
child: const Center(
child: Text(
'确认支付',
style: TextStyle(
fontSize: 16,
color: AppColors.black,
fontWeight: FontWeight.bold,
),
),
),
),
),
按钮的设计:
- 宽度占满(
width: double.infinity)- 高度50,足够大,方便点击
- 金色背景,跟App主题一致
- 圆角25,做成胶囊形状
- 文字黑色加粗,醒目
为什么文字用黑色而不是白色?因为金色背景配黑色文字对比度更高,更容易看清。
支付确认逻辑
void _confirmPayment() {
// 跳转到开箱结果页面
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => BoxResultPage(price: widget.amount),
),
);
}
支付确认的流程:
点击按钮后,直接跳转到开箱结果页。用
pushReplacement而不是push,这样用户不能通过返回键回到支付页。实际项目中,这里应该先调用支付接口,等支付成功后再跳转。如果支付失败,应该显示错误提示。
一些细节优化
颜色的选择
const Color(0xFF07C160) // 微信绿
const Color(0xFF1677FF) // 支付宝蓝
const Color(0xFFF5F5F5) // 浅灰色背景
这些颜色都是精心挑选的:
- 微信绿和支付宝蓝是它们的品牌色,用户一看就知道
- 浅灰色背景能形成层次,但不会太抢眼
颜色的选择很重要,用对了能提升用户体验。
间距的统一
padding: const EdgeInsets.symmetric(vertical: 30), // 金额区域
padding: const EdgeInsets.all(16), // 标题和按钮
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), // 选项
所有的间距都是偶数,而且是4的倍数(12、14、16、30)。这样看起来更整齐,也更容易维护。
字号的层次
fontSize: 36, // 支付金额
fontSize: 18, // 页面标题
fontSize: 16, // 按钮文字
fontSize: 15, // 支付方式名称
fontSize: 14, // 提示文字
不同的信息用不同的字号,形成清晰的层次:
- 支付金额最大(36),最重要
- 页面标题其次(18)
- 按钮文字和支付方式名称适中(15-16)
- 提示文字最小(14),不抢眼
踩过的坑
支付方式选择不生效
一开始我忘了在点击时调用 setState,结果点击支付方式后界面没有更新。
解决方案:
在
onTap里调用setState:onTap: () => setState(() => _selectedPaymentMethod = index),这是Flutter的基本功,状态变化后必须调用
setState才会重新构建。
点击区域太小
一开始我只给图标和文字加了点击事件,结果用户点击空白处没反应。
解决方案:
给整个选项加上
GestureDetector,并设置behavior: HitTestBehavior.opaque:GestureDetector( onTap: () => setState(() => _selectedPaymentMethod = index), behavior: HitTestBehavior.opaque, child: Padding(...), )这样整个选项区域都可以点击,用户体验更好。
按钮被底部遮挡
在有Home指示器的手机上(比如iPhone X及以后的机型),按钮会被底部的指示器遮挡。
解决方案:
用
SafeArea包裹按钮:Container( padding: const EdgeInsets.all(16), child: SafeArea( child: GestureDetector(...), ), )
SafeArea会自动避开系统UI,确保按钮完全可见。
金额显示不完整
一开始我直接用 widget.amount.toString(),结果有时候显示 59.9,有时候显示 59.0,不统一。
解决方案:
用
toStringAsFixed(2)固定显示两位小数:Text('¥ ${widget.amount.toStringAsFixed(2)}')这样无论金额是多少,都会显示两位小数,看起来更专业。
写在最后
支付页是整个支付流程的最后一步,必须做到简洁、清晰、可靠。用户在这个页面停留的时间不会太长,但如果体验不好,很容易导致用户放弃支付。
我的设计原则:
-
信息聚焦。支付金额用超大字号,用户一眼就能看到。其他信息都是辅助,不能抢眼。
-
操作简单。只需要选择支付方式,点击确认,两步完成。不要加太多复杂的功能。
-
视觉舒适。用品牌色表示支付方式,用绿色表示确认,这些都是用户熟悉的视觉语言。
-
细节到位。点击区域要大,按钮要避开系统UI,金额要显示完整,这些细节都要考虑到。
做完支付页后,我拿给几个朋友试用。有人说"很简洁,一眼就知道怎么操作",有人说"支付金额很醒目,不会看错"。这些反馈让我觉得,简单就是最好的设计。
下一步计划:
支付页做完了,接下来要做支付成功页。那个页面会有一些动画效果,让用户感受到支付成功的喜悦。不过有了前面的基础,应该会更容易。
如果你也在做类似的项目,希望这篇文章能给你一些启发。支付功能看起来简单,但每个细节都要考虑周全,才能给用户最好的体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)