基于 Flutter + Dart + Material Design 3 的仿元初到家电商移动端APP项目 01

161 阅读7分钟

基于 Flutter + Dart + Material Design 3 的仿元初到家电商移动端APP项目 01

项目视频

jvideo

抖音 www.douyin.com/video/75135…

项目截图

首页01

Snipaste_2025-06-08_21-25-52.png

首页02

Snipaste_2025-06-08_21-27-04.png

首页03

Snipaste_2025-06-08_21-27-40.png

首页04

Snipaste_2025-06-08_21-28-10.png

首页05

Snipaste_2025-06-08_21-29-16.png

个人中心

Snipaste_2025-06-08_21-29-32.png

📋 项目概述

1.1 项目背景

随着移动电商的快速发展,用户对购物APP的体验要求越来越高。本项目基于Flutter框架开发一款高性能、跨平台的电商移动应用,旨在提供流畅的购物体验和完整的电商功能。

1.2 项目目标

  • 用户体验:打造流畅、直观的购物界面
  • 性能优化:实现快速加载和流畅动画
  • 跨平台兼容:支持iOS、Android等多平台
  • 组件化开发:构建可复用的UI组件库
  • 可扩展性:支持快速功能迭代和业务扩展

1.3 应用场景

  • 移动端购物应用
  • 多平台电商解决方案
  • UI组件库开发参考
  • Flutter企业级应用实践

🛠️ 技术选型

2.1 核心技术栈

技术领域选择方案版本要求选择理由
开发框架Flutter^3.8.0跨平台、性能优秀、生态丰富
编程语言Dart^3.0.0类型安全、异步支持、现代语法
UI设计Material Design 3最新版现代化设计规范、组件丰富
图片缓存cached_network_image^3.3.1性能优化、内存管理
文件系统path_provider^2.1.4跨平台文件路径管理

2.2 开发工具链

开发环境:
  - Flutter SDK: ^3.8.0
  - Dart SDK: ^3.0.0
  - IDE: VS Code / Android Studio
  - 版本控制: Git

构建工具:
  - Flutter CLI
  - 自动化构建脚本 (ARM64优化)
  - 多平台构建支持

代码质量:
  - flutter_lints: ^5.0.0
  - 静态代码分析
  - 0警告编译标准

🏗️ 系统架构

3.1 整体架构设计

┌─────────────────────────────────────────┐
│              Presentation Layer          │
├─────────────────────────────────────────┤
│  Pages/     │ Components/  │ Gallery/   │
│  • HomePage │ • UI (7个)   │ • 组件展示 │
│  • CartPage │ • Home (6个) │ • 实时预览 │
│  • Profile  │ • Product(2) │ • 交互演示 │
│  • Member   │ • Profile(9) │            │
│  • Category │ • Common     │            │
├─────────────────────────────────────────┤
│              Business Logic Layer        │
├─────────────────────────────────────────┤
│  Services/  │ Models/      │ Utils/     │
│  • 网络请求 │ • 数据模型   │ • 工具类   │
│  • 状态管理 │ • 业务实体   │ • 常量定义 │
│  • 缓存管理 │              │ • 扩展方法 │
├─────────────────────────────────────────┤
│              Platform Layer              │
├─────────────────────────────────────────┤
│  iOS    │ Android │  Web   │  Desktop   │
│  Native │ Native  │ Chrome │ Win/Mac/Lin│
└─────────────────────────────────────────┘

3.2 目录结构

lib/
├── main.dart                    # 应用入口点
├── gallery_page.dart           # 组件画廊展示
├── components/                 # 组件库目录
│   ├── ui/                    # 通用UI组件(7个)
│   │   ├── location_tag.dart
│   │   ├── promotion_banner.dart
│   │   ├── coupon_card.dart
│   │   ├── delivery_tag.dart
│   │   ├── store_header.dart
│   │   ├── cart_icon_with_badge.dart
│   │   └── delivery_mode_switcher.dart
│   ├── home/                  # 首页组件(6个)
│   │   ├── custom_search_bar.dart
│   │   ├── category_grid.dart
│   │   ├── horizontal_category_list.dart
│   │   ├── featured_section.dart
│   │   ├── carousel_banner.dart
│   │   └── tab_selector.dart
│   ├── product/               # 商品组件(2个)
│   │   ├── product_card.dart
│   │   └── product_card_large.dart
│   ├── profile/               # 个人中心组件(9个)
│   │   ├── profile_avatar.dart
│   │   ├── vip_badge.dart
│   │   ├── user_info_section.dart
│   │   ├── points_card.dart
│   │   ├── services_card.dart
│   │   ├── service_item.dart
│   │   ├── order_item.dart
│   │   ├── orders_section.dart
│   │   └── member_services_section.dart
│   └── common/                # 公共组件
├── pages/                     # 页面文件
│   ├── home_page.dart
│   ├── category_page.dart
│   ├── member_page.dart
│   ├── cart_page.dart
│   └── profile_page.dart
├── models/                    # 数据模型
├── services/                  # 业务服务
└── utils/                     # 工具类

