Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现

已签合同功能展示了用户已经签署的所有合同。这个功能是用户管理已签合同的重要工具。通过已签合同页面,用户可以查看、搜索、过滤和管理已签署的合同。在这篇文章中,我们将详细讲解如何实现一个功能完整、易于使用的已签合同页面。

已签合同功能的设计目标

已签合同功能需要实现以下设计目标:

  1. 合同列表展示:清晰地展示所有已签合同,让用户能够快速浏览。列表应该显示合同的基本信息,如名称、签署日期等。
  2. 搜索功能:支持按合同名称、合同号等搜索已签合同。搜索应该是实时的,用户输入时应该立即显示搜索结果。
  3. 过滤功能:支持按日期范围、分类等过滤已签合同。过滤可以帮助用户快速找到特定的合同。
  4. 排序功能:支持按签署日期、合同名称等排序已签合同。排序可以帮助用户按照自己的需求查看合同。
  5. 合同详情:点击合同可以查看合同的详细信息,包括合同内容、签署人等。
  6. 合同操作:支持下载、分享、删除等操作。用户可以根据需要对合同进行操作。

已签合同数据模型的设计

首先,我们需要定义已签合同的数据模型。这个模型会包含合同的所有必要信息。

dart 复制代码
class SignedContractModel {
  final String id;
  final String name;
  final String contractNumber;
  final String description;
  final String category;
  final DateTime signedDate;
  final DateTime expiryDate;
  final String status;
  final List<String> signatories;
  final String fileUrl;
  final int fileSize;
  final String contentPreview;

  SignedContractModel({
    required this.id,
    required this.name,
    required this.contractNumber,
    required this.description,
    required this.category,
    required this.signedDate,
    required this.expiryDate,
    required this.status,
    required this.signatories,
    required this.fileUrl,
    required this.fileSize,
    required this.contentPreview,
  });

在定义已签合同的数据模型时,我们包含了合同的所有关键信息。id字段用于唯一标识每份合同,namecontractNumber用于合同的识别,description提供了合同的简要说明。category字段用于对合同进行分类,便于后续的过滤操作。signedDateexpiryDate分别记录了合同的签署日期和过期日期,这对于合同的生命周期管理至关重要。status字段表示合同的当前状态,signatories列表记录了所有签署人。fileUrlfileSize用于合同文件的管理和下载,contentPreview提供了合同内容的预览。

除了基本的属性定义外,我们还需要提供一个工厂构造函数来支持JSON序列化。这样可以方便地从API响应中创建模型对象。

dart 复制代码
  factory SignedContractModel.fromJson(Map<String, dynamic> json) {
    return SignedContractModel(
      id: json['id'] as String,
      name: json['name'] as String,
      contractNumber: json['contractNumber'] as String,
      description: json['description'] as String,
      category: json['category'] as String,
      signedDate: DateTime.parse(json['signedDate'] as String),
      expiryDate: DateTime.parse(json['expiryDate'] as String),
      status: json['status'] as String,
      signatories: List<String>.from(json['signatories'] as List),
      fileUrl: json['fileUrl'] as String,
      fileSize: json['fileSize'] as int,
      contentPreview: json['contentPreview'] as String,
    );
  }
}

fromJson工厂构造函数是一个重要的方法,它允许我们从JSON数据创建SignedContractModel对象。这个方法在从API获取数据时非常有用。通过这个工厂方法,我们可以轻松地将服务器返回的JSON数据转换为Dart对象。这种模式在Flutter开发中是标准做法,它提供了一个清晰的接口来处理数据序列化和反序列化。

已签合同页面的基本结构

现在让我们实现已签合同页面。这个页面会展示所有已签合同,并支持搜索、过滤和排序。页面使用StatefulWidget来管理状态。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';

class SignedContractsPage extends StatefulWidget {
  const SignedContractsPage({Key? key}) : super(key: key);

  @override
  State<SignedContractsPage> createState() => _SignedContractsPageState();
}

