Flutter for OpenHarmony 商城App实战 - 支付实现

支付是电商应用的最后一步,用户在这里输入支付信息、确认订单、完成交易。一个好的支付实现需要提供安全的支付方式、清晰的支付流程和友好的支付反馈。本文将详细讲解如何在 Flutter for OpenHarmony 项目中实现一个功能完整的支付系统,包括支付方式选择、支付信息输入、支付处理和支付结果反馈等功能。

用户数据模型

用户信息是支付的基础。User 类包含了用户的个人信息和账户信息。

dart 复制代码
class User {
  const User({
    required this.id,                    // 用户ID
    required this.email,                 // 邮箱
    required this.nickname,              // 昵称
    this.avatar,                         // 头像URL
    this.phone,                          // 电话
    this.memberLevel = 'Basic',          // 会员等级
    this.points = 0,                     // 积分
  });

  final String id;
  final String email;
  final String nickname;
  final String? avatar;
  final String? phone;
  final String memberLevel;
  final int points;

  // 复制并修改用户信息
  User copyWith({
    String? id,
    String? email,
    String? nickname,
    String? avatar,
    String? phone,
    String? memberLevel,
    int? points,
  }) {
    return User(
      id: id ?? this.id,
      email: email ?? this.email,
      nickname: nickname ?? this.nickname,
      avatar: avatar ?? this.avatar,
      phone: phone ?? this.phone,
      memberLevel: memberLevel ?? this.memberLevel,
      points: points ?? this.points,
    );
  }
}

这个用户模型展示了如何管理用户信息:

用户信息:

  • 包含基本信息(ID、邮箱、昵称)
  • 包含联系方式(头像、电话)
  • 包含账户信息(会员等级、积分)

不可变性:

  • 使用 const 构造函数确保不可变性
  • copyWith 方法用于创建修改后的副本
  • 支持函数式编程风格

应用状态管理

AppState 是应用的全局状态管理类,包含用户、订单、优惠券等信息。

dart 复制代码
class AppState extends ChangeNotifier {
  // 用户状态
  User? _currentUser;
  User? get currentUser => _currentUser;
  bool get isLoggedIn => _currentUser != null;

  // 订单列表
  final List<Order> _orders = [];
  List<Order> get orders => List.unmodifiable(_orders);

  // 优惠券列表
  final List<Coupon> _coupons = [];
  List<Coupon> get coupons => List.unmodifiable(_coupons);
  List<Coupon> get validCoupons => _coupons.where((c) => c.isValid).toList();

  // 积分管理方法
  void addPoints(int amount) {
    if (_currentUser == null) return;
    _currentUser = _currentUser!.copyWith(
      points: _currentUser!.points + amount,
    );
    notifyListeners();
  }

  void usePoints(int amount) {
    if (_currentUser == null) return;
    if (_currentUser!.points >= amount) {
      _currentUser = _currentUser!.copyWith(
        points: _currentUser!.points - amount,
      );
      notifyListeners();
    }
  }

  // 订单管理方法
  void addOrder(Order order) {
    _orders.insert(0, order);
    notifyListeners();
  }

  // 登录方法
  Future<bool> login(String email, String password) async {
    await Future.delayed(const Duration(milliseconds: 800));
    if (email.isNotEmpty && password.length >= 6) {
      _currentUser = User(
        id: 'user_001',
        email: email,
        nickname: email.split('@').first,
        memberLevel: 'Gold',
        points: 2580,
      );
      notifyListeners();
      return true;
    }
    return false;
  }

  // 登出方法
  void logout() {
    _currentUser = null;
    notifyListeners();
  }
}

这个应用状态展示了如何管理全局状态:

状态管理:

  • 使用 ChangeNotifier 实现状态通知
  • 维护用户、订单、优惠券等全局数据
  • 提供修改状态的方法

积分管理:

  • addPoints 增加用户积分
  • usePoints 使用用户积分
  • 检查用户是否有足够的积分

订单管理:

  • 维护订单列表
  • 新订单插入到列表开头
  • 通知所有监听者状态改变

支付方式选择

支付前需要让用户选择支付方式。

