请添加图片描述

写在前面

支付成功页是整个支付流程的终点,也是用户情绪的高点。用户完成支付后,需要一个明确的反馈,告诉他们"支付成功了"。这个页面不能只是简单地显示"支付成功"四个字,而是要通过动画、颜色、文字,让用户感受到成功的喜悦。

我在做这个页面时,特别注意了动画的使用。一个弹性的成功图标,能让用户感受到"完成"的满足感。同时,订单信息要清晰展示,让用户知道自己买了什么、花了多少钱。

支付成功页的功能规划

在动手之前,我先想清楚了支付成功页要做什么。作为支付流程的结束页,这个页面的核心是:给用户明确的成功反馈,同时提供后续操作入口

主要功能:

  • 成功图标动画(弹性缩放)
  • 成功提示文字
  • 订单信息展示(订单号、金额、支付方式、时间)
  • 返回首页按钮
  • 查看订单按钮

功能不复杂,但要做得有仪式感,让用户觉得这次支付很值得。

页面结构搭建

页面类的定义

class PaymentSuccessPage extends StatefulWidget {
  const PaymentSuccessPage({super.key});

  
  State<PaymentSuccessPage> createState() => _PaymentSuccessPageState();
}

这是标准的 StatefulWidget,因为页面有动画需要管理。

动画控制器的初始化

class _PaymentSuccessPageState extends State<PaymentSuccessPage> with TickerProviderStateMixin {
  late AnimationController _scaleController;
  late Animation<double> _scaleAnimation;

  
  void initState() {
    super.initState();
    _scaleController = AnimationController(
      duration: const Duration(milliseconds: 600),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(parent: _scaleController, curve: Curves.elasticOut),
    );
    _scaleController.forward();
  }

动画的设计思路:

  • 动画时长600毫秒,不会太快也不会太慢
  • 从0缩放到1,图标从无到有
  • Curves.elasticOut 曲线,产生弹性效果
  • 页面加载时立即播放动画(forward()

为什么用弹性曲线?因为它能产生"弹跳"的效果,让成功图标看起来更有活力,更有"完成"的感觉。

释放动画资源


void dispose() {
  _scaleController.dispose();
  super.dispose();
}

动画控制器用完后必须释放,避免内存泄漏。这是Flutter开发的基本功。

页面布局结构


Widget build(BuildContext context) {
  return Scaffold(
    body: SafeArea(
      child: SingleChildScrollView(
        child: Column(
          children: [
            const SizedBox(height: 40),
            _buildSuccessIcon(),
            const SizedBox(height: 24),
            _buildSuccessText(),
            const SizedBox(height: 40),
            _buildOrderInfo(),
            const SizedBox(height: 24),
            _buildActionButtons(context),
            const SizedBox(height: 40),
          ],
        ),
      ),
    ),
  );
}

布局思路:

  • SafeArea 避免内容被刘海屏遮挡
  • SingleChildScrollView 让内容可以滚动
  • 从上到下依次是:成功图标、成功文字、订单信息、操作按钮
  • SizedBox 控制间距,让页面有呼吸感

顶部和底部都留了40的空间,让页面看起来更舒展。

成功图标动画

图标的实现

Widget _buildSuccessIcon() {
  return ScaleTransition(
    scale: _scaleAnimation,
    child: Container(
      width: 100,
      height: 100,
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          colors: [Color(0xFFFFD700), Color(0xFFFFA500)],
        ),
        shape: BoxShape.circle,
      ),
      child: const Icon(Icons.check, color: Colors.white, size: 60),
    ),
  );
}

图标的设计:

  • ScaleTransition 应用缩放动画
  • 圆形容器,直径100
  • 金色渐变背景,跟App主题一致
  • 白色对勾图标,size: 60,很大很醒目

为什么用圆形?因为圆形代表"完整"、“圆满”,在成功场景下很合适。

动画的效果

_scaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
  CurvedAnimation(parent: _scaleController, curve: Curves.elasticOut),
);

弹性动画的原理:

Curves.elasticOut 会让动画在接近终点时产生"过冲"和"回弹"的效果。就像一个弹簧,拉到最大后会弹回来一点,然后稳定下来。

这种效果能让成功图标看起来更有活力,给用户一种"完成任务"的满足感。

成功提示文字

文字的实现

Widget _buildSuccessText() {
  return Column(
    children: [
      const Text(
        '支付成功',
        style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
      ),
      const SizedBox(height: 8),
      const Text(
        '您的订单已成功提交',
        style: TextStyle(fontSize: 14, color: Color(0xFF999999)),
      ),
    ],
  );
}