我们使用StatefulWidget作为页面的基础,这样可以在用户交互时动态更新页面的状态。导入了必要的包:flutter/material.dart提供了Material Design组件,flutter_screenutil用于响应式设计,get库用于路由和状态管理。

dart 复制代码
class _SignedContractsPageState extends State<SignedContractsPage> {
  List<SignedContractModel> _contracts = [];
  List<SignedContractModel> _filteredContracts = [];
  bool _isLoading = false;
  String _searchQuery = '';
  String _sortBy = 'date';
  String _filterCategory = 'All';
  DateTimeRange? _dateRange;

在状态类中,我们定义了多个状态变量来管理页面的不同方面。_contracts存储所有已签合同,_filteredContracts存储经过搜索和过滤后的合同列表。_isLoading标记数据加载状态,用于显示加载动画。_searchQuery记录用户的搜索输入,_sortBy记录当前的排序方式(默认按日期排序),_filterCategory记录选中的分类过滤器(默认显示所有分类),_dateRange记录选中的日期范围。

dart 复制代码
  @override
  void initState() {
    super.initState();
    _loadContracts();
  }

initState生命周期方法中,我们调用_loadContracts方法来初始化合同数据。这确保了页面加载时合同数据已经准备好。

数据加载和初始化

dart 复制代码
  Future<void> _loadContracts() async {
    setState(() {
      _isLoading = true;
    });

    try {
      await Future.delayed(const Duration(milliseconds: 500));
      
      setState(() {
        _contracts = _generateMockContracts();
        _filteredContracts = _contracts;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      Get.snackbar('Error', 'Failed to load contracts');
    }
  }

_loadContracts方法负责加载合同数据。首先,我们设置_isLoading为true,这样UI会显示加载动画。然后,我们使用Future.delayed来模拟网络请求的延迟。在实际应用中,这里应该调用真实的API来获取服务器上的合同数据。加载完成后,我们调用_generateMockContracts方法生成模拟数据,并将其赋值给_contracts_filteredContracts。最后,我们设置_isLoading为false,UI会停止显示加载动画。如果发生错误,我们会显示一个错误提示。

dart 复制代码
  List<SignedContractModel> _generateMockContracts() {
    return List.generate(10, (index) {
      return SignedContractModel(
        id: 'contract_$index',
        name: 'Service Agreement ${index + 1}',
        contractNumber: 'SA-2024-${String.fromCharCode(65 + index)}',
        description: 'Service agreement for client ${index + 1}',
        category: index % 2 == 0 ? 'Business' : 'Legal',
        signedDate: DateTime.now().subtract(Duration(days: index * 5)),
        expiryDate: DateTime.now().add(Duration(days: 365 - index * 5)),
        status: 'Active',
        signatories: ['John Doe', 'Jane Smith'],
        fileUrl: 'https://example.com/contract_$index.pdf',
        fileSize: 256 + index * 10,
        contentPreview: 'This is a service agreement between parties...',
      );
    });
  }

_generateMockContracts方法生成了10份模拟合同数据用于演示。每份合同都包含了完整的信息,包括ID、名称、合同号、描述、分类、签署日期、过期日期、状态、签署人、文件URL、文件大小和内容预览。这个方法使用了List.generate来创建列表,每个合同的信息都是根据索引动态生成的。

搜索和过滤功能

搜索功能允许用户按合同名称或合同号搜索已签合同。搜索应该是实时的,用户输入时应该立即显示搜索结果。

dart 复制代码
void _filterContracts() {
  setState(() {
    _filteredContracts = _contracts.where((contract) {
      final matchesSearch = _searchQuery.isEmpty ||
          contract.name.toLowerCase().contains(_searchQuery.toLowerCase()) ||
          contract.contractNumber.toLowerCase().contains(_searchQuery.toLowerCase());

      final matchesCategory = _filterCategory == 'All' ||
          contract.category == _filterCategory;

      final matchesDateRange = _dateRange == null ||
          (contract.signedDate.isAfter(_dateRange!.start) &&
              contract.signedDate.isBefore(_dateRange!.end));

      return matchesSearch && matchesCategory && matchesDateRange;
    }).toList();

    _sortContracts();
  });
}

_filterContracts方法是搜索和过滤功能的核心。它使用where方法来过滤合同列表,根据搜索查询、分类和日期范围进行多条件过滤。搜索是不区分大小写的,用户可以输入合同名称或合同号的任何部分来搜索。过滤完成后,我们调用_sortContracts方法来对结果进行排序。

dart 复制代码
void _sortContracts() {
  if (_sortBy == 'date') {
    _filteredContracts.sort((a, b) => b.signedDate.compareTo(a.signedDate));
  } else if (_sortBy == 'name') {
    _filteredContracts.sort((a, b) => a.name.compareTo(b.name));
  } else if (_sortBy == 'expiry') {
    _filteredContracts.sort((a, b) => a.expiryDate.compareTo(b.expiryDate));
  }
}

_sortContracts方法支持三种排序方式:按签署日期排序(最新优先)、按合同名称排序(字母顺序)和按过期日期排序。这种灵活的排序机制使得用户可以根据自己的需求快速找到所需的合同。

搜索栏UI组件

dart 复制代码
Widget _buildSearchBar() {
  return Container(
    padding: EdgeInsets.all(16.w),
    child: TextField(
      decoration: InputDecoration(
        hintText: 'Search contracts',
        prefixIcon: const Icon(Icons.search),
        suffixIcon: _searchQuery.isNotEmpty
            ? IconButton(
                icon: const Icon(Icons.clear),
                onPressed: () {
                  setState(() {
                    _searchQuery = '';
                    _filterContracts();
                  });
                },
              )
            : null,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8.r),
        ),
        filled: true,
        fillColor: Colors.grey.shade100,
      ),
      onChanged: (value) {
        setState(() {
          _searchQuery = value;
          _filterContracts();
        });
      },
    ),
  );
}

搜索栏使用TextField来实现。当用户输入时,我们调用_filterContracts方法来过滤合同列表。搜索是实时的,用户可以立即看到搜索结果。搜索栏还提供了一个清除按钮,当用户输入了搜索内容时,清除按钮会显示,点击它可以快速清空搜索框。搜索栏的设计简洁而直观,使用了圆角边框和浅灰色背景,与整个应用的设计风格保持一致。

过滤和排序工具栏

dart 复制代码
Widget _buildFilterAndSortBar() {
  return Container(
    padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
    child: Row(
      children: [
        Expanded(
          child: DropdownButton<String>(
            value: _filterCategory,
            isExpanded: true,
            items: ['All', 'Business', 'Legal'].map((category) {
              return DropdownMenuItem(
                value: category,
                child: Text(category),
              );
            }).toList(),
            onChanged: (value) {
              setState(() {
                _filterCategory = value ?? 'All';
                _filterContracts();
              });
            },
          ),
        ),
        SizedBox(width: 8.w),
        PopupMenuButton<String>(
          onSelected: (value) {
            setState(() {
              _sortBy = value;
              _filterContracts();
            });
          },
          itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
            const PopupMenuItem<String>(
              value: 'date',
              child: Text('Sort by Date'),
            ),
            const PopupMenuItem<String>(
              value: 'name',
              child: Text('Sort by Name'),
            ),
            const PopupMenuItem<String>(
              value: 'expiry',
              child: Text('Sort by Expiry'),
            ),
          ],
          child: const Icon(Icons.sort),
        ),
        SizedBox(width: 8.w),
        IconButton(
          icon: const Icon(Icons.calendar_today),
          onPressed: _selectDateRange,
        ),
      ],
    ),
  );
}

