账户管理是理财应用的核心功能之一。用户可能有多个账户:现金、银行卡、信用卡、支付宝、微信等。本篇将实现账户列表页面,展示所有账户和资产概览,让用户对自己的财务状况一目了然。
请添加图片描述

功能设计

账户列表页面包含以下功能:

  1. 资产概览卡片(总资产、净资产、负债)
  2. 账户列表(显示每个账户的余额)
  3. 添加账户入口
  4. 点击账户查看详情
  5. 长按账户显示操作菜单
  6. 滑动删除账户

这种设计让用户既能看到整体资产状况,也能管理单个账户。资产概览放在顶部最显眼的位置,账户列表提供详细的分项信息。

完整代码实现

创建 account_list_page.dart,完整代码如下:

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../core/services/account_service.dart';
import '../../core/services/storage_service.dart';
import '../../data/models/account_model.dart';
import '../../routes/app_pages.dart';

const _primaryColor = Color(0xFF2E7D32);
const _incomeColor = Color(0xFF4CAF50);
const _expenseColor = Color(0xFFE53935);
const _textSecondary = Color(0xFF757575);

class AccountListPage extends StatelessWidget {
  const AccountListPage({super.key});

  
  Widget build(BuildContext context) {
    final accountService = Get.find<AccountService>();
    final storage = Get.find<StorageService>();

这段代码导入了 Flutter 核心库、屏幕适配库 flutter_screenutil、状态管理库 GetX,以及项目内部的服务和模型。AccountService 负责账户数据的增删改查和资产计算,StorageService 提供货币符号等全局设置。四个颜色常量定义了页面的视觉风格:主题绿色用于强调元素,收入绿色和支出红色用于区分交易类型,次要文字灰色用于辅助信息。这些颜色在整个应用中保持一致,形成统一的视觉语言。

    return Scaffold(
      appBar: AppBar(
        title: const Text('账户管理'),
        actions: [
          IconButton(
            icon: const Icon(Icons.add), 
            onPressed: () => Get.toNamed(Routes.accountEdit),
            tooltip: '添加账户',
          ),
        ],
      ),
      body: RefreshIndicator(
        onRefresh: () async {
          accountService.refresh();
        },

这段代码构建了页面的基础结构。Scaffold 是 Material Design 的页面容器,提供了 AppBar 和 body 两个主要区域。AppBar 显示"账户管理"标题,右侧的 IconButton 点击后跳转到账户编辑页面,不传参数表示新增模式。tooltip 属性提供长按提示,增强可访问性。RefreshIndicator 包裹整个页面内容,实现下拉刷新功能,当用户下拉时会调用 accountService.refresh() 刷新数据。这种交互方式符合移动端用户的使用习惯。

        child: SingleChildScrollView(
          physics: const AlwaysScrollableScrollPhysics(),
          padding: EdgeInsets.all(16.w),
          child: Column(
            children: [
              _buildAssetOverview(accountService, storage),
              SizedBox(height: 16.h),
              _buildAccountList(accountService, storage),
            ],
          ),
        ),
      ),
    );
  }

SingleChildScrollView 让整个页面内容可以垂直滚动,适应不同数量的账户。AlwaysScrollableScrollPhysics 是一个重要的设置,它确保即使内容不足一屏高度也能触发下拉刷新,提升用户体验。padding 设为 16.w 在页面四周留出边距,和其他页面保持一致。Column 将页面分为两个主要区域:顶部的资产概览卡片和下方的账户列表,中间用 SizedBox 添加 16.h 的间距。这种布局让信息层次分明,用户首先看到整体资产状况,然后可以查看具体的账户明细。