🚀 核心功能模块

4.1 主要功能清单

📱 用户界面模块
  • 底部导航栏:5个主要页面切换
  • 首页展示:商品轮播、分类导航、特色推荐
  • 商品浏览:商品列表、详情展示、规格选择
  • 购物车管理:商品添加、数量调节、结算功能
  • 用户中心:个人信息、VIP系统、积分管理
🎨 组件库系统
  • UI界面组件:7个通用界面组件
  • 首页组件:6个首页专用组件
  • 商品组件:2个商品展示组件
  • 个人中心组件:9个用户相关组件
  • 组件画廊:24个组件的可视化展示系统
🛒 电商业务功能
  • 商品管理:商品展示、分类筛选、搜索功能
  • 购物车:添加商品、数量管理、价格计算
  • 会员系统:VIP等级、积分累积、专属优惠
  • 优惠券:券码管理、使用规则、优惠计算
  • 订单管理:订单创建、状态跟踪、历史记录

4.2 技术功能特性

🎯 性能优化
  • ARM64专用构建:APK体积减少65%(22MB→8MB)
  • 图片缓存机制:网络图片本地缓存,提升加载速度
  • 懒加载策略:组件按需加载,减少内存占用
  • 动画优化:流畅的过渡效果和交互反馈
🌍 跨平台支持
  • 移动端:iOS、Android原生性能
  • 统一代码库:一套代码,多平台部署

💻 技术实现方案

5.1 应用入口设计

// main.dart - 应用程序入口
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SunGiven App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
        useMaterial3: true,  // 启用 Material Design 3
      ),
      home: const MainPage(),
    );
  }
}

// 主页面架构 - 底部导航
class MainPage extends StatefulWidget {
  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  int _currentIndex = 0;

  final List<Widget> _pages = [
    HomePage(),      // 首页
    CategoryPage(),  // 分类
    MemberPage(),    // 会员
    CartPage(),      // 购物车
    ProfilePage(),   // 个人中心
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        currentIndex: _currentIndex,
        onTap: (index) => setState(() => _currentIndex = index),
        selectedItemColor: Colors.green,
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.card_membership), label: '会员'),
          BottomNavigationBarItem(icon: CartIconWithBadge(), label: '购物车'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: '个人中心'),
        ],
      ),
    );
  }
}

5.2 组件化开发模式

可复用组件设计原则
// 示例:优惠券卡片组件
class CouponCard extends StatelessWidget {
  final String amount;           // 必需参数
  final String condition;        // 必需参数
  final VoidCallback? onTap;     // 可选回调
  final Color? backgroundColor;  // 可选样式
  final Color? textColor;        // 可选样式

  const CouponCard({
    Key? key,
    required this.amount,        // 明确必需参数
    required this.condition,
    this.onTap,                  // 提供回调扩展
    this.backgroundColor,        // 支持样式定制
    this.textColor,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        padding: EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: backgroundColor ?? Colors.red.shade50,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: Colors.red.shade200),
          // 添加券样式的切口效果
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.1),
              blurRadius: 4,
              offset: Offset(0, 2),
            ),
          ],
        ),
        child: Row(
          children: [
            // 优惠金额
            Text($amount', style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
              color: textColor ?? Colors.red,
            )),
            Spacer(),
            // 使用条件
            Text(condition, style: TextStyle(
              color: Colors.grey.shade600,
              fontSize: 14,
            )),
            SizedBox(width: 8),
            Icon(Icons.arrow_forward_ios,
                 size: 16,
                 color: Colors.grey.shade400),
          ],
        ),
      ),
    );
  }
}
商品卡片组件实现
// 商品卡片 - 支持数量选择和购物车动画
class ProductCard extends StatefulWidget {
  final String imageUrl;
  final String title;
  final String currentPrice;
  final String? originalPrice;
  final Function(int)? onQuantityChanged;

