Flutter篆刻石料记录应用开发教程

项目简介

篆刻石料记录是一款专为篆刻爱好者和收藏家设计的石料管理应用,帮助用户系统化管理篆刻石料收藏、记录石料信息、追踪使用情况、管理作品档案。应用集成了石料档案管理、使用记录追踪、作品展示、价值评估、收藏统计等功能,为篆刻爱好者提供全方位的石料管理解决方案。
运行效果图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

核心功能特性

  • 石料档案管理:完整的石料信息录入,包含名称、产地、品种、尺寸、重量、颜色、纹理等
  • 使用记录追踪:详细记录每次使用情况,包含作品信息、使用日期、剩余尺寸
  • 作品档案管理:记录篆刻作品信息,关联使用的石料,保存作品照片
  • 价值评估系统:记录购买价格、当前估值、升值情况
  • 收藏分类管理:按产地、品种、价格等多维度分类管理
  • 数据统计分析:全面的收藏统计,包含数量分析、价值统计、使用频率分析

技术架构

开发环境

  • 框架:Flutter 3.x
  • 开发语言:Dart
  • UI组件:Material Design 3
  • 状态管理:StatefulWidget + setState
  • 动画效果:AnimationController + Tween
  • 数据存储:本地存储 + SharedPreferences

项目结构

lib/
├── main.dart                 # 应用入口和主要逻辑
├── models/                   # 数据模型
│   ├── stone.dart            # 石料模型
│   ├── usage_record.dart     # 使用记录模型
│   ├── artwork.dart          # 作品模型
│   ├── category.dart         # 分类模型
│   └── stats.dart            # 统计数据模型
├── pages/                    # 页面组件
│   ├── stones_page.dart      # 石料列表页面
│   ├── records_page.dart     # 使用记录页面
│   ├── artworks_page.dart    # 作品展示页面
│   ├── categories_page.dart  # 分类管理页面
│   └── stats_page.dart       # 统计分析页面
└── widgets/                  # 自定义组件
    ├── stone_card.dart       # 石料卡片组件
    ├── artwork_card.dart     # 作品卡片组件
    └── stats_chart.dart      # 统计图表组件

数据模型设计

石料模型(Stone)

石料模型是应用的核心数据结构,记录每块石料的详细信息:

class Stone {
  final String id;              // 石料唯一标识
  final String name;            // 石料名称
  final String origin;          // 产地:寿山、青田、昌化、巴林等
  final String variety;         // 品种:田黄、芙蓉、鸡血等
  final StoneType type;         // 类型:印章石、摆件石、雕刻石
  final double length;          // 长度(cm)
  final double width;           // 宽度(cm)
  final double height;          // 高度(cm)
  final double weight;          // 重量(g)
  final String color;           // 颜色描述
  final String texture;         // 质地描述
  final String pattern;         // 纹理描述
  final double purchasePrice;   // 购买价格
  final DateTime purchaseDate;  // 购买日期
  final String purchaseFrom;    // 购买来源
  final double currentValue;    // 当前估值
  final StoneCondition condition; // 品相:完美/良好/一般/有瑕疵
  final bool isUsed;            // 是否已使用
  final String storageLocation; // 存放位置
  final List<String> photos;    // 照片列表
  final String notes;           // 备注信息
  final DateTime createdDate;   // 创建日期
  final bool isFavorite;        // 是否收藏
}

enum StoneType {
  seal,      // 印章石
  ornament,  // 摆件石
  carving    // 雕刻石
}

enum StoneCondition {
  perfect,   // 完美
  good,      // 良好
  fair,      // 一般
  flawed     // 有瑕疵
}

使用记录模型(UsageRecord)

使用记录模型详细记录每次使用石料的完整信息:

class UsageRecord {
  final String id;              // 记录唯一标识
  final String stoneId;         // 关联石料ID
  final String stoneName;       // 石料名称
  final DateTime usageDate;     // 使用日期
  final String purpose;         // 使用目的:篆刻印章/雕刻作品/练习
  final String artworkName;     // 作品名称
  final String content;         // 篆刻内容
  final String style;           // 篆刻风格:汉印/流派印/肖形印等
  final double usedLength;      // 使用长度(cm)
  final double usedWidth;       // 使用宽度(cm)
  final double usedHeight;      // 使用高度(cm)
  final double remainingLength; // 剩余长度(cm)
  final double remainingWidth;  // 剩余宽度(cm)
  final double remainingHeight; // 剩余高度(cm)
  final int duration;           // 耗时(小时)
  final String difficulty;      // 难度:简单/中等/困难
  final String satisfaction;    // 满意度:非常满意/满意/一般/不满意
  final List<String> photos;    // 作品照片
  final String notes;           // 备注
  final DateTime createdDate;   // 创建日期
}