过滤和排序栏提供了多种过滤和排序选项。用户可以通过分类下拉菜单按业务或法律分类过滤合同。排序菜单提供了三种排序方式:按签署日期、按合同名称和按过期日期。日期范围选择器允许用户选择一个特定的日期范围,只显示在该范围内签署的合同。这些工具的组合使得用户可以快速定位到他们需要的合同。

dart 复制代码
Future<void> _selectDateRange() async {
  final picked = await showDateRangePicker(
    context: context,
    firstDate: DateTime(2020),
    lastDate: DateTime.now(),
  );

  if (picked != null) {
    setState(() {
      _dateRange = picked;
      _filterContracts();
    });
  }
}

日期范围选择器使用Flutter内置的showDateRangePicker对话框。用户可以选择一个开始日期和结束日期,然后系统会自动过滤出在该范围内签署的合同。这个功能对于查找特定时间段内的合同非常有用。

合同列表构建

dart 复制代码
Widget _buildContractList() {
  if (_filteredContracts.isEmpty) {
    return Center(
      child: Text(
        'No contracts found',
        style: TextStyle(fontSize: 14.sp, color: Colors.grey),
      ),
    );
  }

  return ListView.builder(
    padding: EdgeInsets.all(16.w),
    itemCount: _filteredContracts.length,
    itemBuilder: (context, index) {
      return _buildContractCard(_filteredContracts[index]);
    },
  );
}

