Flutter for OpenHarmony 实战: mango_shop 购物车模块的状态同步与本地缓存处理

Flutter for OpenHarmony 实战: mango_shop 购物车模块的状态同步与本地缓存处理

作者:爱吃大芒果

个人主页 爱吃大芒果

本文所属专栏Flutter

更多专栏
Ascend C 算子开发教程(进阶)
鸿蒙集成
OpenAgents
openJiuwen
从0到1自学C++


购物车模块现状分析

通过对 mango_shop 项目的分析,我们发现:

  1. 当前实现

    • 使用 CartView 组件接收购物车数据
    • 通过回调函数与父组件通信,更新购物车数据
    • 实现了基本的购物车功能:选择商品、修改数量、删除商品、清空购物车、结算
    • 购物车数据存储在 Mainpage 组件的状态中,没有持久化存储
  2. 存在问题

    • 应用重启后购物车数据会丢失
    • 使用回调函数传递数据,代码耦合度高
    • 没有实现跨设备同步
    • 没有针对 OpenHarmony 平台的特殊适配

购物车模块优化方案

1. 状态管理优化

1.1 购物车状态管理

使用 Riverpod 进行购物车状态管理:

dart 复制代码
// lib/providers/cart_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/models/cart_item.dart';
import 'package:mango_shop/services/cart_service.dart';

// 购物车状态
class CartState {
  final List<CartItem> items;
  final bool isLoading;
  final String? errorMessage;

  const CartState({
    required this.items,
    this.isLoading = false,
    this.errorMessage,
  });

  // 计算购物车总数量
  int get totalCount {
    return items.fold(0, (sum, item) => sum + item.quantity);
  }

  // 计算选中商品数量
  int get selectedCount {
    return items
        .where((item) => item.isSelected)
        .fold(0, (sum, item) => sum + item.quantity);
  }

  // 计算选中商品总价
  double get totalPrice {
    return items
        .where((item) => item.isSelected)
        .fold(0, (sum, item) => sum + item.price * item.quantity);
  }

  // 是否全选
  bool get isAllSelected {
    return items.isNotEmpty && items.every((item) => item.isSelected);
  }
}

// 购物车状态提供者
class CartNotifier extends StateNotifier<CartState> {
  final CartService _cartService;

  CartNotifier(this._cartService) : super(const CartState(items: [])) {
    // 初始化时加载购物车数据
    _loadCart();
  }