  Widget _buildAssetOverview(
    AccountService accountService, 
    StorageService storage
  ) {
    return Card(
      color: _primaryColor,
      child: Padding(
        padding: EdgeInsets.all(20.w),
        child: Column(
          children: [
            Text(
              '总资产', 
              style: TextStyle(color: Colors.white70, fontSize: 14.sp)
            ),
            SizedBox(height: 8.h),

资产概览卡片使用主题绿色作为背景色,和白色文字形成强烈的视觉对比,让这个最重要的信息区域非常突出。Card 组件自带圆角和阴影效果,padding 设为 20.w 比普通卡片稍大,让内容有更多的呼吸空间。"总资产"标签使用半透明白色(white70),既清晰可见又不会太抢眼。Column 垂直排列标签、金额和下方的净资产/负债信息。这种设计让用户一打开页面就能立即看到自己的总资产情况,符合用户最关心的信息优先展示的原则。

            Obx(() => Text(
              '${storage.currency}${accountService.totalAssets.toStringAsFixed(2)}',
              style: TextStyle(
                color: Colors.white, 
                fontSize: 28.sp, 
                fontWeight: FontWeight.bold
              )
            )),
            SizedBox(height: 16.h),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                _buildAssetInfo('净资产', accountService.netWorth, storage.currency),
                _buildAssetInfo('负债', accountService.totalLiabilities, storage.currency),
              ],
            ),

Obx 是 GetX 的响应式组件,它会监听 accountService.totalAssets 的变化并自动重建 UI。当用户添加、删除或修改账户时,总资产会自动更新显示。28.sp 是页面上最大的字号,配合 bold 粗体,强调总资产的重要性。toStringAsFixed(2) 保留两位小数,让金额显示更规范专业。Row 组件让净资产和负债水平排列,mainAxisAlignment.spaceAround 让它们在可用空间中均匀分布。净资产 = 总资产 - 负债,对于有信用卡等负债账户的用户来说,这个数字比总资产更有参考价值。

          ],
        ),
      ),
    );
  }

  Widget _buildAssetInfo(String label, double value, String currency) {
    return Column(
      children: [
        Text(label, style: TextStyle(color: Colors.white70, fontSize: 12.sp)),
        SizedBox(height: 4.h),
        Obx(() => Text(
          '$currency${value.toStringAsFixed(2)}', 
          style: TextStyle(
            color: Colors.white, 
            fontSize: 16.sp, 
            fontWeight: FontWeight.w500
          )
        )),
      ],
    );
  }

_buildAssetInfo 方法构建单个资产信息项,采用垂直布局。标签使用小字号(12.sp)和半透明白色(white70),金额使用中等字号(16.sp)和纯白色,这种大小和颜色的对比让金额数字更加突出。fontWeight.w500 是中等粗细,比普通文字稍粗但不会太重。Obx 包裹确保当账户数据变化时,净资产和负债能够自动重新计算并更新显示。这种响应式设计是 GetX 的核心优势,让开发者无需手动管理状态更新,UI 会自动跟随数据变化。

  Widget _buildAccountList(
    AccountService accountService, 
    StorageService storage
  ) {
    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: EdgeInsets.all(16.w),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text('我的账户', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),

账户列表卡片的头部包含标题和账户数量统计。"我的账户"使用 16.sp 字号和 w600 粗细,和其他卡片标题保持一致的样式。Row 组件让标题和账户数量左右分布,mainAxisAlignment.spaceBetween 让它们分别靠左和靠右。账户数量用灰色小字显示,Obx 包裹确保当账户数量变化时能自动更新。Divider 是一条细分隔线,height 设为 1 让它尽量细,视觉上分隔标题和列表内容。这种设计让用户一眼就能看到自己有多少个账户,同时保持界面的整洁。

                Obx(() => Text(
                  '${accountService.allAccounts.length}个',
                  style: TextStyle(fontSize: 14.sp, color: _textSecondary),
                )),
              ],
            ),
          ),
          const Divider(height: 1),
          Obx(() {
            final accounts = accountService.allAccounts;
            if (accounts.isEmpty) {
              return _buildEmptyState();
            }

Obx 包裹整个列表区域,监听 accountService.allAccounts 的变化。当账户列表为空时,显示空状态引导用户添加账户,这比单纯显示"暂无数据"更友好。map 方法遍历账户列表,为每个账户对象调用 _buildAccountItem 方法创建对应的列表项。toList() 将 map 返回的 Iterable 转换为 List,因为 Column 的 children 参数需要 List 类型。空状态使用大尺寸(64.sp)的浅灰色图标,配合提示文字和操作按钮,形成完整的引导流程,帮助新用户快速上手。

            return Column(
              children: accounts.map((account) => 
                _buildAccountItem(account, accountService, storage)
              ).toList(),
            );
          }),
        ],
      ),
    );
  }

  Widget _buildEmptyState() {
    return Padding(
      padding: EdgeInsets.all(32.w),
      child: Column(
        children: [
          Icon(Icons.account_balance_wallet_outlined, size: 64.sp, color: Colors.grey[300]),
          SizedBox(height: 16.h),

空状态的设计非常重要,它不仅告诉用户当前没有数据,更重要的是引导用户如何添加第一个账户。padding 设为 32.w 让空状态内容有足够的视觉空间,不会显得拥挤。Icon 使用 account_balance_wallet_outlined 图标,size 设为 64.sp 足够大能引起注意,浅灰色让它看起来柔和不刺眼。两行提示文字分别说明当前状态和操作方法,字号和颜色有所区别形成层次。ElevatedButton.icon 提供明确的操作入口,点击后跳转到账户编辑页面。这种友好的空状态设计能有效降低用户的学习成本,提升首次使用体验。

          Text('还没有添加账户', style: TextStyle(fontSize: 16.sp, color: _textSecondary)),
          SizedBox(height: 8.h),
          Text(
            '点击右上角 + 添加您的第一个账户',
            style: TextStyle(fontSize: 14.sp, color: _textSecondary),
          ),
          SizedBox(height: 24.h),
          ElevatedButton.icon(
            onPressed: () => Get.toNamed(Routes.accountEdit),
            icon: const Icon(Icons.add),
            label: const Text('添加账户'),
          ),
        ],
      ),
    );
  }