dart 复制代码
// 支付方式枚举
enum PaymentMethod {
  creditCard,    // 信用卡
  debitCard,     // 借记卡
  wallet,        // 钱包
  bankTransfer,  // 银行转账
}

// 支付方式选择UI
class PaymentMethodSelector extends StatefulWidget {
  const PaymentMethodSelector({
    required this.onMethodSelected,
  });

  final Function(PaymentMethod) onMethodSelected;

  @override
  State<PaymentMethodSelector> createState() => _PaymentMethodSelectorState();
}

class _PaymentMethodSelectorState extends State<PaymentMethodSelector> {
  PaymentMethod? _selectedMethod;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 信用卡选项
        _buildMethodOption(
          method: PaymentMethod.creditCard,
          title: '信用卡',
          icon: Icons.credit_card,
          description: '使用信用卡支付',
        ),
        // 借记卡选项
        _buildMethodOption(
          method: PaymentMethod.debitCard,
          title: '借记卡',
          icon: Icons.credit_card,
          description: '使用借记卡支付',
        ),
        // 钱包选项
        _buildMethodOption(
          method: PaymentMethod.wallet,
          title: '钱包',
          icon: Icons.account_balance_wallet,
          description: '使用账户余额支付',
        ),
        // 银行转账选项
        _buildMethodOption(
          method: PaymentMethod.bankTransfer,
          title: '银行转账',
          icon: Icons.account_balance,
          description: '通过银行转账支付',
        ),
      ],
    );
  }

  Widget _buildMethodOption({
    required PaymentMethod method,
    required String title,
    required IconData icon,
    required String description,
  }) {
    final isSelected = _selectedMethod == method;
    
    return Card(
      child: ListTile(
        // 支付方式图标
        leading: Icon(icon),
        // 支付方式标题和描述
        title: Text(title),
        subtitle: Text(description),
        // 选择状态
        trailing: Radio<PaymentMethod>(
          value: method,
          groupValue: _selectedMethod,
          onChanged: (value) {
            setState(() {
              _selectedMethod = value;
              widget.onMethodSelected(value!);
            });
          },
        ),
        // 点击卡片也可以选择
        onTap: () {
          setState(() {
            _selectedMethod = method;
            widget.onMethodSelected(method);
          });
        },
      ),
    );
  }
}

这个支付方式选择展示了如何让用户选择支付方式:

支付方式:

  • 信用卡和借记卡支付
  • 钱包余额支付
  • 银行转账支付

UI设计:

  • 使用卡片展示每个支付方式
  • 显示支付方式的图标和描述
  • 使用单选按钮表示选择状态

支付信息输入

用户需要输入支付信息来完成支付。

dart 复制代码
// 支付信息表单
class PaymentForm extends StatefulWidget {
  const PaymentForm({
    required this.onSubmit,
  });

  final Function(PaymentInfo) onSubmit;

  @override
  State<PaymentForm> createState() => _PaymentFormState();
}

class _PaymentFormState extends State<PaymentForm> {
  // 表单控制器
  final _cardNumberController = TextEditingController();
  final _cardHolderController = TextEditingController();
  final _expiryController = TextEditingController();
  final _cvvController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 卡号输入
        TextField(
          controller: _cardNumberController,
          decoration: InputDecoration(
            labelText: '卡号',
            hintText: '1234 5678 9012 3456',
            prefixIcon: const Icon(Icons.credit_card),
          ),
          // 限制输入为数字,并自动添加空格
          inputFormatters: [
            FilteringTextInputFormatter.digitsOnly,
            CardNumberFormatter(),
          ],
          keyboardType: TextInputType.number,
        ),
        const SizedBox(height: 16),
        