作品模型(Artwork)

作品模型记录篆刻作品的详细信息:

class Artwork {
  final String id;              // 作品唯一标识
  final String name;            // 作品名称
  final String stoneId;         // 关联石料ID
  final String stoneName;       // 石料名称
  final String content;         // 篆刻内容
  final String style;           // 篆刻风格
  final ArtworkType type;       // 作品类型:白文/朱文/朱白相间
  final DateTime createDate;    // 创建日期
  final int duration;           // 创作耗时(小时)
  final String technique;       // 技法描述
  final double size;            // 尺寸(边长cm)
  final String description;     // 作品描述
  final List<String> photos;    // 作品照片
  final double rating;          // 自评分(1-5)
  final String exhibition;      // 展览记录
  final bool isForSale;         // 是否出售
  final double price;           // 售价
  final String notes;           // 备注
  final DateTime createdDate;   // 记录创建日期
}

enum ArtworkType {
  white,     // 白文
  red,       // 朱文
  mixed      // 朱白相间
}

分类模型(Category)

分类模型用于组织和管理石料收藏:

class Category {
  final String id;              // 分类唯一标识
  final String name;            // 分类名称
  final String description;     // 分类描述
  final CategoryType type;      // 分类类型
  final String icon;            // 图标
  final int stoneCount;         // 石料数量
  final DateTime createdDate;   // 创建日期
}

enum CategoryType {
  origin,    // 按产地分类
  variety,   // 按品种分类
  price,     // 按价格分类
  condition, // 按品相分类
  custom     // 自定义分类
}

统计数据模型(StoneStats)

统计数据模型汇总收藏的各项统计信息:

class StoneStats {
  final int totalStones;                    // 石料总数
  final int usedStones;                     // 已使用石料数
  final int unusedStones;                   // 未使用石料数
  final int totalArtworks;                  // 作品总数
  final double totalValue;                  // 总价值
  final double totalInvestment;             // 总投资
  final double appreciation;                // 升值金额
  final double appreciationRate;            // 升值率
  final Map<String, int> originDistribution;    // 产地分布
  final Map<String, int> varietyDistribution;   // 品种分布
  final Map<String, double> valueByOrigin;      // 各产地价值
  final Map<String, int> monthlyUsage;          // 月度使用统计
  final String mostUsedOrigin;              // 最常用产地
  final String mostValuableStone;           // 最贵石料
  final double averageStoneValue;           // 平均石料价值
}

界面设计

主界面布局

应用采用底部导航栏设计,包含五个主要页面:

  1. 石料库:石料列表和详细信息展示
  2. 使用记录:石料使用历史记录
  3. 作品集:篆刻作品展示和管理
  4. 分类:石料分类管理
  5. 统计:收藏数据统计分析