  const ProductCard({
    Key? key,
    required this.imageUrl,
    required this.title,
    required this.currentPrice,
    this.originalPrice,
    this.onQuantityChanged,
  }) : super(key: key);

  @override
  State<ProductCard> createState() => _ProductCardState();
}

class _ProductCardState extends State<ProductCard>
    with SingleTickerProviderStateMixin {
  int _quantity = 0;
  late AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: Duration(milliseconds: 300),
      vsync: this,
    );
  }

  void _updateQuantity(int delta) {
    setState(() {
      _quantity = (_quantity + delta).clamp(0, 99);
      if (_quantity > 0) {
        _animationController.forward().then((_) {
          _animationController.reverse();
        });
      }
    });
    widget.onQuantityChanged?.call(_quantity);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(8),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 8,
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 商品图片
          Expanded(
            child: ClipRRect(
              borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
              child: CachedNetworkImage(
                imageUrl: widget.imageUrl,
                fit: BoxFit.cover,
                width: double.infinity,
                placeholder: (context, url) => Container(
                  color: Colors.grey.shade100,
                  child: Center(child: CircularProgressIndicator()),
                ),
                errorWidget: (context, url, error) => Container(
                  color: Colors.grey.shade100,
                  child: Icon(Icons.image_not_supported),
                ),
              ),
            ),
          ),

          // 商品信息
          Padding(
            padding: EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // 商品标题
                Text(
                  widget.title,
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),

                SizedBox(height: 8),

                // 价格信息
                Row(
                  children: [
                    Text(
                      widget.currentPrice,
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                        color: Colors.red,
                      ),
                    ),
                    if (widget.originalPrice != null) ...[
                      SizedBox(width: 8),
                      Text(
                        widget.originalPrice!,
                        style: TextStyle(
                          fontSize: 12,
                          color: Colors.grey,
                          decoration: TextDecoration.lineThrough,
                        ),
                      ),
                    ],
                  ],
                ),

                SizedBox(height: 8),

                // 数量选择器
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    if (_quantity == 0)
                      GestureDetector(
                        onTap: () => _updateQuantity(1),
                        child: AnimatedBuilder(
                          animation: _animationController,
                          builder: (context, child) {
                            return Transform.scale(
                              scale: 1.0 + _animationController.value * 0.1,
                              child: Container(
                                padding: EdgeInsets.all(8),
                                decoration: BoxDecoration(
                                  color: Colors.green,
                                  borderRadius: BorderRadius.circular(6),
                                ),
                                child: Icon(
                                  Icons.add_shopping_cart,
                                  color: Colors.white,
                                  size: 16,
                                ),
                              ),
                            );
                          },
                        ),
                      )
                    else
                      Row(
                        children: [
                          GestureDetector(
                            onTap: () => _updateQuantity(-1),
                            child: Container(
                              padding: EdgeInsets.all(4),
                              decoration: BoxDecoration(
                                border: Border.all(color: Colors.green),
                                borderRadius: BorderRadius.circular(4),
                              ),
                              child: Icon(Icons.remove,
                                         size: 16,
                                         color: Colors.green),
                            ),
                          ),
                          Padding(
                            padding: EdgeInsets.symmetric(horizontal: 12),
                            child: Text('$_quantity',
                                       style: TextStyle(fontWeight: FontWeight.bold)),
                          ),
                          GestureDetector(
                            onTap: () => _updateQuantity(1),
                            child: Container(
                              padding: EdgeInsets.all(4),
                              decoration: BoxDecoration(
                                color: Colors.green,
                                borderRadius: BorderRadius.circular(4),
                              ),
                              child: Icon(Icons.add,
                                         size: 16,
                                         color: Colors.white),
                            ),
                          ),
                        ],
                      ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
}

5.3 图片缓存与性能优化

// pubspec.yaml 依赖配置
dependencies:
  cached_network_image: ^3.3.1
  path_provider: ^2.1.4

// 图片缓存实现
class OptimizedNetworkImage extends StatelessWidget {
  final String imageUrl;
  final double? width;
  final double? height;
  final BoxFit fit;

  const OptimizedNetworkImage({
    Key? key,
    required this.imageUrl,
    this.width,
    this.height,
    this.fit = BoxFit.cover,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      imageUrl: imageUrl,
      width: width,
      height: height,
      fit: fit,
      // 内存缓存优化
      memCacheWidth: width?.toInt(),
      memCacheHeight: height?.toInt(),
      // 加载状态
      placeholder: (context, url) => Container(
        width: width,
        height: height,
        color: Colors.grey.shade100,
        child: Center(
          child: CircularProgressIndicator(strokeWidth: 2),
        ),
      ),
      // 错误状态
      errorWidget: (context, url, error) => Container(
        width: width,
        height: height,
        color: Colors.grey.shade100,
        child: Icon(
          Icons.image_not_supported,
          color: Colors.grey.shade400,
        ),
      ),
      // 缓存配置
      cacheKey: imageUrl,
      fadeInDuration: Duration(milliseconds: 200),
      fadeOutDuration: Duration(milliseconds: 100),
    );
  }
}

⚡ 性能优化策略

6.1 构建优化

ARM64专用构建
# macOS/Linux 构建脚本 (build_arm64.sh)
#!/bin/bash
set -e

echo "🔧 开始构建 ARM64 专用 APK..."
echo "📱 目标平台: Android ARM64"
echo "⚡ 优化模式: Release"

# 清理旧的构建文件
flutter clean

# 获取依赖
flutter pub get

# ARM64 专用构建 - 大幅减少APK体积
flutter build apk --release --target-platform android-arm64

# 构建结果
APK_PATH="build/app/outputs/flutter-apk/app-arm64-v8a-release.apk"
if [ -f "$APK_PATH" ]; then
    APK_SIZE=$(du -h "$APK_PATH" | cut -f1)
    echo "✅ 构建成功!"
    echo "📦 APK文件: $APK_PATH"
    echo "📏 文件大小: $APK_SIZE"
    echo "🚀 性能优化: ARM64专用构建,体积减少65%"
else
    echo "❌ 构建失败!"
    exit 1
fi
多平台构建命令
# Android 构建
flutter build apk --release --target-platform android-arm64  # ARM64优化
flutter build apk --release  # 通用版本

# iOS 构建
flutter build ios --release

6.2 运行时优化

内存管理策略
// 图片内存优化
class ImageMemoryManager {
  static const int MAX_CACHE_WIDTH = 800;
  static const int MAX_CACHE_HEIGHT = 600;

  static Widget buildOptimizedImage(String url, {
    double? width,
    double? height,
  }) {
    return CachedNetworkImage(
      imageUrl: url,
      memCacheWidth: (width ?? MAX_CACHE_WIDTH).toInt(),
      memCacheHeight: (height ?? MAX_CACHE_HEIGHT).toInt(),
      maxWidthDiskCache: MAX_CACHE_WIDTH,
      maxHeightDiskCache: MAX_CACHE_HEIGHT,
    );
  }
}

// 组件懒加载
class LazyLoadingList extends StatefulWidget {
  final List<Widget> children;

  @override
  State<LazyLoadingList> createState() => _LazyLoadingListState();
}

class _LazyLoadingListState extends State<LazyLoadingList> {
  final ScrollController _controller = ScrollController();
  final Set<int> _loadedIndices = {};

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _controller,
      itemCount: widget.children.length,
      itemBuilder: (context, index) {
        // 懒加载逻辑
        if (!_loadedIndices.contains(index)) {
          _loadedIndices.add(index);
          return widget.children[index];
        }
        return Container(); // 占位符
      },
    );
  }
}