合同列表使用ListView.builder来构建列表。这样可以高效地处理大量合同。通过使用itemBuilder,我们可以只渲染可见的项目。当合同列表为空时,我们显示一个友好的提示信息,告诉用户没有找到匹配的合同。这种设计提升了用户体验,避免了显示一个空白的屏幕。ListView.builder是处理动态列表的最佳选择,它可以自动处理列表的滚动和性能优化。

dart 复制代码
void _showMoreOptions(SignedContractModel contract) {
  Get.bottomSheet(
    Container(
      color: Colors.white,
      child: Wrap(
        children: [
          ListTile(
            leading: const Icon(Icons.edit),
            title: const Text('Edit'),
            onTap: () {
              Get.back();
              Get.snackbar('Edit', 'Edit contract functionality');
            },
          ),
          ListTile(
            leading: const Icon(Icons.delete),
            title: const Text('Delete'),
            onTap: () {
              Get.back();
              _deleteContract(contract);
            },
          ),
          ListTile(
            leading: const Icon(Icons.close),
            title: const Text('Cancel'),
            onTap: () => Get.back(),
          ),
        ],
      ),
    ),
  );
}

更多选项菜单显示一个底部菜单,提供了编辑、删除和取消选项。用户可以根据需要选择相应的操作。这个菜单使用了Get.bottomSheet来显示,提供了一个友好的用户界面。

dart 复制代码
void _deleteContract(SignedContractModel contract) {
  Get.dialog(
    AlertDialog(
      title: const Text('Delete Contract'),
      content: const Text('Are you sure you want to delete this contract?'),
      actions: [
        TextButton(
          onPressed: () => Get.back(),
          child: const Text('Cancel'),
        ),
        TextButton(
          onPressed: () {
            Get.back();
            setState(() {
              _contracts.removeWhere((c) => c.id == contract.id);
              _filterContracts();
            });
            Get.snackbar('Success', 'Contract deleted');
          },
          child: const Text('Delete'),
        ),
      ],
    ),
  );
}

删除功能在删除前会显示一个确认对话框,确保用户不会误删合同。这是一个很好的用户体验设计,可以防止用户的误操作。

日期格式化功能

dart 复制代码
String _formatDate(DateTime date) {
  return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

日期格式化功能将日期转换为YYYY-MM-DD格式。这个方法使用了padLeft来确保月份和日期总是两位数字,例如01而不是1。这种格式化方式提供了一致的日期显示,使得用户可以轻松理解日期信息。

页面的完整构建

dart 复制代码
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('Signed Contracts'),
      centerTitle: true,
      elevation: 0,
    ),
    body: _isLoading
        ? const Center(child: CircularProgressIndicator())
        : Column(
            children: [
              _buildSearchBar(),
              _buildFilterAndSortBar(),
              Expanded(
                child: _buildContractList(),
              ),
            ],
          ),
  );
}