        // 持卡人名字
        TextField(
          controller: _cardHolderController,
          decoration: InputDecoration(
            labelText: '持卡人名字',
            hintText: 'JOHN DOE',
            prefixIcon: const Icon(Icons.person),
          ),
          inputFormatters: [
            FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\s]')),
          ],
        ),
        const SizedBox(height: 16),
        
        // 有效期和CVV
        Row(
          children: [
            Expanded(
              child: TextField(
                controller: _expiryController,
                decoration: InputDecoration(
                  labelText: '有效期',
                  hintText: 'MM/YY',
                ),
                // 限制输入格式为MM/YY
                inputFormatters: [
                  FilteringTextInputFormatter.digitsOnly,
                  ExpiryDateFormatter(),
                ],
                keyboardType: TextInputType.number,
              ),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: TextField(
                controller: _cvvController,
                decoration: InputDecoration(
                  labelText: 'CVV',
                  hintText: '123',
                ),
                // 限制输入为3位数字
                inputFormatters: [
                  FilteringTextInputFormatter.digitsOnly,
                  LengthLimitingTextInputFormatter(3),
                ],
                keyboardType: TextInputType.number,
                obscureText: true,
              ),
            ),
          ],
        ),
        const SizedBox(height: 24),
        
        // 提交按钮
        ElevatedButton(
          onPressed: _submitForm,
          child: const Text('确认支付'),
        ),
      ],
    );
  }

  void _submitForm() {
    final paymentInfo = PaymentInfo(
      cardNumber: _cardNumberController.text,
      cardHolder: _cardHolderController.text,
      expiry: _expiryController.text,
      cvv: _cvvController.text,
    );
    widget.onSubmit(paymentInfo);
  }

  @override
  void dispose() {
    _cardNumberController.dispose();
    _cardHolderController.dispose();
    _expiryController.dispose();
    _cvvController.dispose();
    super.dispose();
  }
}

// 卡号格式化器
class CardNumberFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    final text = newValue.text;
    if (text.isEmpty) return newValue;

    // 每4位添加一个空格
    final buffer = StringBuffer();
    for (int i = 0; i < text.length; i++) {
      buffer.write(text[i]);
      if ((i + 1) % 4 == 0 && i + 1 != text.length) {
        buffer.write(' ');
      }
    }

    return TextEditingValue(
      text: buffer.toString(),
      selection: TextSelection.collapsed(offset: buffer.length),
    );
  }
}

// 有效期格式化器
class ExpiryDateFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    final text = newValue.text;
    if (text.isEmpty) return newValue;

    // 格式化为MM/YY
    if (text.length >= 2) {
      final month = text.substring(0, 2);
      final year = text.length > 2 ? text.substring(2) : '';
      return TextEditingValue(
        text: year.isEmpty ? month : '$month/$year',
        selection: TextSelection.collapsed(
          offset: year.isEmpty ? month.length : '$month/$year'.length,
        ),
      );
    }

    return newValue;
  }
}

// 支付信息类
class PaymentInfo {
  const PaymentInfo({
    required this.cardNumber,
    required this.cardHolder,
    required this.expiry,
    required this.cvv,
  });

  final String cardNumber;
  final String cardHolder;
  final String expiry;
  final String cvv;
}

这个支付信息输入展示了如何安全地收集支付信息:

输入字段:

  • 卡号:自动格式化为 1234 5678 9012 3456 格式
  • 持卡人名字:只允许字母和空格
  • 有效期:自动格式化为 MM/YY 格式
  • CVV:只允许3位数字,并隐藏显示

格式化器:

  • CardNumberFormatter 每4位添加空格
  • ExpiryDateFormatter 自动添加斜杠
  • FilteringTextInputFormatter 限制输入类型

安全性:

  • CVV 字段使用 obscureText: true 隐藏
  • 限制输入长度和类型
  • 防止用户输入无效数据

支付处理

支付需要进行异步处理,并显示加载状态。

dart 复制代码
// 支付处理方法
Future<bool> processPayment(
  PaymentInfo paymentInfo,
  double amount,
  AppState appState,
) async {
  try {
    // 显示加载对话框
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (context) => const AlertDialog(
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 16),
            Text('正在处理支付...'),
          ],
        ),
      ),
    );

    // 模拟支付处理(实际应该调用支付API)
    await Future.delayed(const Duration(seconds: 2));

    // 关闭加载对话框
    if (mounted) Navigator.pop(context);

    // 支付成功
    if (mounted) {
      // 创建订单
      final order = Order(
        id: 'order_${DateTime.now().millisecondsSinceEpoch}',
        items: [],
        status: OrderStatus.paid,
        createdAt: DateTime.now(),
        totalUsd: amount,
        paidAt: DateTime.now(),
      );

      // 添加订单到应用状态
      appState.addOrder(order);

      // 显示成功提示
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('支付成功!')),
      );

      return true;
    }

    return false;
  } catch (e) {
    // 关闭加载对话框
    if (mounted) Navigator.pop(context);

    // 显示错误提示
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('支付失败:$e')),
      );
    }

    return false;
  }
}