6.3 用户体验优化

加载状态管理
// 统一的加载状态组件
class LoadingStateWidget extends StatelessWidget {
  final bool isLoading;
  final String? loadingText;
  final Widget child;
  final Widget? errorWidget;

  const LoadingStateWidget({
    Key? key,
    required this.isLoading,
    required this.child,
    this.loadingText,
    this.errorWidget,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (isLoading) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            if (loadingText != null) ...[
              SizedBox(height: 16),
              Text(loadingText!),
            ],
          ],
        ),
      );
    }
    return child;
  }
}

// 错误处理组件
class ErrorStateWidget extends StatelessWidget {
  final String message;
  final VoidCallback? onRetry;

  const ErrorStateWidget({
    Key? key,
    required this.message,
    this.onRetry,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.error_outline, size: 64, color: Colors.grey),
          SizedBox(height: 16),
          Text(message, textAlign: TextAlign.center),
          if (onRetry != null) ...[
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: onRetry,
              child: Text('重试'),
            ),
          ],
        ],
      ),
    );
  }
}

🚀 开发与部署

7.1 开发环境配置

环境要求
开发环境:
  Flutter SDK: ">=3.8.0 <4.0.0"
  Dart SDK: ">=3.0.0 <4.0.0"

推荐IDE:
  - Visual Studio Code + Flutter Extension

7.3 自动化构建