色彩方案

  • 主色调:深褐色系(#8D6E63)- 代表石料的质朴感
  • 辅助色
    • 金色(#FFB74D)- 田黄等名贵石料
    • 红色(#E57373)- 鸡血石
    • 青色(#4DB6AC)- 青田石
    • 灰色(#90A4AE)- 普通石料

状态指示设计

未使用  ● 绿色徽章 + "完整"文字
已使用  ● 橙色徽章 + "已用"文字
部分使用 ● 蓝色徽章 + "部分"文字
完全使用 ● 灰色徽章 + "用完"文字

核心功能实现

1. 石料列表展示

石料列表是应用的核心展示页面,支持多种视图模式和筛选功能:

顶部统计栏

  • 显示石料总数、总价值、升值率
  • 使用渐变背景和卡片布局
  • 数字大字体突出显示

筛选和排序

  • 按产地筛选(寿山、青田、昌化、巴林等)
  • 按品种筛选(田黄、芙蓉、鸡血等)
  • 按价格排序(从高到低/从低到高)
  • 按购买日期排序
  • 按使用状态筛选

石料卡片

Widget _buildStoneCard(Stone stone) {
  return Card(
    elevation: 2,
    margin: EdgeInsets.only(bottom: 12),
    child: InkWell(
      onTap: () => _showStoneDetail(stone),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 标题行:名称 + 状态徽章
            Row(
              children: [
                Container(
                  width: 60,
                  height: 60,
                  decoration: BoxDecoration(
                    color: _getOriginColor(stone.origin).withOpacity(0.1),
                    borderRadius: BorderRadius.circular(8),
                  ),
                  child: Icon(
                    _getStoneIcon(stone.type),
                    color: _getOriginColor(stone.origin),
                    size: 30,
                  ),
                ),
                SizedBox(width: 12),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(stone.name, 
                        style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                      Text('${stone.origin} · ${stone.variety}', 
                        style: TextStyle(color: Colors.grey, fontSize: 14)),
                    ],
                  ),
                ),
                _buildUsageStatusBadge(stone.isUsed),
              ],
            ),
            
            SizedBox(height: 12),
            
            // 尺寸信息
            Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.grey.shade50,
                borderRadius: BorderRadius.circular(8),
              ),
              child: Row(
                children: [
                  Icon(Icons.straighten, size: 16, color: Colors.grey.shade600),
                  SizedBox(width: 8),
                  Text('${stone.length}×${stone.width}×${stone.height}cm'),
                  SizedBox(width: 16),
                  Icon(Icons.scale, size: 16, color: Colors.grey.shade600),
                  SizedBox(width: 8),
                  Text('${stone.weight}g'),
                ],
              ),
            ),
            
            SizedBox(height: 12),
            
            // 价值信息
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('购买价格', style: TextStyle(fontSize: 12, color: Colors.grey)),
                    Text(${stone.purchasePrice.toStringAsFixed(0)}', 
                      style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
                  ],
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: [
                    Text('当前估值', style: TextStyle(fontSize: 12, color: Colors.grey)),
                    Text(${stone.currentValue.toStringAsFixed(0)}', 
                      style: TextStyle(
                        fontSize: 16, 
                        fontWeight: FontWeight.bold,
                        color: stone.currentValue > stone.purchasePrice 
                          ? Colors.green : Colors.red,
                      )),
                  ],
                ),
                if (stone.currentValue > stone.purchasePrice)
                  Container(
                    padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                    decoration: BoxDecoration(
                      color: Colors.green.withOpacity(0.1),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Row(
                      children: [
                        Icon(Icons.trending_up, size: 14, color: Colors.green),
                        SizedBox(width: 4),
                        Text(
                          '+${((stone.currentValue - stone.purchasePrice) / stone.purchasePrice * 100).toStringAsFixed(1)}%',
                          style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold),
                        ),
                      ],
                    ),
                  ),
              ],
            ),
          ],
        ),
      ),
    ),
  );
}

2. 石料详情页

详情信息展示

  • 基本信息:名称、产地、品种、类型
  • 尺寸重量:长宽高、重量
  • 外观描述:颜色、质地、纹理
  • 价值信息:购买价格、当前估值、升值情况
  • 购买信息:购买日期、购买来源
  • 存放信息:存放位置、品相状态
  • 照片展示:多张照片轮播

操作按钮

  • 编辑信息
  • 记录使用
  • 删除石料
  • 分享信息