空状态提供了清晰的操作指引和便捷的操作按钮。第一行文字"还没有添加账户"说明当前状态,第二行文字"点击右上角 + 添加您的第一个账户"告诉用户如何操作。ElevatedButton.icon 同时显示图标和文字,提供了第二个添加入口,让用户有多种方式完成操作。这种设计考虑到了不同用户的使用习惯,有的用户习惯点击顶部按钮,有的用户更倾向于点击页面中的大按钮。24.h 的间距让按钮和上方文字保持适当距离,视觉上更舒适。整个空状态的设计遵循了"引导而非阻碍"的原则。

  Widget _buildAccountItem(
    AccountModel account, 
    AccountService accountService,
    StorageService storage
  ) {
    return Dismissible(
      key: Key(account.id),
      direction: DismissDirection.endToStart,
      background: Container(
        alignment: Alignment.centerRight,
        padding: EdgeInsets.only(right: 20.w),
        color: _expenseColor,
        child: const Icon(Icons.delete, color: Colors.white),
      ),

Dismissible 组件实现了滑动删除功能,这是移动端常见的交互方式。key 必须是唯一的,这里使用账户 id 确保每个列表项都有唯一标识。direction 设为 endToStart 表示只允许从右向左滑动,这是 iOS 和 Android 用户都熟悉的删除手势。background 定义了滑动时显示的背景,红色背景配合白色删除图标,清晰地提示用户这是删除操作。alignment.centerRight 让图标显示在右侧,padding 让图标不会紧贴边缘。这种视觉反馈让用户在滑动过程中就能明确知道即将执行的操作。

      confirmDismiss: (_) => _confirmDelete(account),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: account.color.withOpacity(0.2),
          child: Icon(account.icon, color: account.color, size: 20.sp),
        ),
        title: Text(account.name),
        subtitle: Text(_getAccountTypeName(account.type)),
        trailing: Obx(() => Text(
          '${storage.currency}${account.balance.toStringAsFixed(2)}',
          style: TextStyle(
            fontSize: 16.sp, 
            fontWeight: FontWeight.w600,
            color: account.balance >= 0 ? _incomeColor : _expenseColor
          )
        )),

confirmDismiss 是一个重要的安全机制,它在滑动完成前弹出确认对话框,防止用户误删除。只有当用户在对话框中点击确认,返回 true 时,才会真正执行删除操作。ListTile 是 Material Design 的标准列表项组件,它自动处理了点击效果、内边距等细节。leading 位置显示账户图标,CircleAvatar 创建圆形背景,背景色是账户颜色的 20% 透明度,图标使用账户的完整颜色。title 显示账户名称,subtitle 显示账户类型。trailing 显示账户余额,正余额用绿色表示资产,负余额用红色表示负债(如信用卡欠款)。这种颜色编码让用户一眼就能区分账户的财务状况。

        onTap: () => Get.toNamed(Routes.accountDetail, arguments: account),
        onLongPress: () => _showAccountOptions(account, accountService),
      ),
    );
  }

  String _getAccountTypeName(AccountType type) {
    switch (type) {
      case AccountType.cash: return '现金账户';
      case AccountType.bank: return '银行卡';
      case AccountType.creditCard: return '信用卡';
      case AccountType.alipay: return '支付宝';
      case AccountType.wechat: return '微信';
      case AccountType.investment: return '投资账户';
      case AccountType.other: return '其他账户';
    }
  }