完整的页面构建包含了搜索栏、过滤和排序栏以及合同列表。页面使用Column来组织这些元素。当数据正在加载时,我们显示一个加载动画。加载完成后,页面显示搜索栏、过滤和排序工具栏,以及可滚动的合同列表。这种布局设计使得用户可以轻松地搜索、过滤和浏览已签合同。

关键功能说明

这个已签合同功能包含了以下核心功能:

  1. 合同列表展示:清晰地展示所有已签合同
  2. 搜索功能:支持按合同名称或合同号搜索
  3. 过滤功能:支持按分类和日期范围过滤
  4. 排序功能:支持按不同维度排序
  5. 合同操作:支持下载、分享、编辑、删除等操作
  6. 合同详情:点击合同可以查看详细信息

已签合同功能的核心在于为用户提供一个高效的合同管理界面。通过搜索功能,用户可以快速找到特定的合同。通过过滤功能,用户可以按分类或日期范围查看合同。通过排序功能,用户可以按自己的需求调整合同的显示顺序。通过操作功能,用户可以直接在列表中执行常用操作,无需进入详情页面。这些功能的组合使得用户可以高效地管理已签合同。

设计考虑

已签合同功能的设计需要考虑以下几个方面:

  1. 用户体验:提供清晰的列表展示和快速的搜索
  2. 功能完整性:提供搜索、过滤、排序等功能
  3. 操作便利性:提供快速的合同操作入口
  4. 性能优化 :使用ListView.builder来高效处理大量合同
  5. 可访问性:确保所有用户都能理解页面内容

在设计已签合同页面时,我们需要充分考虑用户的使用场景。用户可能需要快速查找特定的合同,因此搜索功能是必不可少的。用户可能需要按不同的维度查看合同,因此过滤和排序功能也很重要。用户可能需要对合同进行各种操作,因此我们提供了快速操作按钮。性能优化对于处理大量合同至关重要,我们使用了ListView.builder来确保应用的流畅性。可访问性设计确保了所有用户,包括有视觉障碍的用户,都能理解和使用这个功能。

总结

这个已签合同功能为用户提供了一个完整的已签合同管理界面。通过提供搜索、过滤、排序和各种操作功能,我们能够帮助用户轻松管理已签合同。用户可以快速查找、查看和操作已签合同。这个功能的实现展示了如何在Flutter中构建一个功能完整、用户友好的列表管理界面。在实际开发中,我们可以根据具体需求进一步扩展这个功能,例如添加合同预览、批量操作、高级搜索等功能。


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

相关推荐
郝学胜-神的一滴14 小时前
计算思维:数字时代的超级能力
开发语言·数据结构·c++·人工智能·python·算法
m0_5312371714 小时前
C语言-数组练习
c语言·开发语言·算法
lili-felicity14 小时前
进阶实战 Flutter for OpenHarmony:image_cropper 第三方库实战 - 图片裁剪
flutter
lili-felicity14 小时前
进阶实战 Flutter for OpenHarmony:battery_plus 第三方库实战 - 电池状态监控
flutter
尘缘浮梦14 小时前
websockets处理流式接口
开发语言·python
lili-felicity14 小时前
进阶实战 Flutter for OpenHarmony:image_gallery_saver 第三方库实战 - 图片保存
flutter
今天你TLE了吗14 小时前
JVM学习笔记:第四章——虚拟机栈
java·jvm·笔记·后端·学习
Coder_Boy_14 小时前
Java高级_资深_架构岗 核心知识点全解析(通俗透彻+理论+实践+最佳实践)
java·spring boot·分布式·面试·架构
识君啊14 小时前
Java 动态规划 - 力扣 零钱兑换与完全平方数 深度解析
java·算法·leetcode·动态规划·状态转移
HoneyMoose14 小时前
Eclipse Temurin JDK 21 ubuntu 安装
java·ubuntu·eclipse