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

相关推荐
Swift社区12 分钟前
Flutter 项目如何做好性能监控与问题定位?
flutter
LawrenceLan12 分钟前
36.Flutter 零基础入门(三十六):StatefulWidget 与 setState 进阶 —— 动态页面必学
开发语言·前端·flutter·dart
weixin_4434785117 分钟前
flutter组件学习之Stack 组件详解
学习·flutter
程序员Ctrl喵18 分钟前
分层架构的协同艺术——解构 Flutter 的心脏
flutter·架构
Hello.Reader32 分钟前
Flutter IM 桌面端消息发送、ACK 回执、SQLite 本地缓存与断线重连设计
flutter·缓存·sqlite
Hello.Reader38 分钟前
Flutter IM 桌面端项目架构、聊天窗口布局与 WebSocket 长连接设计
websocket·flutter·架构
前端不太难40 分钟前
Flutter Web / Desktop 为什么“能跑但不好用”?
前端·flutter·状态模式
前端不太难1 小时前
Flutter 国际化和主题系统如何避免后期大改?
flutter·状态模式
小雨凉如水1 小时前
flutter 基础组件学习
学习·flutter
Swift社区1 小时前
Flutter 适合长期大型项目 - 真实边界在哪里
flutter