文字的层次:

  • 主标题"支付成功"用大字号(28)和粗体,最醒目
  • 副标题"您的订单已成功提交"用小字号(14)和灰色,作为补充说明
  • 两行文字之间用 SizedBox(height: 8) 隔开

为什么要有副标题?因为只说"支付成功"还不够,要告诉用户接下来会发生什么(订单已提交)。

订单信息展示

信息卡片的容器

Widget _buildOrderInfo() {
  return Container(
    margin: const EdgeInsets.symmetric(horizontal: 16),
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.05),
          blurRadius: 8,
        ),
      ],
    ),

订单信息用白色卡片展示,配上很浅的阴影,让它从页面背景中凸显出来。

信息列表

child: Column(
  children: [
    _buildInfoRow('订单号', '2024011512345678'),
    const Divider(height: 16),
    _buildInfoRow('支付金额', '¥11257.00', isAmount: true),
    const Divider(height: 16),
    _buildInfoRow('支付方式', '微信支付'),
    const Divider(height: 16),
    _buildInfoRow('支付时间', '2024-01-15 12:34:56'),
  ],
),

信息的组织:

  • 订单号:让用户可以查询订单
  • 支付金额:最重要的信息,用特殊样式突出
  • 支付方式:告诉用户用的什么方式付款
  • 支付时间:记录支付的时间点

每项信息之间用 Divider 分割,清晰明了。

信息行的封装

Widget _buildInfoRow(String label, String value, {bool isAmount = false}) {
  return Row(
    children: [
      Text(label, style: const TextStyle(color: Color(0xFF999999))),
      const Spacer(),
      Text(
        value,
        style: TextStyle(
          fontWeight: isAmount ? FontWeight.bold : FontWeight.normal,
          color: isAmount ? const Color(0xFFFF6B6B) : const Color(0xFF333333),
          fontSize: isAmount ? 16 : 14,
        ),
      ),
    ],
  );
}

信息行的设计:

  • 左边是标签,用灰色显示
  • 右边是值,用深色显示
  • Spacer() 让标签和值分别靠左右两边
  • 如果是金额(isAmount: true),用粗体、红色、大字号突出显示

这种左右对齐的布局在很多App里都能看到,因为它确实好用。

操作按钮

按钮区域的容器

Widget _buildActionButtons(BuildContext context) {
  return Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16),
    child: Column(
      children: [
        // 返回首页按钮
        // 查看订单按钮
      ],
    ),
  );
}

按钮区域用 Padding 包裹,左右各留16的间距。

返回首页按钮

GestureDetector(
  onTap: () {
    Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
  },
  child: Container(
    width: double.infinity,
    height: 50,
    decoration: BoxDecoration(
      gradient: const LinearGradient(
        colors: [Color(0xFFFFD700), Color(0xFFFFA500)],
      ),
      borderRadius: BorderRadius.circular(25),
    ),
    child: const Center(
      child: Text(
        '返回首页',
        style: TextStyle(
          color: Colors.white,
          fontWeight: FontWeight.bold,
          fontSize: 16,
        ),
      ),
    ),
  ),
),

返回首页按钮的设计:

  • 金色渐变背景,跟App主题一致
  • 圆角25,做成胶囊形状
  • 白色加粗文字,醒目
  • 宽度占满,高度50,方便点击

为什么用 pushNamedAndRemoveUntil?因为要清空导航栈,让用户不能通过返回键回到支付页。第二个参数 (route) => false 表示清空所有路由。

查看订单按钮

const SizedBox(height: 12),
GestureDetector(
  onTap: () {
    Navigator.pushNamed(context, '/my_orders');
  },
  child: Container(
    width: double.infinity,
    height: 50,
    decoration: BoxDecoration(
      border: Border.all(color: const Color(0xFFFFD700)),
      borderRadius: BorderRadius.circular(25),
    ),
    child: const Center(
      child: Text(
        '查看订单',
        style: TextStyle(
          color: Color(0xFFFFD700),
          fontWeight: FontWeight.bold,
          fontSize: 16,
        ),
      ),
    ),
  ),
),

查看订单按钮的设计:

  • 白色背景,金色边框
  • 金色文字,跟边框颜色一致
  • 样式跟返回首页按钮类似,但用边框而不是填充

为什么用边框样式?因为"查看订单"是次要操作,不能比"返回首页"更醒目。用边框样式能形成视觉层次。

一些细节优化

动画时长的选择

duration: const Duration(milliseconds: 600),