  // 加载购物车数据
  Future<void> _loadCart() async {
    state = state.copyWith(isLoading: true);
    
    try {
      final cartItems = await _cartService.getCart();
      state = CartState(items: cartItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 添加商品到购物车
  Future<void> addToCart(CartItem item) async {
    state = state.copyWith(isLoading: true);
    
    try {
      final updatedItems = await _cartService.addToCart(item);
      state = CartState(items: updatedItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 更新商品数量
  Future<void> updateQuantity(String itemId, int quantity) async {
    state = state.copyWith(isLoading: true);
    
    try {
      final updatedItems = await _cartService.updateQuantity(itemId, quantity);
      state = CartState(items: updatedItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 切换商品选中状态
  Future<void> toggleItemSelection(String itemId) async {
    state = state.copyWith(isLoading: true);
    
    try {
      final updatedItems = await _cartService.toggleItemSelection(itemId);
      state = CartState(items: updatedItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 切换全选状态
  Future<void> toggleAllSelection() async {
    state = state.copyWith(isLoading: true);
    
    try {
      final updatedItems = await _cartService.toggleAllSelection();
      state = CartState(items: updatedItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 删除商品
  Future<void> removeItem(String itemId) async {
    state = state.copyWith(isLoading: true);
    
    try {
      final updatedItems = await _cartService.removeItem(itemId);
      state = CartState(items: updatedItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 清空购物车
  Future<void> clearCart() async {
    state = state.copyWith(isLoading: true);
    
    try {
      await _cartService.clearCart();
      state = const CartState(items: []);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }

  // 结算购物车
  Future<void> checkout() async {
    state = state.copyWith(isLoading: true);
    
    try {
      final selectedItems = state.items.where((item) => item.isSelected).toList();
      await _cartService.checkout(selectedItems);
      
      // 从购物车中移除已结算的商品
      final remainingItems = state.items.where((item) => !item.isSelected).toList();
      await _cartService.saveCart(remainingItems);
      
      state = CartState(items: remainingItems);
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
    }
  }
}

// 创建购物车状态提供者
final cartProvider = StateNotifierProvider<CartNotifier, CartState>((ref) {
  final cartService = CartService();
  return CartNotifier(cartService);
});

// 便捷获取购物车商品数量的提供者
final cartCountProvider = Provider<int>((ref) {
  return ref.watch(cartProvider).totalCount;
});

// 便捷获取购物车选中商品数量的提供者
final cartSelectedCountProvider = Provider<int>((ref) {
  return ref.watch(cartProvider).selectedCount;
});

// 便捷获取购物车总价的提供者
final cartTotalPriceProvider = Provider<double>((ref) {
  return ref.watch(cartProvider).totalPrice;
});

// 便捷获取购物车是否全选的提供者
final cartIsAllSelectedProvider = Provider<bool>((ref) {
  return ref.watch(cartProvider).isAllSelected;
});



1.2 购物车商品模型
dart 复制代码
// lib/models/cart_item.dart
class CartItem {
  final String id;
  final String name;
  final String image;
  final double price;
  int quantity;
  bool isSelected;

  CartItem({
    required this.id,
    required this.name,
    required this.image,
    required this.price,
    required this.quantity,
    this.isSelected = true,
  });

  // 从 JSON 创建购物车项实例
  factory CartItem.fromJson(Map<String, dynamic> json) {
    return CartItem(
      id: json['id'] ?? '',
      name: json['name'] ?? '',
      image: json['image'] ?? '',
      price: json['price'] ?? 0.0,
      quantity: json['quantity'] ?? 1,
      isSelected: json['isSelected'] ?? true,
    );
  }

  // 转换为 JSON
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'image': image,
      'price': price,
      'quantity': quantity,
      'isSelected': isSelected,
    };
  }
}

2. 本地缓存实现

2.1 购物车服务
dart 复制代码
// lib/services/cart_service.dart
import 'dart:convert';
import 'package:mango_shop/models/cart_item.dart';
import 'package:mango_shop/services/secure_storage_service.dart';
import 'package:mango_shop/utils/platform/adapter.dart';

class CartService {
  static const String _cartKey = 'mango_shop_cart';
  final SecureStorageService _storage = SecureStorageService();

  // 获取购物车数据
  Future<List<CartItem>> getCart() async {
    try {
      final cartJson = await _storage.read(_cartKey);
      if (cartJson == null) {
        return [];
      }
      
      final List<dynamic> cartList = jsonDecode(cartJson);
      return cartList.map((item) => CartItem.fromJson(item)).toList();
    } catch (e) {
      print('获取购物车数据失败: $e');
      return [];
    }
  }

  // 保存购物车数据
  Future<void> saveCart(List<CartItem> cartItems) async {
    try {
      final cartJson = jsonEncode(cartItems.map((item) => item.toJson()).toList());
      await _storage.write(_cartKey, cartJson);
    } catch (e) {
      print('保存购物车数据失败: $e');
    }
  }

  // 添加商品到购物车
  Future<List<CartItem>> addToCart(CartItem item) async {
    final cartItems = await getCart();
    
    // 检查商品是否已在购物车中
    final existingItemIndex = cartItems.indexWhere((cartItem) => cartItem.id == item.id);
    
    if (existingItemIndex != -1) {
      // 商品已存在,增加数量
      cartItems[existingItemIndex].quantity += item.quantity;
    } else {
      // 商品不存在,添加到购物车
      cartItems.add(item);
    }
    
    await saveCart(cartItems);
    return cartItems;
  }

  // 更新商品数量
  Future<List<CartItem>> updateQuantity(String itemId, int quantity) async {
    final cartItems = await getCart();
    
    final itemIndex = cartItems.indexWhere((item) => item.id == itemId);
    if (itemIndex != -1) {
      if (quantity > 0) {
        cartItems[itemIndex].quantity = quantity;
      } else {
        // 数量为0,移除商品
        cartItems.removeAt(itemIndex);
      }
    }
    
    await saveCart(cartItems);
    return cartItems;
  }

  // 切换商品选中状态
  Future<List<CartItem>> toggleItemSelection(String itemId) async {
    final cartItems = await getCart();
    
    final itemIndex = cartItems.indexWhere((item) => item.id == itemId);
    if (itemIndex != -1) {
      cartItems[itemIndex].isSelected = !cartItems[itemIndex].isSelected;
    }
    
    await saveCart(cartItems);
    return cartItems;
  }

  // 切换全选状态
  Future<List<CartItem>> toggleAllSelection() async {
    final cartItems = await getCart();
    
    if (cartItems.isEmpty) {
      return cartItems;
    }
    
    // 检查当前是否全选
    final isAllSelected = cartItems.every((item) => item.isSelected);
    
    // 切换所有商品的选中状态
    for (final item in cartItems) {
      item.isSelected = !isAllSelected;
    }
    
    await saveCart(cartItems);
    return cartItems;
  }

  // 删除商品
  Future<List<CartItem>> removeItem(String itemId) async {
    final cartItems = await getCart();
    
    cartItems.removeWhere((item) => item.id == itemId);
    
    await saveCart(cartItems);
    return cartItems;
  }

  // 清空购物车
  Future<void> clearCart() async {
    await _storage.delete(_cartKey);
  }

  // 结算
  Future<void> checkout(List<CartItem> items) async {
    // 这里可以添加结算逻辑,如调用支付 API 等
    print('结算商品: ${items.length} 件');
    // 模拟结算过程
    await Future.delayed(const Duration(seconds: 1));
  }
}
2.2 安全存储服务
dart 复制代码
// lib/services/secure_storage_service.dart
import 'dart:io';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class SecureStorageService {
  static final SecureStorageService _instance = SecureStorageService._internal();
  factory SecureStorageService() => _instance;

  late FlutterSecureStorage _storage;

  SecureStorageService._internal() {
    // 根据平台创建不同的存储实例
    if (Platform.isOpenHarmony) {
      // OpenHarmony 平台特定配置
      _storage = FlutterSecureStorage(
        aOptions: AndroidOptions(
          encryptedSharedPreferences: true,
        ),
      );
    } else if (Platform.isAndroid) {
      _storage = FlutterSecureStorage(
        aOptions: AndroidOptions(
          encryptedSharedPreferences: true,
        ),
      );
    } else if (Platform.isIOS) {
      _storage = FlutterSecureStorage(
        iOptions: IOSOptions(
          accessibility: IOSAccessibility.first_unlock,
        ),
      );
    } else {
      // 其他平台配置
      _storage = FlutterSecureStorage();
    }
  }

  // 存储数据
  Future<void> write(String key, String value) async {
    await _storage.write(key: key, value: value);
  }

  // 读取数据
  Future<String?> read(String key) async {
    return await _storage.read(key: key);
  }

  // 删除数据
  Future<void> delete(String key) async {
    await _storage.delete(key: key);
  }

  // 删除所有数据
  Future<void> deleteAll() async {
    await _storage.deleteAll();
  }

  // 检查键是否存在
  Future<bool> containsKey(String key) async {
    final value = await _storage.read(key: key);
    return value != null;
  }
}

3. 购物车组件优化

3.1 购物车页面
dart 复制代码
// lib/pages/Cart/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/providers/cart_provider.dart';
import 'package:mango_shop/utils/colors.dart';
import 'package:mango_shop/utils/text_styles.dart';

class CartView extends ConsumerWidget {
  const CartView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cartState = ref.watch(cartProvider);
    final cartNotifier = ref.read(cartProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        title: const Text('购物车'),
        centerTitle: true,
        backgroundColor: Colors.white,
        elevation: 1,
        actions: [
          if (cartState.items.isNotEmpty)
            TextButton(
              onPressed: () {
                // 显示确认对话框
                showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: const Text('确认清空'),
                      content: const Text('确定要清空购物车吗?'),
                      actions: [
                        TextButton(
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                          child: const Text('取消'),
                        ),
                        TextButton(
                          onPressed: () async {
                            await cartNotifier.clearCart();
                            Navigator.of(context).pop();
                            // 显示清空成功提示
                            ScaffoldMessenger.of(context).showSnackBar(
                              const SnackBar(content: Text('购物车已清空')),
                            );
                          },
                          child: const Text('确定', style: TextStyle(color: Colors.red)),
                        ),
                      ],
                    );
                  },
                );
              },
              child: const Text('清空', style: TextStyle(color: Colors.red)),
            ),
        ],
      ),
      body: cartState.items.isEmpty
          ? _buildEmptyCart()
          : Column(
              children: [
                Expanded(
                  child: ListView.builder(
                    itemCount: cartState.items.length,
                    itemBuilder: (context, index) {
                      final item = cartState.items[index];
                      return _buildCartItem(context, item, cartNotifier);
                    },
                  ),
                ),
                _buildCheckoutBar(context, ref, cartNotifier),
              ],
            ),
    );
  }

  Widget _buildEmptyCart() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.asset(
            'lib/assets/ic_public_cart_normal.png',
            width: 100,
            height: 100,
            color: Colors.grey[300],
          ),
          const SizedBox(height: 20),
          const Text('购物车是空的', style: TextStyle(color: Colors.grey, fontSize: 16)),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // 跳转到首页
              Navigator.pushNamed(context, '/');
            },
            child: const Text('去购物'),
            style: ElevatedButton.styleFrom(
              backgroundColor: AppColors.primary,
              foregroundColor: AppColors.white,
              padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 12),
              elevation: 2,
              shadowColor: AppColors.primary.withOpacity(0.3),
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildCartItem(BuildContext context, CartItem item, CartNotifier notifier) {
    return Container(
      padding: const EdgeInsets.all(16),
      margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(8),
        boxShadow: [
          BoxShadow(
            color: Colors.grey[200]!,
            spreadRadius: 1,
            blurRadius: 3,
            offset: const Offset(0, 1),
          ),
        ],
      ),
      child: Row(
        children: [
          // 选择框
          Checkbox(
            value: item.isSelected,
            onChanged: (value) => notifier.toggleItemSelection(item.id),
            activeColor: Colors.red,
          ),
          // 商品图片
          Container(
            width: 80,
            height: 80,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              image: DecorationImage(
                image: AssetImage(item.image),
                fit: BoxFit.cover,
              ),
            ),
          ),
          const SizedBox(width: 12),
          // 商品信息
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  item.name,
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                  style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
                ),
                const SizedBox(height: 8),
                Text(
                  '¥${item.price.toStringAsFixed(2)}',
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: Colors.red,
                  ),
                ),
                const SizedBox(height: 8),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    // 数量控制
                    Row(
                      children: [
                        IconButton(
                          onPressed: () => notifier.updateQuantity(item.id, item.quantity - 1),
                          icon: const Icon(Icons.remove, size: 18),
                          constraints: const BoxConstraints(minWidth: 30),
                        ),
                        Container(
                          width: 40,
                          alignment: Alignment.center,
                          child: Text(item.quantity.toString()),
                        ),
                        IconButton(
                          onPressed: () => notifier.updateQuantity(item.id, item.quantity + 1),
                          icon: const Icon(Icons.add, size: 18),
                          constraints: const BoxConstraints(minWidth: 30),
                        ),
                      ],
                    ),
                    // 删除按钮
                    IconButton(
                      onPressed: () => notifier.removeItem(item.id),
                      icon: const Icon(Icons.delete_outline, color: Colors.grey),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildCheckoutBar(BuildContext context, WidgetRef ref, CartNotifier notifier) {
    final totalPrice = ref.watch(cartTotalPriceProvider);
    final selectedCount = ref.watch(cartSelectedCountProvider);
    final isAllSelected = ref.watch(cartIsAllSelectedProvider);

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border(top: BorderSide(color: Colors.grey[200]!)),
      ),
      child: Row(
        children: [
          // 全选
          Row(
            children: [
              Checkbox(
                value: isAllSelected,
                onChanged: (value) => notifier.toggleAllSelection(),
                activeColor: Colors.red,
              ),
              const Text('全选'),
            ],
          ),
          Expanded(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                // 总价
                Column(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: [
                    Text(
                      '合计: ¥${totalPrice.toStringAsFixed(2)}',
                      style: const TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                        color: Colors.red,
                      ),
                    ),
                    Text(
                      '已选 ${selectedCount} 件商品',
                      style: const TextStyle(fontSize: 12, color: Colors.grey),
                    ),
                  ],
                ),
                const SizedBox(width: 16),
                // 结算按钮
                ElevatedButton(
                  onPressed: selectedCount > 0
                      ? () async {
                          // 显示结算确认对话框
                          showDialog(
                            context: context,
                            builder: (BuildContext context) {
                              return AlertDialog(
                                title: const Text('确认结算'),
                                content: Column(
                                  mainAxisSize: MainAxisSize.min,
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Text('共 ${selectedCount} 件商品'),
                                    const SizedBox(height: 8),
                                    Text('合计:¥${totalPrice.toStringAsFixed(2)}'),
                                  ],
                                ),
                                actions: [
                                  TextButton(
                                    onPressed: () {
                                      Navigator.of(context).pop();
                                    },
                                    child: const Text('取消'),
                                    style: TextButton.styleFrom(
                                      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                                    ),
                                  ),
                                  TextButton(
                                    onPressed: () async {
                                      Navigator.of(context).pop();
                                      try {
                                        await notifier.checkout();
                                        // 显示结算成功提示
                                        ScaffoldMessenger.of(context).showSnackBar(
                                          const SnackBar(content: Text('结算成功!')),
                                        );
                                      } catch (e) {
                                        // 显示结算失败提示
                                        ScaffoldMessenger.of(context).showSnackBar(
                                          SnackBar(content: Text('结算失败:$e')),
                                        );
                                      }
                                    },
                                    child: const Text('确认结算', style: TextStyle(color: Colors.red)),
                                  ),
                                ],
                              );
                            },
                          );
                        }
                      : null,
                  child: const Text('结算'),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: AppColors.primary,
                    foregroundColor: AppColors.white,
                    padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
                    elevation: 2,
                    shadowColor: AppColors.primary.withOpacity(0.3),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(8),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
3.2 主页面集成
dart 复制代码
// lib/pages/Main/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/pages/Cart/index.dart';
import 'package:mango_shop/pages/Category/index.dart';
import 'package:mango_shop/pages/Home/index.dart';
import 'package:mango_shop/pages/Mine/index.dart';
import 'package:mango_shop/providers/cart_provider.dart';
import 'package:mango_shop/utils/colors.dart';

class Mainpage extends ConsumerWidget {
  const Mainpage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cartCount = ref.watch(cartCountProvider);
    int _currentIndex = 0;

    // 定义Tab栏数据
    final List<Map<String, dynamic>> _tablist = [
      {
        "icon": "lib/assets/ic_public_home_normal.png",
        "activeIcon": "lib/assets/ic_public_home_active.png",
        "text": "首页",
        "page": const HomeView(),
      },
      {
        "icon": "lib/assets/ic_public_pro_normal.png",
        "activeIcon": "lib/assets/ic_public_pro_active.png",
        "text": "分类",
        "page": const categoryView(),
      },
      {
        "icon": "lib/assets/ic_public_cart_normal.png",
        "activeIcon": "lib/assets/ic_public_cart_active.png",
        "text": "购物车",
        "page": const CartView(),
      },
      {
        "icon": "lib/assets/ic_public_my_normal.png",
        "activeIcon": "lib/assets/ic_public_my_active.png",
        "text": "我的",
        "page": const MineView(),
      },
    ];

    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: _tablist.map((tab) => tab["page"] as Widget).toList(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        showUnselectedLabels: true,
        selectedItemColor: Colors.red,
        unselectedItemColor: Colors.grey[600],
        selectedFontSize: 12,
        unselectedFontSize: 12,
        type: BottomNavigationBarType.fixed,
        elevation: 8,
        backgroundColor: Colors.white,
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
        currentIndex: _currentIndex,
        items: _tablist.asMap().entries.map((entry) {
          int index = entry.key;
          Map<String, dynamic> tab = entry.value;
          
          return BottomNavigationBarItem(
            icon: _buildTabIcon(index, false, cartCount),
            activeIcon: _buildTabIcon(index, true, cartCount),
            label: tab["text"],
          );
        }).toList(),
      ),
    );
  }

  // 构建Tab栏图标
  Widget _buildTabIcon(int index, bool isActive, int cartCount) {
    // 根据是否选中使用不同的图标
    String iconPath = isActive 
        ? _tablist[index]["activeIcon"]! 
        : _tablist[index]["icon"]!;
    
    Widget icon = Image.asset(
      iconPath,
      width: 30,
      height: 30,
    );

    // 购物车图标添加数量显示
    if (index == 2) {
      return Stack(
        alignment: Alignment.topRight,
        children: [
          icon,
          if (cartCount > 0)
            Container(
              padding: const EdgeInsets.all(2),
              decoration: BoxDecoration(
                color: Colors.red,
                borderRadius: BorderRadius.circular(10),
              ),
              constraints: const BoxConstraints(
                minWidth: 18,
                minHeight: 18,
              ),
              child: Text(
                cartCount > 99 ? '99+' : cartCount.toString(),
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 12,
                  fontWeight: FontWeight.bold,
                ),
                textAlign: TextAlign.center,
              ),
            ),
        ],
      );
    }

    return icon;
  }
}

4. 跨平台适配

4.1 OpenHarmony 平台适配

针对 OpenHarmony 平台,我们需要进行以下适配:

  1. 存储适配

    • OpenHarmony 平台的存储机制与其他平台有所不同,需要使用特定的存储 API
    • 实现平台感知的存储策略
  2. 性能优化

    • 针对 OpenHarmony 平台的性能特性,优化购物车数据的加载和更新
    • 减少不必要的 UI 重建
  3. 平台感知实现

dart 复制代码
// lib/utils/platform/adapter.dart
import 'dart:io';

class PlatformAdapter {
  // 判断当前平台
  static bool get isOpenHarmony {
    return Platform.environment.containsKey('OHOS') || 
           Platform.operatingSystem.toLowerCase() == 'openharmony';
  }

  // 获取平台特定的存储策略
  static bool useSecureStorage {
    return !isOpenHarmony; // OpenHarmony 平台使用普通存储
  }

  // 获取平台特定的购物车数据同步策略
  static bool useRealTimeSync {
    return !isOpenHarmony; // OpenHarmony 平台使用定时同步
  }

  // 获取平台特定的缓存大小限制
  static int get cartCacheSizeLimit {
    return isOpenHarmony ? 100 : 200; // OpenHarmony 平台限制更小
  }
}
  1. OpenHarmony 特定的存储实现
dart 复制代码
// lib/services/ohos_storage_adapter.dart
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:mango_shop/utils/platform/adapter.dart';
import 'package:path_provider/path_provider.dart';

class OhosStorageAdapter {
  static final OhosStorageAdapter _instance = OhosStorageAdapter._internal();
  factory OhosStorageAdapter() => _instance;

  OhosStorageAdapter._internal();

  // 检查是否为 OpenHarmony 平台
  bool get isOpenHarmony => PlatformAdapter.isOpenHarmony;

  // OpenHarmony 平台的存储路径
  Future<String> get _storagePath async {
    if (isOpenHarmony) {
      final directory = await getApplicationDocumentsDirectory();
      return '${directory.path}/mango_shop';
    }
    throw UnsupportedError('Not OpenHarmony platform');
  }

  // 确保存储目录存在
  Future<void> _ensureDirectoryExists() async {
    if (isOpenHarmony) {
      final path = await _storagePath;
      final directory = Directory(path);
      if (!directory.existsSync()) {
        directory.createSync(recursive: true);
      }
    }
  }

  // 保存数据到 OpenHarmony 存储
  Future<void> save(String key, dynamic value) async {
    if (isOpenHarmony) {
      try {
        await _ensureDirectoryExists();
        final path = await _storagePath;
        final file = File('$path/$key.json');
        final jsonValue = jsonEncode(value);
        await file.writeAsString(jsonValue);
        if (kDebugMode) {
          print('OpenHarmony: Saved $key to storage');
        }
      } catch (e) {
        if (kDebugMode) {
          print('OpenHarmony storage error: $e');
        }
      }
    }
  }

  // 从 OpenHarmony 存储读取数据
  Future<dynamic> read(String key) async {
    if (isOpenHarmony) {
      try {
        await _ensureDirectoryExists();
        final path = await _storagePath;
        final file = File('$path/$key.json');
        if (file.existsSync()) {
          final jsonValue = await file.readAsString();
          if (kDebugMode) {
            print('OpenHarmony: Read $key from storage');
          }
          return jsonDecode(jsonValue);
        }
      } catch (e) {
        if (kDebugMode) {
          print('OpenHarmony storage error: $e');
        }
      }
    }
    return null;
  }

  // 删除 OpenHarmony 存储中的数据
  Future<void> delete(String key) async {
    if (isOpenHarmony) {
      try {
        await _ensureDirectoryExists();
        final path = await _storagePath;
        final file = File('$path/$key.json');
        if (file.existsSync()) {
          await file.delete();
          if (kDebugMode) {
            print('OpenHarmony: Deleted $key from storage');
          }
        }
      } catch (e) {
        if (kDebugMode) {
          print('OpenHarmony storage error: $e');
        }
      }
    }
  }
}

测试与调试

1. 购物车功能测试

  1. 功能测试

    • 测试添加商品到购物车
    • 测试修改商品数量
    • 测试删除商品
    • 测试清空购物车
    • 测试结算功能
    • 测试应用重启后购物车数据是否保留
  2. 性能测试

    • 测试购物车数据加载性能
    • 测试购物车数据更新性能
    • 测试大量商品时的性能表现
  3. 跨平台测试

    • 确保在所有平台上购物车功能表现一致
    • 特别测试 OpenHarmony 平台的适配情况

2. 调试工具

  1. 日志调试

    • 添加购物车操作的详细日志,便于调试
    • 使用不同级别的日志,区分正常操作和错误情况
  2. 性能分析

    • 使用 Flutter DevTools 分析购物车操作的性能
    • 监控内存使用和 UI 渲染性能
  3. 存储调试

    • 添加存储操作的日志,了解数据存储情况
    • 提供查看当前存储内容的工具

总结与展望

通过本文介绍的购物车模块优化方案,我们可以:

  1. 提高代码可维护性:使用 Riverpod 进行状态管理,代码结构更清晰
  2. 增强数据持久性:实现购物车数据的本地缓存,确保应用重启后数据不丢失
  3. 提升用户体验:优化购物车操作的响应速度和流畅度
  4. 增强跨平台兼容性:针对 OpenHarmony 平台进行专门的适配
  5. 简化开发流程:封装购物车服务,减少重复代码

未来,可以考虑:

  1. 云同步:实现购物车数据的云端同步,支持多设备共享购物车
  2. 离线购物:增强离线购物体验,在网络恢复后自动同步
  3. 智能推荐:基于购物车内容提供个性化商品推荐
  4. 购物车分析:分析用户购物习惯,优化商品展示和促销策略

Flutter for OpenHarmony 为跨平台应用开发提供了新的可能性,通过合理的购物车模块设计和跨平台适配,可以构建出在所有平台上表现出色的应用。


欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区

相关推荐
恋猫de小郭13 小时前
iOS + AI ,国外一个叫 Rork Max 的项目打算替换掉 Xcode
android·前端·flutter
Rick199314 小时前
如何保证数据库和Redis缓存一致性
数据库·redis·缓存
左手厨刀右手茼蒿14 小时前
Flutter for OpenHarmony:dart_console 打造炫酷命令行界面,绘制表格、控制光标与进度条(CLI 交互库) 深度解析与鸿蒙适配指南
flutter·交互·harmonyos·绘制
加农炮手Jinx14 小时前
Flutter for OpenHarmony 实战:疯狂头像 App(三)— 复合动画与交互反馈 — 让 UI 跃动起来
flutter·ui·交互·harmonyos·鸿蒙
王码码203514 小时前
lutter for OpenHarmony 实战之基础组件:第六十二篇 SystemChannels — 探秘 Flutter 与系统交互的捷径
flutter·microsoft·交互·harmonyos
RaidenLiu16 小时前
别再手写 MethodChannel 了:Flutter Pigeon 工程级实践与架构设计
前端·flutter·前端框架
Bowen_J19 小时前
HarmonyOS 主流跨平台开发框架对比: ArkUI、Flutter、React Native、KMP、UniApp
flutter·react native·harmonyos
九狼JIULANG1 天前
Flutter SSE 流式响应用 Dio 实现 OpenAI 兼容接口的逐 Token 输出
flutter
恋猫de小郭1 天前
你是不是觉得 R8 很讨厌,但 Android 为什么选择 R8 ?也许你对 R8 还不够了解
android·前端·flutter
前端不太难1 天前
Flutter 页面切换后为什么会“状态丢失”或“状态常驻”?
flutter·状态模式