void _showStoneDetail(Stone stone) {
  showModalBottomSheet(
    context: context,
    isScrollControlled: true,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
    ),
    builder: (context) => DraggableScrollableSheet(
      initialChildSize: 0.8,
      minChildSize: 0.5,
      maxChildSize: 0.95,
      expand: false,
      builder: (context, scrollController) => SingleChildScrollView(
        controller: scrollController,
        child: Padding(
          padding: EdgeInsets.all(20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 标题栏
              Row(
                children: [
                  Icon(_getStoneIcon(stone.type), size: 32),
                  SizedBox(width: 12),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(stone.name, 
                          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
                        Text('${stone.origin} · ${stone.variety}', 
                          style: TextStyle(color: Colors.grey)),
                      ],
                    ),
                  ),
                  _buildUsageStatusBadge(stone.isUsed),
                ],
              ),
              
              Divider(height: 32),
              
              // 基本信息
              _buildDetailSection('基本信息', [
                _buildDetailRow('产地', stone.origin),
                _buildDetailRow('品种', stone.variety),
                _buildDetailRow('类型', _getStoneTypeName(stone.type)),
                _buildDetailRow('品相', _getConditionName(stone.condition)),
              ]),
              
              // 尺寸重量
              _buildDetailSection('尺寸重量', [
                _buildDetailRow('长度', '${stone.length}cm'),
                _buildDetailRow('宽度', '${stone.width}cm'),
                _buildDetailRow('高度', '${stone.height}cm'),
                _buildDetailRow('重量', '${stone.weight}g'),
              ]),
              
              // 外观描述
              _buildDetailSection('外观描述', [
                _buildDetailRow('颜色', stone.color),
                _buildDetailRow('质地', stone.texture),
                _buildDetailRow('纹理', stone.pattern),
              ]),
              
              // 价值信息
              _buildDetailSection('价值信息', [
                _buildDetailRow('购买价格', ${stone.purchasePrice.toStringAsFixed(2)}'),
                _buildDetailRow('当前估值', ${stone.currentValue.toStringAsFixed(2)}'),
                _buildDetailRow('升值金额', 
                  ${(stone.currentValue - stone.purchasePrice).toStringAsFixed(2)}'),
                _buildDetailRow('升值率', 
                  '${((stone.currentValue - stone.purchasePrice) / stone.purchasePrice * 100).toStringAsFixed(2)}%'),
              ]),
              
              // 购买信息
              _buildDetailSection('购买信息', [
                _buildDetailRow('购买日期', _formatDate(stone.purchaseDate)),
                _buildDetailRow('购买来源', stone.purchaseFrom),
              ]),
              
              // 存放信息
              _buildDetailSection('存放信息', [
                _buildDetailRow('存放位置', stone.storageLocation),
              ]),
              
              // 备注
              if (stone.notes.isNotEmpty)
                _buildDetailSection('备注', [
                  Text(stone.notes, style: TextStyle(color: Colors.grey.shade700)),
                ]),
              
              SizedBox(height: 20),
              
              // 操作按钮
              Row(
                children: [
                  Expanded(
                    child: OutlinedButton.icon(
                      onPressed: () => _editStone(stone),
                      icon: Icon(Icons.edit),
                      label: Text('编辑'),
                    ),
                  ),
                  SizedBox(width: 12),
                  Expanded(
                    child: ElevatedButton.icon(
                      onPressed: () {
                        Navigator.pop(context);
                        _showUsageRecordDialog(stone);
                      },
                      icon: Icon(Icons.add),
                      label: Text('记录使用'),
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.brown,
                      ),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    ),
  );
}

3. 使用记录管理

记录添加对话框

void _showUsageRecordDialog(Stone stone) {
  final artworkNameController = TextEditingController();
  final contentController = TextEditingController();
  String purpose = '篆刻印章';
  String style = '汉印';
  String difficulty = '中等';
  
  showDialog(
    context: context,
    builder: (context) => StatefulBuilder(
      builder: (context, setState) => AlertDialog(
        title: Text('记录使用 - ${stone.name}'),
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // 使用目的
              DropdownButtonFormField<String>(
                value: purpose,
                decoration: InputDecoration(labelText: '使用目的'),
                items: ['篆刻印章', '雕刻作品', '练习'].map((p) => 
                  DropdownMenuItem(value: p, child: Text(p))
                ).toList(),
                onChanged: (value) => setState(() => purpose = value!),
              ),
              
              SizedBox(height: 12),
              
              // 作品名称
              TextField(
                controller: artworkNameController,
                decoration: InputDecoration(labelText: '作品名称'),
              ),
              
              SizedBox(height: 12),
              
              // 篆刻内容
              TextField(
                controller: contentController,
                decoration: InputDecoration(labelText: '篆刻内容'),
              ),
              
              SizedBox(height: 12),
              
              // 篆刻风格
              DropdownButtonFormField<String>(
                value: style,
                decoration: InputDecoration(labelText: '篆刻风格'),
                items: ['汉印', '流派印', '肖形印', '鸟虫篆', '其他'].map((s) => 
                  DropdownMenuItem(value: s, child: Text(s))
                ).toList(),
                onChanged: (value) => setState(() => style = value!),
              ),
              
              SizedBox(height: 12),
              
              // 难度
              DropdownButtonFormField<String>(
                value: difficulty,
                decoration: InputDecoration(labelText: '难度'),
                items: ['简单', '中等', '困难'].map((d) => 
                  DropdownMenuItem(value: d, child: Text(d))
                ).toList(),
                onChanged: (value) => setState(() => difficulty = value!),
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('取消'),
          ),
          ElevatedButton(
            onPressed: () {
              _saveUsageRecord(
                stone,
                artworkNameController.text,
                contentController.text,
                purpose,
                style,
                difficulty,
              );
              Navigator.pop(context);
            },
            child: Text('保存'),
          ),
        ],
      ),
    ),
  );
}

使用记录列表

  • 按时间倒序排列
  • 显示石料名称、作品名称、使用日期
  • 显示篆刻内容和风格
  • 显示剩余尺寸信息
  • 支持查看详情和删除

4. 作品展示

作品网格视图

  • 瀑布流布局展示作品照片
  • 显示作品名称和篆刻内容
  • 显示创作日期和评分
  • 点击查看作品详情

作品详情

  • 作品照片轮播
  • 作品基本信息(名称、内容、风格、类型)
  • 关联石料信息
  • 创作信息(日期、耗时、技法)
  • 尺寸和描述
  • 自评分和展览记录
  • 出售信息

5. 数据统计分析

统计概览卡片

Widget _buildStatsOverview() {
  final stats = _calculateStats();
  
  return Card(
    elevation: 4,
    margin: EdgeInsets.all(16),
    child: Padding(
      padding: EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Icon(Icons.analytics, color: Colors.brown, size: 28),
              SizedBox(width: 12),
              Text('收藏统计', 
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
            ],
          ),
          
          SizedBox(height: 20),
          
          // 数量统计
          Row(
            children: [
              Expanded(child: _buildStatItem('石料总数', '${stats.totalStones}块', Icons.category)),
              Expanded(child: _buildStatItem('作品总数', '${stats.totalArtworks}件', Icons.palette)),
            ],
          ),
          
          SizedBox(height: 16),
          
          // 价值统计
          Row(
            children: [
              Expanded(child: _buildStatItem('总投资', ${stats.totalInvestment.toStringAsFixed(0)}', Icons.shopping_cart)),
              Expanded(child: _buildStatItem('总价值', ${stats.totalValue.toStringAsFixed(0)}', Icons.account_balance_wallet)),
            ],
          ),
          
          SizedBox(height: 16),
          
          // 升值情况
          Container(
            width: double.infinity,
            padding: EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: stats.appreciation > 0 
                ? Colors.green.withOpacity(0.1)
                : Colors.red.withOpacity(0.1),
              borderRadius: BorderRadius.circular(12),
              border: Border.all(
                color: stats.appreciation > 0 
                  ? Colors.green.withOpacity(0.3)
                  : Colors.red.withOpacity(0.3),
              ),
            ),
            child: Column(
              children: [
                Icon(
                  stats.appreciation > 0 ? Icons.trending_up : Icons.trending_down,
                  color: stats.appreciation > 0 ? Colors.green : Colors.red,
                  size: 32,
                ),
                SizedBox(height: 8),
                Text('升值金额', style: TextStyle(fontSize: 14, color: Colors.grey)),
                Text(
                  ${stats.appreciation.toStringAsFixed(2)}',
                  style: TextStyle(
                    fontSize: 24,
                    fontWeight: FontWeight.bold,
                    color: stats.appreciation > 0 ? Colors.green : Colors.red,
                  ),
                ),
                Text(
                  '升值率: ${stats.appreciationRate.toStringAsFixed(2)}%',
                  style: TextStyle(
                    fontSize: 14,
                    color: stats.appreciation > 0 ? Colors.green : Colors.red,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    ),
  );
}

产地分布图表

  • 饼图展示各产地石料数量占比
  • 柱状图展示各产地价值分布
  • 列表展示详细数据

品种分布统计

  • 按品种统计石料数量
  • 显示各品种平均价值
  • 最贵品种排行

使用趋势分析

  • 月度使用次数统计
  • 使用频率最高的石料
  • 创作作品数量趋势

数据初始化

示例数据

应用内置了完整的示例数据,包括多块石料、使用记录和作品信息:

void _initializeData() {
  // 初始化石料数据
  _stones = [
    Stone(
      id: 'stone001',
      name: '寿山田黄石',
      origin: '寿山',
      variety: '田黄',
      type: StoneType.seal,
      length: 3.5,
      width: 2.8,
      height: 6.0,
      weight: 185.0,
      color: '金黄色',
      texture: '细腻温润',
      pattern: '萝卜纹明显',
      purchasePrice: 15000.0,
      purchaseDate: DateTime(2022, 5, 15),
      purchaseFrom: '福州古玩市场',
      currentValue: 18000.0,
      condition: StoneCondition.perfect,
      isUsed: false,
      storageLocation: '保险柜A区',
      photos: ['stone001_1.jpg', 'stone001_2.jpg'],
      notes: '品质上乘,萝卜纹清晰,色泽纯正',
      createdDate: DateTime.now().subtract(Duration(days: 600)),
      isFavorite: true,
    ),
    Stone(
      id: 'stone002',
      name: '青田封门青',
      origin: '青田',
      variety: '封门青',
      type: StoneType.seal,
      length: 2.5,
      width: 2.5,
      height: 5.0,
      weight: 95.0,
      color: '青绿色',
      texture: '细腻通透',
      pattern: '纯净无杂质',
      purchasePrice: 3500.0,
      purchaseDate: DateTime(2023, 3, 20),
      purchaseFrom: '浙江青田石市场',
      currentValue: 4200.0,
      condition: StoneCondition.perfect,
      isUsed: true,
      storageLocation: '展示柜B区',
      photos: ['stone002_1.jpg'],
      notes: '封门青精品,适合篆刻细腻印文',
      createdDate: DateTime.now().subtract(Duration(days: 300)),
      isFavorite: true,
    ),
    Stone(
      id: 'stone003',
      name: '昌化鸡血石',
      origin: '昌化',
      variety: '鸡血石',
      type: StoneType.ornament,
      length: 8.0,
      width: 5.0,
      height: 12.0,
      weight: 1200.0,
      color: '红白相间',
      texture: '坚硬细密',
      pattern: '血色鲜艳',
      purchasePrice: 25000.0,
      purchaseDate: DateTime(2021, 10, 8),
      purchaseFrom: '杭州拍卖会',
      currentValue: 32000.0,
      condition: StoneCondition.good,
      isUsed: false,
      storageLocation: '保险柜A区',
      photos: ['stone003_1.jpg', 'stone003_2.jpg', 'stone003_3.jpg'],
      notes: '血色占比约40%,品相优良,适合收藏',
      createdDate: DateTime.now().subtract(Duration(days: 800)),
      isFavorite: true,
    ),
    Stone(
      id: 'stone004',
      name: '巴林福黄石',
      origin: '巴林',
      variety: '福黄石',
      type: StoneType.seal,
      length: 2.0,
      width: 2.0,
      height: 4.5,
      weight: 55.0,
      color: '明黄色',
      texture: '细腻柔和',
      pattern: '色泽均匀',
      purchasePrice: 800.0,
      purchaseDate: DateTime(2023, 8, 12),
      purchaseFrom: '内蒙古巴林右旗',
      currentValue: 950.0,
      condition: StoneCondition.good,
      isUsed: true,
      storageLocation: '工作台',
      photos: ['stone004_1.jpg'],
      notes: '性价比高,适合练习篆刻',
      createdDate: DateTime.now().subtract(Duration(days: 150)),
      isFavorite: false,
    ),
    Stone(
      id: 'stone005',
      name: '寿山芙蓉石',
      origin: '寿山',
      variety: '芙蓉石',
      type: StoneType.seal,
      length: 3.0,
      width: 3.0,
      height: 5.5,
      weight: 145.0,
      color: '粉白色',
      texture: '细腻如脂',
      pattern: '温润通透',
      purchasePrice: 2800.0,
      purchaseDate: DateTime(2023, 1, 25),
      purchaseFrom: '福州寿山石市场',
      currentValue: 3200.0,
      condition: StoneCondition.perfect,
      isUsed: false,
      storageLocation: '展示柜A区',
      photos: ['stone005_1.jpg', 'stone005_2.jpg'],
      notes: '芙蓉石精品,质地细腻,适合篆刻',
      createdDate: DateTime.now().subtract(Duration(days: 360)),
      isFavorite: true,
    ),
  ];

  // 初始化使用记录
  _usageRecords = [
    UsageRecord(
      id: 'ur001',
      stoneId: 'stone002',
      stoneName: '青田封门青',
      usageDate: DateTime.now().subtract(Duration(days: 30)),
      purpose: '篆刻印章',
      artworkName: '清风徐来',
      content: '清风徐来',
      style: '汉印',
      usedLength: 0.0,
      usedWidth: 0.0,
      usedHeight: 1.5,
      remainingLength: 2.5,
      remainingWidth: 2.5,
      remainingHeight: 3.5,
      duration: 4,
      difficulty: '中等',
      satisfaction: '非常满意',
      photos: ['artwork001_1.jpg', 'artwork001_2.jpg'],
      notes: '印文清晰,石质细腻,篆刻顺畅',
      createdDate: DateTime.now().subtract(Duration(days: 30)),
    ),
    UsageRecord(
      id: 'ur002',
      stoneId: 'stone004',
      stoneName: '巴林福黄石',
      usageDate: DateTime.now().subtract(Duration(days: 60)),
      purpose: '练习',
      artworkName: '练习作品',
      content: '福',
      style: '流派印',
      usedLength: 0.0,
      usedWidth: 0.0,
      usedHeight: 1.0,
      remainingLength: 2.0,
      remainingWidth: 2.0,
      remainingHeight: 3.5,
      duration: 2,
      difficulty: '简单',
      satisfaction: '满意',
      photos: ['practice001.jpg'],
      notes: '练习刀法,石质适中',
      createdDate: DateTime.now().subtract(Duration(days: 60)),
    ),
    UsageRecord(
      id: 'ur003',
      stoneId: 'stone002',
      stoneName: '青田封门青',
      usageDate: DateTime.now().subtract(Duration(days: 90)),
      purpose: '篆刻印章',
      artworkName: '静以修身',
      content: '静以修身',
      style: '汉印',
      usedLength: 0.0,
      usedWidth: 0.0,
      usedHeight: 1.5,
      remainingLength: 2.5,
      remainingWidth: 2.5,
      remainingHeight: 5.0,
      duration: 5,
      difficulty: '困难',
      satisfaction: '非常满意',
      photos: ['artwork002_1.jpg'],
      notes: '四字印章,布局合理,刀法流畅',
      createdDate: DateTime.now().subtract(Duration(days: 90)),
    ),
  ];

  // 初始化作品数据
  _artworks = [
    Artwork(
      id: 'art001',
      name: '清风徐来',
      stoneId: 'stone002',
      stoneName: '青田封门青',
      content: '清风徐来',
      style: '汉印',
      type: ArtworkType.white,
      createDate: DateTime.now().subtract(Duration(days: 30)),
      duration: 4,
      technique: '冲刀为主,切刀辅助',
      size: 2.5,
      description: '四字白文印,布局方正,刀法简洁有力',
      photos: ['artwork001_1.jpg', 'artwork001_2.jpg'],
      rating: 4.5,
      exhibition: '',
      isForSale: false,
      price: 0.0,
      notes: '个人满意作品',
      createdDate: DateTime.now().subtract(Duration(days: 30)),
    ),
    Artwork(
      id: 'art002',
      name: '静以修身',
      stoneId: 'stone002',
      stoneName: '青田封门青',
      content: '静以修身',
      style: '汉印',
      type: ArtworkType.white,
      createDate: DateTime.now().subtract(Duration(days: 90)),
      duration: 5,
      technique: '冲刀为主,注重笔意',
      size: 2.5,
      description: '四字白文印,字形端庄,章法严谨',
      photos: ['artwork002_1.jpg'],
      rating: 4.8,
      exhibition: '2023年社区篆刻展',
      isForSale: false,
      price: 0.0,
      notes: '参展作品,获得好评',
      createdDate: DateTime.now().subtract(Duration(days: 90)),
    ),
  ];
}

辅助方法

工具函数

// 获取石料类型图标
IconData _getStoneIcon(StoneType type) {
  switch (type) {
    case StoneType.seal:
      return Icons.crop_square;
    case StoneType.ornament:
      return Icons.diamond;
    case StoneType.carving:
      return Icons.brush;
  }
}

// 获取产地颜色
Color _getOriginColor(String origin) {
  switch (origin) {
    case '寿山':
      return Colors.brown.shade700;
    case '青田':
      return Colors.teal.shade600;
    case '昌化':
      return Colors.red.shade600;
    case '巴林':
      return Colors.amber.shade700;
    default:
      return Colors.grey.shade600;
  }
}

// 获取石料类型名称
String _getStoneTypeName(StoneType type) {
  switch (type) {
    case StoneType.seal:
      return '印章石';
    case StoneType.ornament:
      return '摆件石';
    case StoneType.carving:
      return '雕刻石';
  }
}

// 获取品相名称
String _getConditionName(StoneCondition condition) {
  switch (condition) {
    case StoneCondition.perfect:
      return '完美';
    case StoneCondition.good:
      return '良好';
    case StoneCondition.fair:
      return '一般';
    case StoneCondition.flawed:
      return '有瑕疵';
  }
}

// 格式化日期
String _formatDate(DateTime date) {
  return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

// 格式化日期时间
String _formatDateTime(DateTime dateTime) {
  return '${_formatDate(dateTime)} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
}

// 计算统计数据
StoneStats _calculateStats() {
  final totalStones = _stones.length;
  final usedStones = _stones.where((s) => s.isUsed).length;
  final unusedStones = totalStones - usedStones;
  final totalArtworks = _artworks.length;
  
  final totalInvestment = _stones.fold(0.0, (sum, s) => sum + s.purchasePrice);
  final totalValue = _stones.fold(0.0, (sum, s) => sum + s.currentValue);
  final appreciation = totalValue - totalInvestment;
  final appreciationRate = totalInvestment > 0 
    ? (appreciation / totalInvestment * 100) 
    : 0.0;
  
  // 产地分布
  final originDistribution = <String, int>{};
  for (final stone in _stones) {
    originDistribution[stone.origin] = (originDistribution[stone.origin] ?? 0) + 1;
  }
  
  // 品种分布
  final varietyDistribution = <String, int>{};
  for (final stone in _stones) {
    varietyDistribution[stone.variety] = (varietyDistribution[stone.variety] ?? 0) + 1;
  }
  
  // 各产地价值
  final valueByOrigin = <String, double>{};
  for (final stone in _stones) {
    valueByOrigin[stone.origin] = (valueByOrigin[stone.origin] ?? 0.0) + stone.currentValue;
  }
  
  // 月度使用统计
  final monthlyUsage = <String, int>{};
  for (final record in _usageRecords) {
    final monthKey = '${record.usageDate.year}-${record.usageDate.month.toString().padLeft(2, '0')}';
    monthlyUsage[monthKey] = (monthlyUsage[monthKey] ?? 0) + 1;
  }
  
  // 最常用产地
  final originUsageCount = <String, int>{};
  for (final record in _usageRecords) {
    final stone = _stones.firstWhere((s) => s.id == record.stoneId);
    originUsageCount[stone.origin] = (originUsageCount[stone.origin] ?? 0) + 1;
  }
  final mostUsedOrigin = originUsageCount.entries.isNotEmpty
    ? originUsageCount.entries.reduce((a, b) => a.value > b.value ? a : b).key
    : '暂无';
  
  // 最贵石料
  final mostValuableStone = _stones.isNotEmpty
    ? _stones.reduce((a, b) => a.currentValue > b.currentValue ? a : b).name
    : '暂无';
  
  // 平均石料价值
  final averageStoneValue = totalStones > 0 ? totalValue / totalStones : 0.0;
  
  return StoneStats(
    totalStones: totalStones,
    usedStones: usedStones,
    unusedStones: unusedStones,
    totalArtworks: totalArtworks,
    totalValue: totalValue,
    totalInvestment: totalInvestment,
    appreciation: appreciation,
    appreciationRate: appreciationRate,
    originDistribution: originDistribution,
    varietyDistribution: varietyDistribution,
    valueByOrigin: valueByOrigin,
    monthlyUsage: monthlyUsage,
    mostUsedOrigin: mostUsedOrigin,
    mostValuableStone: mostValuableStone,
    averageStoneValue: averageStoneValue,
  );
}

性能优化

列表优化

使用ListView.builder而不是ListView,实现按需加载:

ListView.builder(
  itemCount: stones.length,
  itemBuilder: (context, index) {
    return _buildStoneCard(stones[index]);
  },
  // 添加缓存范围
  cacheExtent: 500,
)

图片优化

对于石料和作品照片,使用缩略图和懒加载:

// 使用Image.asset加载本地图片
Image.asset(
  stone.photos.first,
  width: 60,
  height: 60,
  fit: BoxFit.cover,
  // 使用缓存
  cacheWidth: 120,
  cacheHeight: 120,
)

动画优化

使用FadeTransition实现页面切换动画:

FadeTransition(
  opacity: _fadeAnimation,
  child: IndexedStack(
    index: _selectedIndex,
    children: [
      _buildStonesPage(),
      _buildRecordsPage(),
      _buildArtworksPage(),
      _buildCategoriesPage(),
      _buildStatsPage(),
    ],
  ),
)

扩展功能建议

1. 云端同步

  • 数据云端备份
  • 多设备同步
  • 数据导入导出

2. 社交功能

  • 作品分享到社交平台
  • 篆刻爱好者社区
  • 作品交流和点评

3. 智能识别

  • 石料照片识别产地品种
  • AI辅助估值
  • 真伪鉴别辅助

4. 市场行情

  • 石料市场价格行情
  • 拍卖信息推送
  • 投资价值分析

5. 学习资源

  • 篆刻技法教程
  • 名家作品欣赏
  • 石料知识库

常见问题

Q1: 如何准确记录石料尺寸?

A: 建议使用游标卡尺测量,精确到0.1mm。对于不规则石料,记录最大尺寸。

Q2: 石料估值如何确定?

A: 可以参考市场行情、拍卖记录、专家评估等多方面因素,定期更新估值。

Q3: 如何保存石料照片?

A: 建议从多个角度拍摄,包括正面、侧面、纹理特写等,光线充足,背景简洁。

Q4: 使用记录中的剩余尺寸如何计算?

A: 根据实际使用情况,记录使用后的剩余长宽高。对于印章石,主要记录高度变化。

Q5: 如何分类管理大量石料?

A: 可以按产地、品种、价格区间、使用状态等多维度分类,使用标签系统。

项目总结

本项目实现了一个功能完整的篆刻石料记录应用,主要特点包括:

功能完整性

  • 石料档案管理
  • 使用记录追踪
  • 作品档案管理
  • 价值评估系统
  • 数据统计分析

用户体验

  • 界面简洁美观
  • 操作流畅便捷
  • 信息展示清晰
  • 数据可视化

技术实现

  • 代码结构清晰
  • 注释完整详细
  • 遵循最佳实践
  • 性能优化到位

学习价值

  • Flutter基础组件使用
  • 状态管理实践
  • 数据模型设计
  • 统计分析实现
  • 业务逻辑处理

通过本项目的学习和实践,可以掌握Flutter应用开发的核心技能,同时了解篆刻艺术和石料收藏的相关知识,为开发更专业的收藏管理应用打下基础。

参考资料

  • Flutter官方文档:https://flutter.dev/docs
  • Material Design 3:https://m3.material.io
  • Dart语言指南:https://dart.dev/guides
  • 篆刻艺术知识:相关专业书籍和网站

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