ListTile 提供了两种交互方式:onTap 和 onLongPress。onTap 点击跳转到账户详情页面,通过 arguments 参数传递完整的账户对象,详情页可以获取并显示账户的所有信息。onLongPress 长按显示操作菜单,提供编辑、调整余额、查看交易记录、删除等更多操作选项。这种设计让常用操作(查看详情)只需一次点击,而不常用的操作(编辑、删除)需要长按,既提高了效率又防止了误操作。_getAccountTypeName 方法使用 switch 语句根据枚举值返回对应的中文名称,使用枚举而不是字符串可以避免拼写错误,也方便 IDE 提供代码补全和类型检查。

  void _showAccountOptions(AccountModel account, AccountService accountService) {
    Get.bottomSheet(
      Container(
        padding: EdgeInsets.all(16.w),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
        ),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              width: 40.w,
              height: 4.h,
              decoration: BoxDecoration(
                color: Colors.grey[300],
                borderRadius: BorderRadius.circular(2.r),
              ),
            ),

_showAccountOptions 方法使用 Get.bottomSheet 显示底部弹窗,这是移动端常见的交互模式。Container 设置白色背景和顶部圆角(16.r),BorderRadius.vertical 只设置顶部圆角,底部保持直角贴合屏幕边缘。顶部的小横条(40.w × 4.h)是拖动指示器,使用浅灰色,暗示用户可以通过下拉手势关闭弹窗。mainAxisSize.min 让弹窗高度自适应内容,不会占据整个屏幕。头部显示账户名称,使用 18.sp 字号和粗体,让用户明确知道当前操作的是哪个账户,避免误操作。这种设计既美观又实用,符合现代移动应用的交互规范。

            SizedBox(height: 16.h),
            Text(account.name, style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold)),
            SizedBox(height: 16.h),
            ListTile(
              leading: const Icon(Icons.edit),
              title: const Text('编辑账户'),
              onTap: () {
                Get.back();
                Get.toNamed(Routes.accountEdit, arguments: account);
              },
            ),
            ListTile(
              leading: const Icon(Icons.sync),
              title: const Text('调整余额'),

弹窗提供了四个操作选项,每个选项都使用 ListTile 组件,保持一致的样式和交互。编辑选项点击后先调用 Get.back() 关闭弹窗,再跳转到账户编辑页面,通过 arguments 传递账户对象。这个顺序很重要,如果先跳转再关闭,会导致页面叠加。调整余额功能让用户可以手动修正账户余额,这在实际使用中很有用,比如银行卡余额和实际不符时,用户可以快速调整而不需要添加交易记录。查看交易记录跳转到交易列表页面,通过 arguments 传递 accountId 参数,列表页会根据这个参数筛选显示该账户的所有交易。这种设计让用户可以快速查看账户的详细使用情况。

              onTap: () {
                Get.back();
                _showAdjustBalanceDialog(account, accountService);
              },
            ),
            ListTile(
              leading: const Icon(Icons.history),
              title: const Text('查看交易记录'),
              onTap: () {
                Get.back();
                Get.toNamed(Routes.transactionList, arguments: {'accountId': account.id});
              },
            ),
            ListTile(
              leading: Icon(Icons.delete, color: _expenseColor),
              title: Text('删除账户', style: TextStyle(color: _expenseColor)),

删除选项使用红色图标和文字,在视觉上强调这是一个危险操作,提醒用户谨慎操作。点击删除后的流程是:先关闭弹窗,然后调用 _confirmDelete 显示确认对话框,等待用户确认。使用 await 关键字等待对话框返回结果,如果用户点击确认(返回 true),才调用 accountService.deleteAccount 真正删除账户,并显示成功提示。这种多层确认机制(长按 → 点击删除 → 确认对话框)有效防止了误删除,因为删除账户是不可逆的操作,一旦删除,相关的交易记录也会失去账户关联。这种设计体现了"防错"原则,在用户可能犯错的地方提供多重保护。

              onTap: () async {
                Get.back();
                final confirmed = await _confirmDelete(account);
                if (confirmed == true) {
                  accountService.deleteAccount(account.id);
                  Get.snackbar('成功', '账户已删除');
                }
              },
            ),
            SizedBox(height: 16.h),
          ],
        ),
      ),
    );
  }