CI/CD 配置示例 (GitHub Actions)
# .github/workflows/build.yml
name: Build and Deploy

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.8.0'

    - name: Install dependencies
      run: flutter pub get

    - name: Run tests
      run: flutter test

    - name: Build APK (ARM64)
      run: flutter build apk --release --target-platform android-arm64

    - name: Upload artifacts
      uses: actions/upload-artifact@v3
      with:
        name: app-builds
        path: |
          build/app/outputs/flutter-apk/
          build/web/

  deploy:
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'

    steps:
    - name: Deploy to Firebase Hosting
      uses: FirebaseExtended/action-hosting-deploy@v0
      with:
        repoToken: '${{ secrets.GITHUB_TOKEN }}'
        firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
        projectId: 'sungiven-app'

## 🧪 测试策略

### 8.1 测试架构

#### 测试配置
```yaml
# pubspec.yaml - 测试依赖
dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0
  mockito: ^5.4.4
  build_runner: ^2.4.7
  json_annotation: ^4.8.1

8.2 单元测试实现

// test/components/coupon_card_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sungiven_app/components/ui/coupon_card.dart';

void main() {
  group('CouponCard Tests', () {
    testWidgets('应该正确显示优惠券信息', (WidgetTester tester) async {
      // 准备测试数据
      const String amount = '15';
      const String condition = '满99元可用';
      bool tapCalled = false;

      // 构建组件
      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: CouponCard(
              amount: amount,
              condition: condition,
              onTap: () => tapCalled = true,
            ),
          ),
        ),
      );

      // 验证显示内容
      expect(find.text($amount'), findsOneWidget);
      expect(find.text(condition), findsOneWidget);

      // 测试点击事件
      await tester.tap(find.byType(CouponCard));
      expect(tapCalled, true);
    });

    testWidgets('应该支持自定义样式', (WidgetTester tester) async {
      const Color customColor = Colors.blue;

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: CouponCard(
              amount: '20',
              condition: '满150元可用',
              backgroundColor: customColor,
              textColor: Colors.white,
            ),
          ),
        ),
      );

      // 验证自定义样式
      final container = tester.widget<Container>(
        find.descendant(
          of: find.byType(CouponCard),
          matching: find.byType(Container).first,
        ),
      );

      final decoration = container.decoration as BoxDecoration;
      expect(decoration.color, customColor);
    });
  });
}

8.3 Widget测试

// test/pages/home_page_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sungiven_app/pages/home_page.dart';

void main() {
  group('HomePage Tests', () {
    testWidgets('应该显示所有必要的组件', (WidgetTester tester) async {
      await tester.pumpWidget(
        MaterialApp(home: HomePage()),
      );

      // 验证核心组件存在
      expect(find.byType(CustomSearchBar), findsOneWidget);
      expect(find.byType(CategoryGrid), findsOneWidget);
      expect(find.byType(CarouselBanner), findsOneWidget);
      expect(find.byType(FeaturedSection), findsOneWidget);
    });

    testWidgets('搜索栏应该响应用户输入', (WidgetTester tester) async {
      await tester.pumpWidget(
        MaterialApp(home: HomePage()),
      );

      // 查找搜索框
      final searchBar = find.byType(TextField);
      expect(searchBar, findsOneWidget);

      // 模拟输入
      await tester.enterText(searchBar, '牛奶');
      await tester.pump();

      // 验证输入内容
      expect(find.text('牛奶'), findsOneWidget);
    });
  });
}

📊 项目数据指标

9.1 开发统计

指标类别数值说明
代码量~15,000行Dart代码总量
组件数量24个UI组件库规模
页面数量5个主要功能页面
支持平台2个iOS/Android
依赖包数3个核心外部依赖

9.2 性能指标

9.3 质量指标

🔧 开发工具与工作流

10.1 推荐开发工具

10.2 Git工作流

分支策略
提交规范
# 提交信息格式
<type>(<scope>): <subject>

<body>

<footer>

# 示例
feat(components): 添加优惠券卡片组件

- 实现基础优惠券UI设计
- 支持自定义颜色和金额
- 添加点击回调功能

Closes #123
提交类型
  • feat: 新功能
  • fix: Bug修复
  • docs: 文档更新
  • style: 代码格式化
  • refactor: 重构
  • test: 测试相关
  • chore: 构建过程或辅助工具的变动

10.3 代码规范

Dart代码风格

🚀 部署方案

11.1 多平台部署

Android部署
iOS部署

11.2 CI/CD流水线

GitLab CI配置