600毫秒是我试了很多次后定下来的。太快(比如300毫秒)会显得急躁,太慢(比如1000毫秒)又会让用户等得不耐烦。

600毫秒刚刚好,既能看清动画效果,又不会让用户觉得慢。

间距的统一

const SizedBox(height: 40),  // 顶部和底部
const SizedBox(height: 24),  // 模块之间
const SizedBox(height: 12),  // 按钮之间
const SizedBox(height: 8),   // 文字之间

所有的间距都是8的倍数(8、12、24、40),这样看起来更整齐,也更容易维护。

大的间距用40和24,小的间距用12和8,形成清晰的层次。

颜色的选择

Color(0xFFFFD700)  // 金色
Color(0xFFFFA500)  // 橙色
Color(0xFFFF6B6B)  // 红色
Color(0xFF999999)  // 灰色
Color(0xFF333333)  // 深灰色

这些颜色都是精心挑选的:

  • 金色和橙色用于渐变,跟App主题一致
  • 红色用于金额,吸引注意
  • 灰色用于标签,不抢眼
  • 深灰色用于普通文字,清晰可读

字号的层次

fontSize: 28,  // 主标题
fontSize: 16,  // 按钮文字和金额
fontSize: 14,  // 副标题和普通文字

不同的信息用不同的字号,形成清晰的层次。主标题最大,按钮文字和金额其次,普通文字最小。

踩过的坑

动画不流畅

一开始我用的是 Curves.linear,结果动画看起来很僵硬,没有生气。

解决方案:

改用 Curves.elasticOut,产生弹性效果:

CurvedAnimation(parent: _scaleController, curve: Curves.elasticOut)

弹性曲线让动画看起来更有活力,更符合"成功"的情绪。

返回键导致问题

一开始我用的是 Navigator.pushNamed,结果用户可以通过返回键回到支付页,再次点击支付。

解决方案:

改用 pushNamedAndRemoveUntil,清空导航栈:

Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);

这样用户只能通过"返回首页"或"查看订单"按钮离开,不能回到支付页。

动画资源没释放

测试时没发现问题,但用户反馈说多次进入支付成功页后App会卡。

解决方案:

dispose 方法里释放动画控制器:


void dispose() {
  _scaleController.dispose();
  super.dispose();
}

这是Flutter开发的基本功,创建了动画控制器就必须释放。

订单信息写死

一开始我把订单号、金额、时间都写死了,结果每次支付成功显示的都是同样的信息。

解决方案:

应该从路由参数或全局状态中获取真实的订单信息:

// 实际项目中应该这样
final order = ModalRoute.of(context)!.settings.arguments as Order;
_buildInfoRow('订单号', order.id),
_buildInfoRow('支付金额', ${order.amount}', isAmount: true),

这里为了演示简化了,实际项目中必须用真实数据。

写在最后

支付成功页是整个支付流程的终点,也是用户情绪的高点。这个页面不能只是简单地告诉用户"支付成功了",而是要通过动画、颜色、文字,让用户感受到成功的喜悦。

我的设计原则:

  1. 动画要有仪式感。弹性的成功图标能让用户感受到"完成"的满足感,这种情绪上的反馈很重要。

  2. 信息要清晰完整。订单号、金额、支付方式、时间,这些信息都要展示,让用户心里有数。

  3. 操作要明确。提供两个按钮:返回首页和查看订单,让用户知道接下来可以做什么。

  4. 细节要到位。动画时长、间距、颜色、字号,这些细节都要精心调整,才能达到最好的效果。

做完支付成功页后,我拿给几个朋友试用。有人说"动画很有意思,有种完成任务的感觉",有人说"信息很清楚,一眼就能看到订单号和金额"。这些反馈让我觉得,那些细节的打磨是值得的。

一些经验:

  • 动画不是越多越好,一个恰到好处的动画就够了。成功图标的弹性动画已经足够表达"成功"的情绪。

  • 颜色要克制。虽然是成功页,但不能用太多鲜艳的颜色。金色渐变配白色文字,简洁又醒目。

  • 按钮的层次要清晰。主要操作用填充样式,次要操作用边框样式,这样用户不会搞混。

下一步计划:

支付成功页做完了,接下来要做订单管理页。那个页面会展示用户的所有订单,支持筛选和查看详情。不过有了前面的基础,应该会更容易。

如果你也在做类似的项目,希望这篇文章能给你一些启发。支付成功页看起来简单,但要做出仪式感,让用户感受到成功的喜悦,还是需要在动画和细节上下功夫的。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