这个支付处理展示了如何处理支付流程:

加载状态:

  • 显示加载对话框
  • 禁止用户关闭对话框
  • 显示加载提示

支付处理:

  • 模拟支付处理(实际应该调用支付API)
  • 创建订单对象
  • 添加订单到应用状态

错误处理:

  • 使用 try-catch 捕获异常
  • 显示错误提示
  • 关闭加载对话框

支付结果反馈

支付完成后需要显示结果反馈。

dart 复制代码
// 支付结果页面
class PaymentResultPage extends StatelessWidget {
  const PaymentResultPage({
    required this.success,
    required this.orderId,
    required this.amount,
  });

  final bool success;
  final String orderId;
  final double amount;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('支付结果'),
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 结果图标
            Icon(
              success ? Icons.check_circle : Icons.error,
              size: 80,
              color: success ? Colors.green : Colors.red,
            ),
            const SizedBox(height: 24),
            
            // 结果文本
            Text(
              success ? '支付成功' : '支付失败',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            const SizedBox(height: 16),
            
            // 订单信息
            if (success) ...[
              Text('订单号:$orderId'),
              const SizedBox(height: 8),
              Text('金额:¥${amount.toStringAsFixed(2)}'),
            ],
            const SizedBox(height: 32),
            
            // 操作按钮
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: const Text('返回'),
                ),
                const SizedBox(width: 16),
                if (success)
                  ElevatedButton(
                    onPressed: () {
                      // 查看订单详情
                    },
                    child: const Text('查看订单'),
                  ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

这个支付结果反馈展示了如何显示支付结果:

成功状态:

  • 显示成功图标和文本
  • 显示订单号和金额
  • 提供查看订单的选项

失败状态:

  • 显示失败图标和文本
  • 提供返回的选项

用户体验:

  • 清晰的视觉反馈
  • 提供后续操作选项
  • 防止用户返回支付页面

总结

支付的实现涉及多个重要的技术点。首先是用户和应用状态的管理,维护用户信息和订单数据。其次是支付方式的选择,让用户选择合适的支付方式。再次是支付信息的输入,安全地收集支付信息。最后是支付处理和结果反馈,提供清晰的支付流程和用户体验。

这种设计确保了支付流程的安全性和用户体验的流畅性。用户可以轻松选择支付方式、输入支付信息、完成支付,整个流程自然而直观。


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

相关推荐
ZH15455891316 分钟前
Flutter for OpenHarmony Python学习助手实战:面向对象编程实战的实现
python·学习·flutter
renke33641 小时前
Flutter for OpenHarmony:构建一个 Flutter 色彩调和师游戏,RGB 空间探索、感知色差计算与视觉认知训练的工程实现
flutter·游戏
王码码20351 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
ujainu3 小时前
Flutter + OpenHarmony 实现经典打砖块游戏开发实战—— 物理反弹、碰撞检测与关卡系统
flutter·游戏·openharmony·arkanoid·breakout
微祎_3 小时前
构建一个 Flutter 点击速度测试器:深入解析实时交互、性能度量与响应式 UI 设计
flutter·ui·交互
王码码20353 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
ZH15455891313 小时前
Flutter for OpenHarmony Python学习助手实战:Web开发框架应用的实现
python·学习·flutter
晚霞的不甘4 小时前
Flutter for OpenHarmony 构建简洁高效的待办事项应用 实战解析
flutter·ui·前端框架·交互·鸿蒙
百锦再4 小时前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
廖松洋(Alina)4 小时前
【收尾以及复盘】flutter开发鸿蒙APP之成就徽章页面
flutter·华为·开源·harmonyos·鸿蒙