删除操作需要二次确认,这是处理危险操作的最佳实践。_confirmDelete 方法返回 Future<bool?>,通过 Get.dialog 显示确认对话框并等待用户选择。content 部分详细说明了删除的后果:“删除后相关的交易记录将失去账户关联”,让用户在做出决定前充分了解操作的影响。这种"知情同意"的设计原则非常重要,特别是对于不可逆的操作。取消按钮返回 false 表示不删除,确认按钮返回 true 表示确认删除。确认按钮使用红色文字,再次强调这是危险操作。Get.back(result: true/false) 在关闭对话框的同时返回结果,调用方可以根据这个结果决定是否执行删除。

  Future<bool?> _confirmDelete(AccountModel account) {
    return Get.dialog<bool>(
      AlertDialog(
        title: const Text('删除账户'),
        content: Text(
          '确定要删除账户"${account.name}"吗?\n\n'
          '注意:删除后相关的交易记录将失去账户关联。'
        ),
        actions: [
          TextButton(
            onPressed: () => Get.back(result: false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Get.back(result: true),
            child: Text('删除', style: TextStyle(color: _expenseColor)),
          ),
        ],
      ),
    );
  }

删除确认对话框使用 AlertDialog 组件,这是 Material Design 的标准对话框样式。content 使用多行文本,第一行说明要删除的账户名称,第二行用换行符分隔后说明删除的后果。这种分层次的信息展示让用户能够快速理解操作的影响。actions 数组包含两个按钮,取消按钮使用 TextButton 样式,点击后返回 false 表示取消操作。确认按钮也使用 TextButton 但文字颜色设为红色,点击后返回 true 表示确认删除。这个返回值会被 confirmDismiss 或长按菜单的删除选项接收,根据返回值决定是否真正执行删除。整个流程体现了"防错"和"容错"的设计原则。

  void _showAdjustBalanceDialog(AccountModel account, AccountService accountService) {
    final controller = TextEditingController(text: account.balance.toStringAsFixed(2));
    final storage = Get.find<StorageService>();
    
    Get.dialog(
      AlertDialog(
        title: const Text('调整余额'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('当前余额: ${storage.currency}${account.balance.toStringAsFixed(2)}'),
            SizedBox(height: 16.h),

调整余额对话框让用户可以手动修正账户余额,这在实际使用中非常实用。TextEditingController 初始化为当前余额(保留两位小数),用户可以在此基础上修改。Column 的 mainAxisSize 设为 min 让对话框高度自适应内容,crossAxisAlignment 设为 start 让内容左对齐。第一行显示当前余额,让用户知道原来的值是多少,方便对比。TextField 的 keyboardType 设置为 numberWithOptions,decimal: true 支持小数输入,signed: true 支持负数输入(信用卡余额可能是负数)。prefixText 显示货币符号,OutlineInputBorder 提供带边框的输入框样式,视觉上更清晰。

            TextField(
              controller: controller,
              keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true),
              decoration: InputDecoration(
                labelText: '新余额',
                prefixText: storage.currency,
                border: const OutlineInputBorder(),
              ),
            ),
          ],
        ),
        actions: [
          TextButton(onPressed: () => Get.back(), child: const Text('取消')),
          ElevatedButton(
            onPressed: () {
              final newBalance = double.tryParse(controller.text);

确定按钮的点击处理包含了输入验证逻辑。double.tryParse 尝试将输入文本解析为数字,如果解析成功返回数字,失败返回 null。这种方式比 double.parse 更安全,因为后者在解析失败时会抛出异常。验证通过后,调用 accountService.updateBalance 更新账户余额,这个方法会使用 copyWith 创建新的账户对象,保持数据的不可变性。然后关闭对话框并显示成功提示。如果验证失败(用户输入了非数字内容),显示错误提示但不关闭对话框,让用户可以重新输入。这种即时反馈的设计让用户能够快速纠正错误,提升了用户体验。整个流程体现了"容错"原则,即使用户输入错误也能优雅地处理。

              if (newBalance != null) {
                accountService.updateBalance(account.id, newBalance);
                Get.back();
                Get.snackbar('成功', '余额已更新');
              } else {
                Get.snackbar('错误', '请输入有效金额');
              }
            },
            child: const Text('确定'),
          ),
        ],
      ),
    );
  }
}

确定按钮验证输入是否为有效数字,验证通过后更新余额并关闭对话框。验证失败显示错误提示但不关闭对话框。

AccountService 实现

账户服务管理账户数据和计算资产:

class AccountService extends GetxService {
  final _accounts = <AccountModel>[].obs;
  
  List<AccountModel> get allAccounts => _accounts;
  
  double get totalAssets => _accounts
    .where((a) => a.balance > 0)
    .fold(0.0, (sum, a) => sum + a.balance);

_accounts 是响应式列表,数据变化时会自动通知监听者。allAccounts 是只读属性,返回账户列表供外部使用。totalAssets 计算所有正余额账户的总和,where 筛选正余额账户,fold 累加金额。这种响应式设计让 UI 能自动更新。

负债和净资产计算:

  double get totalLiabilities => _accounts
    .where((a) => a.balance < 0)
    .fold(0.0, (sum, a) => sum + a.balance.abs());
    
  double get netWorth => totalAssets - totalLiabilities;
  
  AccountModel? getAccountById(String id) => 
    _accounts.firstWhereOrNull((a) => a.id == id);

totalLiabilities 计算所有负余额账户的绝对值总和,主要是信用卡等负债账户。netWorth 是净资产,等于总资产减去负债,这个数字对用户来说更有参考价值。getAccountById 根据 id 查找账户,找不到返回 null,使用 firstWhereOrNull 避免抛出异常。

账户增删改方法:

  void addAccount(AccountModel account) {
    _accounts.add(account);
  }
  
  void updateAccount(AccountModel account) {
    final index = _accounts.indexWhere((a) => a.id == account.id);
    if (index != -1) {
      _accounts[index] = account;
    }
  }

addAccount 添加新账户到列表。updateAccount 更新已有账户,先用 indexWhere 查找索引,找到后替换整个对象。这种方式确保响应式列表能检测到变化并通知监听者更新 UI。

删除和更新余额方法:

  void deleteAccount(String id) {
    _accounts.removeWhere((a) => a.id == id);
  }
  
  void updateBalance(String id, double newBalance) {
    final index = _accounts.indexWhere((a) => a.id == id);
    if (index != -1) {
      _accounts[index] = _accounts[index].copyWith(balance: newBalance);
    }
  }
  
  void refresh() {
    _accounts.refresh();
  }
}

deleteAccount 根据 id 删除账户。updateBalance 更新指定账户的余额,使用 copyWith 创建新对象保持不可变性。refresh 方法触发列表刷新,用于下拉刷新场景。

设计要点总结

账户列表的设计考虑了以下几点:

  1. 资产概览放在顶部,一目了然,用户最关心的信息最先看到
  2. 使用颜色区分正负余额,绿色表示正余额,红色表示负余额
  3. 每个账户有独特的图标和颜色,方便识别
  4. 点击查看详情,长按显示更多选项,滑动可以删除
  5. 空状态引导用户添加账户,不会让用户感到困惑
  6. 调整余额功能方便用户手动修正数据

小结

账户列表页面让用户可以全面了解自己的资产状况。通过资产概览卡片,用户可以快速看到总资产、净资产和负债。账户列表展示每个账户的详细信息,支持多种操作方式。下一篇将实现账户详情页面,展示单个账户的完整信息和交易记录。


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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