在这里插入图片描述

收藏功能是现代应用中非常常见的特性,它让用户可以标记自己喜欢的内容,方便日后快速访问。在游戏中心应用中,收藏功能让玩家可以将自己喜欢的游戏添加到收藏列表,不需要在众多游戏中寻找。这种个性化的功能可以提升用户体验,增加应用的粘性。本文将详细介绍收藏游戏页面的实现,包括收藏列表展示、空状态处理、取消收藏等功能。

收藏功能的设计思路

收藏功能的核心是让用户能够快速访问自己喜欢的内容。设计时需要考虑几个关键点:收藏操作要简单直观,最好一键完成;收藏状态要清晰可见,用户应该能够一眼看出哪些游戏已收藏;取消收藏也要方便,让用户可以随时调整收藏列表。

我们的收藏页面采用列表式布局,每个收藏的游戏显示为一个卡片。卡片包含游戏图标、名称、游玩次数等信息,右侧有一个红心图标表示已收藏状态。点击红心可以取消收藏,这种交互方式简单直观,符合用户的使用习惯。

当收藏列表为空时,显示一个友好的空状态提示,告诉用户可以去收藏游戏。这种引导性的设计比简单的"暂无数据"要好得多,给用户明确的行动指引。

页面组件的定义

FavoritesPage是一个无状态组件,负责展示收藏的游戏列表。

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

  
  Widget build(BuildContext context) {

使用StatelessWidget让组件保持简单。收藏数据的管理可以通过状态管理方案来处理,页面本身只负责展示。这种设计符合单一职责原则,让代码更容易理解和维护。

const构造函数表示这个Widget是编译时常量,可以提高性能。super.key传递给父类,用于Widget的标识。虽然这些都是基础知识,但正确使用它们可以让应用运行得更加流畅。

在实际应用中,收藏数据应该从数据库或状态管理中获取。这里我们先使用模拟数据来展示页面效果,后续可以很容易地替换为真实数据。

收藏数据的定义

我们定义一组模拟的收藏游戏数据,展示不同游戏的收藏信息。

    final favorites = [
      {'name': '拼图游戏', 'icon': '🧩', 'plays': 45},
      {'name': '记忆翻牌', 'icon': '🎴', 'plays': 32},
      {'name': '数字消除', 'icon': '🔢', 'plays': 28},
    ];

每个收藏项是一个Map,包含游戏名称、图标和游玩次数。名称用于显示游戏的标题,图标使用emoji让界面更加生动,游玩次数显示用户对这个游戏的喜爱程度。

使用List来存储收藏数据是一个简单有效的方式。List保持了收藏的顺序,可以按照收藏时间或其他规则排序。在实际应用中,这个列表应该从数据库查询得到,每次用户收藏或取消收藏时更新数据库。

游玩次数是一个有用的信息,它可以帮助用户了解自己对某个游戏的投入程度。经常玩的游戏说明用户真的喜欢它,这个数据也可以用于推荐算法,向用户推荐类似的游戏。

页面框架的构建

页面使用Scaffold作为基本框架,包含AppBar和body两部分。

    return Scaffold(
      appBar: AppBar(
        title: const Text('收藏游戏'),
        backgroundColor: const Color(0xFF16213e),
      ),

Scaffold提供了标准的Material Design页面结构。AppBar显示页面标题"收藏游戏",让用户清楚地知道当前浏览的内容。

backgroundColor设置为深蓝色,与应用的整体主题保持一致。这种一致性让应用看起来更加专业,用户在不同页面之间切换时不会感到突兀。

const关键字用于Text,因为标题是固定的。这些小的优化累积起来,可以让应用的性能得到提升。虽然单个const的影响很小,但在整个应用中大量使用,效果就会很明显。

AppBar左侧会自动显示返回按钮,这是Scaffold提供的默认行为。用户可以点击返回按钮回到上一个页面,这种标准的导航方式符合用户的使用习惯。

空状态的处理

当收藏列表为空时,显示一个友好的空状态提示。

      body: favorites.isEmpty
          ? Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.favorite_border, size: 80.sp, color: Colors.white30),
                  SizedBox(height: 20.h),
                  Text('还没有收藏游戏', style: TextStyle(fontSize: 16.sp, color: Colors.white60)),
                ],
              ),
            )

body部分使用三元运算符根据收藏列表是否为空来决定显示什么内容。如果列表为空,显示空状态提示;如果有收藏,显示收藏列表。

空状态使用Center组件将内容居中显示,Column垂直排列图标和文字。mainAxisAlignment设置为center,让内容在垂直方向上也居中。这样整个空状态提示就会出现在屏幕的正中央。

图标使用Material Icons中的favorite_border,这是一个空心的爱心图标,与收藏的主题完美契合。size设置为80.sp,这是一个很大的尺寸,让图标成为视觉焦点。color设置为半透明的白色,表明这是一个空状态,不是主要内容。

SizedBox添加了20.h的垂直间距,将图标和文字分开。适当的间距让界面更加舒适,不会显得拥挤。

文字提示"还没有收藏游戏"简洁明了,告诉用户当前的状态。fontSize设置为16.sp,color设置为半透明的白色。这种友好的提示比简单的"暂无数据"要好得多,让用户明白这是正常的状态,不是错误。

收藏列表的构建

当有收藏游戏时,使用ListView展示收藏列表。

          : ListView.builder(
              padding: EdgeInsets.all(16.w),
              itemCount: favorites.length,
              itemBuilder: (context, index) {
                final game = favorites[index];

ListView.builder是构建列表的高效方式,它只会渲染可见区域的列表项。这种懒加载的方式比一次性构建所有列表项要高效得多,特别是当收藏游戏很多时。

padding设置为EdgeInsets.all(16.w),在列表四周添加16个设计稿单位的内边距。这样列表内容不会紧贴屏幕边缘,看起来更加舒适。使用flutter_screenutil的适配单位,确保在不同设备上显示一致。

itemCount设置为收藏列表的长度,itemBuilder为每个收藏游戏创建一个Widget。在回调函数中,我们首先获取当前索引对应的游戏数据,然后构建游戏卡片。

收藏卡片的设计

每个收藏游戏显示为一个卡片,包含游戏图标、名称、游玩次数和收藏按钮。

                return Container(
                  margin: EdgeInsets.only(bottom: 12.h),
                  padding: EdgeInsets.all(16.w),
                  decoration: BoxDecoration(
                    color: const Color(0xFF16213e),
                    borderRadius: BorderRadius.circular(12.r),
                  ),

Container是卡片的容器,margin设置了底部间距12.h,让相邻的卡片之间有一定的间隔。这种间隔让列表看起来不会太拥挤,每个卡片都有自己的空间。

padding设置了内边距16.w,让卡片内的内容不会紧贴边缘。适当的内边距可以让内容更加舒适,提升视觉体验。

decoration定义了容器的装饰样式。color设置为深蓝色,与AppBar的颜色一致,形成了统一的视觉风格。borderRadius设置为12.r,创建了圆角效果。圆角让卡片看起来更加柔和,符合现代UI设计的趋势。

BoxDecoration是Flutter中非常强大的装饰类,除了颜色和圆角,还可以设置边框、阴影、渐变等效果。这里我们只使用了基本的颜色和圆角,保持了简洁的设计风格。

卡片内容的布局

卡片内容使用Row水平排列,从左到右依次是游戏图标、游戏信息和收藏按钮。

                  child: Row(
                    children: [
                      Text(game['icon'] as String, style: TextStyle(fontSize: 48.sp)),
                      SizedBox(width: 16.w),

Row组件水平排列子Widget。第一个子元素是游戏图标,使用Text显示emoji。fontSize设置为48.sp,这是一个比较大的尺寸,让图标清晰可见。

使用emoji作为游戏图标是一个巧妙的设计。emoji不需要准备图片资源,在所有平台上都有统一的显示效果,而且非常直观。不同的游戏使用不同的emoji,让用户可以快速识别游戏类型。

SizedBox添加了16.w的水平间距,将图标和文字信息分开。适当的间距让布局更加清晰,不会显得拥挤。使用SizedBox创建间距是Flutter中的标准做法,比使用Padding更加简洁。

游戏信息的展示

游戏信息包括名称和游玩次数,使用Column垂直排列。Expanded让这部分内容占据剩余的水平空间。

                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(game['name'] as String, style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold)),
                            SizedBox(height: 4.h),
                            Text('已玩 ${game['plays']} 次', style: TextStyle(fontSize: 14.sp, color: Colors.white60)),
                          ],
                        ),
                      ),

Expanded是一个非常有用的布局组件,它会让子Widget占据父Widget中剩余的空间。在Row中使用Expanded,可以让某个子Widget自动填充剩余的水平空间。这样无论屏幕宽度如何,游戏信息都会占据图标和按钮之间的所有空间。

Column垂直排列游戏名称和游玩次数。crossAxisAlignment设置为start,让文本左对齐。这是文本内容的标准对齐方式,符合阅读习惯。

游戏名称使用18.sp的字号和粗体,让它醒目突出。这是最重要的信息,应该最先被用户注意到。较大的字号和粗体让名称非常清晰,即使在快速滚动列表时也能看清。

SizedBox添加了4.h的垂直间距,将名称和游玩次数分开。这个间距比较小,因为这两个信息是紧密相关的,不需要太大的分隔。

游玩次数使用字符串插值将数字嵌入到文本中。fontSize设置为14.sp,比名称小一些,表明这是次要信息。color设置为半透明的白色,让它看起来更柔和。这个信息告诉用户对这个游戏的投入程度,经常玩的游戏说明用户真的喜欢它。

收藏按钮的实现

卡片右侧是一个收藏按钮,显示为红色的实心爱心图标。

                      IconButton(
                        icon: const Icon(Icons.favorite, color: Colors.red),
                        onPressed: () {},
                      ),
                    ],
                  ),
                );
              },
            ),
    );
  }
}

IconButton是一个可点击的图标按钮。icon使用Material Icons中的favorite图标,这是一个实心的爱心,表示已收藏状态。color设置为红色,这是爱心的经典颜色,让收藏状态非常明显。

onPressed回调处理点击事件。在实际应用中,这里应该调用取消收藏的方法,更新数据库和UI状态。当前代码中是一个空函数,后续可以替换为实际的逻辑。

红色的实心爱心是收藏功能的通用视觉语言,用户看到这个图标就知道这是已收藏的内容。点击这个按钮可以取消收藏,这种交互方式简单直观,不需要额外的说明。

收藏状态的管理

在实际应用中,收藏状态需要持久化存储,并且要在多个页面之间同步。

可以使用状态管理方案(如GetX)来管理收藏状态:

class FavoritesController extends GetxController {
  final RxList<String> favoriteIds = <String>[].obs;
  
  bool isFavorite(String gameId) {
    return favoriteIds.contains(gameId);
  }
  
  Future<void> toggleFavorite(String gameId) async {
    if (isFavorite(gameId)) {
      favoriteIds.remove(gameId);
      await _removeFavoriteFromDb(gameId);
    } else {
      favoriteIds.add(gameId);
      await _addFavoriteToDb(gameId);
    }
  }
  
  Future<void> _addFavoriteToDb(String gameId) async {
    // 将收藏添加到数据库
    await db.insert('favorites', {'game_id': gameId, 'created_at': DateTime.now().toIso8601String()});
  }
  
  Future<void> _removeFavoriteFromDb(String gameId) async {
    // 从数据库删除收藏
    await db.delete('favorites', where: 'game_id = ?', whereArgs: [gameId]);
  }
}

这个控制器管理收藏状态,favoriteIds是一个响应式列表,保存所有已收藏游戏的id。isFavorite方法检查某个游戏是否已收藏,toggleFavorite方法切换收藏状态。

使用响应式变量的好处是,当收藏状态改变时,所有使用这个状态的Widget都会自动更新。比如游戏详情页的收藏按钮、收藏列表页、游戏大厅的收藏标记,都会同步更新,不需要手动刷新。

数据库操作使用异步方法,确保不会阻塞UI线程。insert方法添加收藏记录,delete方法删除收藏记录。这些操作完成后,收藏状态就持久化了,即使应用关闭也不会丢失。

收藏按钮的交互优化

点击收藏按钮时,应该有明确的视觉反馈,让用户知道操作已经生效。

可以添加一个动画效果,比如爱心的缩放动画:

class FavoriteButton extends StatefulWidget {
  final bool isFavorite;
  final VoidCallback onPressed;
  
  const FavoriteButton({super.key, required this.isFavorite, required this.onPressed});
  
  
  State<FavoriteButton> createState() => _FavoriteButtonState();
}

class _FavoriteButtonState extends State<FavoriteButton> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scaleAnimation;
  
  
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 200),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 1.3).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }
  
  
  Widget build(BuildContext context) {
    return ScaleTransition(
      scale: _scaleAnimation,
      child: IconButton(
        icon: Icon(
          widget.isFavorite ? Icons.favorite : Icons.favorite_border,
          color: widget.isFavorite ? Colors.red : Colors.white60,
        ),
        onPressed: () {
          _controller.forward().then((_) => _controller.reverse());
          widget.onPressed();
        },
      ),
    );
  }
  
  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

这个自定义的收藏按钮包含了缩放动画。点击时,爱心会先放大到1.3倍,然后恢复原始大小。这种动画反馈让交互更加生动,用户可以清楚地感受到操作已经生效。

根据收藏状态显示不同的图标:已收藏显示实心爱心,未收藏显示空心爱心。颜色也不同:已收藏是红色,未收藏是半透明的白色。这种视觉区分让收藏状态一目了然。

收藏列表的排序

收藏列表可以提供多种排序方式,让用户按照自己的需求查看收藏。

常见的排序方式包括:按收藏时间排序(最新收藏的在前)、按游玩次数排序(最常玩的在前)、按游戏名称排序(字母顺序)。

可以在AppBar中添加一个排序按钮,点击时弹出排序选项菜单:

actions: [
  PopupMenuButton<String>(
    icon: const Icon(Icons.sort),
    onSelected: (value) {
      setState(() {
        _sortType = value;
        _sortFavorites();
      });
    },
    itemBuilder: (context) => [
      const PopupMenuItem(value: 'time', child: Text('按收藏时间')),
      const PopupMenuItem(value: 'plays', child: Text('按游玩次数')),
      const PopupMenuItem(value: 'name', child: Text('按名称')),
    ],
  ),
],

PopupMenuButton创建一个弹出菜单按钮,点击时显示排序选项。onSelected回调在用户选择某个选项时触发,更新排序类型并重新排序列表。

排序的实现:

void _sortFavorites() {
  switch (_sortType) {
    case 'time':
      favorites.sort((a, b) => b['createdAt'].compareTo(a['createdAt']));
      break;
    case 'plays':
      favorites.sort((a, b) => b['plays'].compareTo(a['plays']));
      break;
    case 'name':
      favorites.sort((a, b) => a['name'].compareTo(b['name']));
      break;
  }
}

这个方法根据排序类型对收藏列表进行排序。sort方法接收一个比较函数,返回负数表示a在b前面,返回正数表示b在a前面。compareTo方法比较两个值,实现升序或降序排序。

批量管理功能

当收藏游戏很多时,用户可能需要批量管理功能,比如批量取消收藏。

可以添加一个编辑模式,进入编辑模式后,每个列表项前面显示一个复选框。用户可以选择多个游戏,然后点击删除按钮批量取消收藏。

编辑模式的实现与历史记录页面类似,使用一个布尔变量标记是否处于编辑模式,使用一个Set保存选中的游戏id。在AppBar中添加编辑按钮和删除按钮,在列表项中添加复选框。

批量取消收藏的实现:

Future<void> _removeSelected() async {
  final confirmed = await _showConfirmDialog('确定要取消收藏选中的${_selectedIds.length}个游戏吗?');
  if (confirmed) {
    for (final id in _selectedIds) {
      await favoritesController.toggleFavorite(id);
    }
    setState(() {
      favorites.removeWhere((game) => _selectedIds.contains(game['id']));
      _selectedIds.clear();
      _isEditMode = false;
    });
  }
}

这个方法先显示确认对话框,确认后遍历选中的id,逐个取消收藏。然后更新UI状态,移除已取消收藏的游戏,清空选中集合,退出编辑模式。

收藏数据的持久化

收藏数据需要持久化存储,确保应用关闭后数据不会丢失。可以使用SQLite数据库来存储收藏信息。

首先定义收藏表的结构:

class FavoritesDatabase {
  static const String tableName = 'favorites';
  
  static Future<void> createTable(Database db) async {
    await db.execute('''
      CREATE TABLE $tableName (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        game_id TEXT NOT NULL,
        game_name TEXT NOT NULL,
        game_icon TEXT NOT NULL,
        created_at TEXT NOT NULL,
        UNIQUE(game_id)
      )
    ''');
  }
}

这个表包含了收藏的基本信息:游戏id、名称、图标和收藏时间。game_id字段设置了UNIQUE约束,确保同一个游戏不会被重复收藏。created_at字段记录收藏时间,可以用于按时间排序。

数据库的设计要考虑查询效率。如果经常需要查询某个游戏是否已收藏,可以在game_id字段上创建索引。如果需要按收藏时间排序,可以在created_at字段上创建索引。这些索引可以显著提升查询性能,特别是当收藏数量很多时。

收藏操作的实现

基于数据库,实现收藏和取消收藏的操作:

class FavoritesService {
  final Database db;
  
  FavoritesService(this.db);
  
  Future<void> addFavorite(String gameId, String gameName, String gameIcon) async {
    await db.insert(
      FavoritesDatabase.tableName,
      {
        'game_id': gameId,
        'game_name': gameName,
        'game_icon': gameIcon,
        'created_at': DateTime.now().toIso8601String(),
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }
  
  Future<void> removeFavorite(String gameId) async {
    await db.delete(
      FavoritesDatabase.tableName,
      where: 'game_id = ?',
      whereArgs: [gameId],
    );
  }
}

addFavorite方法将游戏添加到收藏表。conflictAlgorithm设置为replace,如果游戏已存在则替换,避免重复收藏。created_at使用ISO 8601格式的时间字符串,这是一个标准的时间格式,便于排序和比较。

removeFavorite方法从收藏表删除游戏。使用where子句指定删除条件,whereArgs传递参数值。这种参数化查询可以防止SQL注入攻击,是安全的做法。即使游戏id包含特殊字符,也不会导致SQL语句错误或安全问题。

这两个方法都是异步的,使用async和await关键字。数据库操作通常比较耗时,使用异步可以避免阻塞UI线程,保持应用的流畅性。

查询收藏列表

实现查询所有收藏和检查收藏状态的方法:

  Future<List<Map<String, dynamic>>> getAllFavorites() async {
    return await db.query(
      FavoritesDatabase.tableName,
      orderBy: 'created_at DESC',
    );
  }
  
  Future<bool> isFavorite(String gameId) async {
    final result = await db.query(
      FavoritesDatabase.tableName,
      where: 'game_id = ?',
      whereArgs: [gameId],
    );
    return result.isNotEmpty;
  }

getAllFavorites方法查询所有收藏,按收藏时间降序排列。这样最新收藏的游戏会显示在列表顶部,符合用户的使用习惯。orderBy参数指定排序字段和顺序,DESC表示降序。

isFavorite方法检查某个游戏是否已收藏。查询结果不为空说明游戏已收藏,返回true;否则返回false。这个方法可以用于在游戏详情页显示收藏状态,让用户知道这个游戏是否已经收藏过。

这些查询方法返回的数据类型是List<Map<String, dynamic>>,这是SQLite查询的标准返回类型。每个Map代表一行数据,键是列名,值是列值。

收藏列表的加载

页面加载时,需要从数据库查询收藏列表并显示:

class FavoritesPage extends StatefulWidget {
  const FavoritesPage({super.key});

  
  State<FavoritesPage> createState() => _FavoritesPageState();
}

class _FavoritesPageState extends State<FavoritesPage> {
  List<Map<String, dynamic>> _favorites = [];
  bool _isLoading = true;

  
  void initState() {
    super.initState();
    _loadFavorites();
  }

  Future<void> _loadFavorites() async {
    setState(() {
      _isLoading = true;
    });
    
    final service = FavoritesService(await DatabaseHelper.database);
    final favorites = await service.getAllFavorites();
    
    setState(() {
      _favorites = favorites;
      _isLoading = false;
    });
  }
}

现在FavoritesPage改为StatefulWidget,因为需要管理收藏列表的状态。_favorites保存收藏数据,_isLoading标记是否正在加载。使用下划线前缀表示这是私有变量,只在当前类中使用。

initState方法在页面初始化时调用_loadFavorites加载收藏列表。这是一个异步操作,先设置_isLoading为true显示加载指示器,查询完成后更新_favorites并设置_isLoading为false。

setState方法通知Flutter框架状态已改变,需要重新构建Widget。每次修改状态变量后都要调用setState,否则UI不会更新。这是Flutter状态管理的基本原则。

加载状态的显示

根据加载状态显示不同的内容:

  
  Widget build(BuildContext context) {
    if (_isLoading) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('收藏游戏'),
          backgroundColor: const Color(0xFF16213e),
        ),
        body: const Center(child: CircularProgressIndicator()),
      );
    }
    
    // 显示收藏列表...
  }

build方法根据加载状态显示不同的内容。如果正在加载,显示CircularProgressIndicator;加载完成后显示收藏列表。这种加载状态的处理让用户体验更好,不会看到空白页面或闪烁。

CircularProgressIndicator是Material Design的标准加载指示器,显示为一个旋转的圆圈。使用Center组件将它居中显示,让用户清楚地看到应用正在加载数据。

这种加载状态的处理是移动应用的标准做法。用户启动页面时,立即看到加载指示器,知道应用正在工作。加载完成后,平滑地过渡到实际内容,整个过程流畅自然。

取消收藏的确认对话框

取消收藏是一个破坏性操作,应该有确认对话框,避免用户误操作:

Future<bool> _showRemoveConfirmDialog(String gameName) async {
  return await showDialog<bool>(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('取消收藏'),
      content: Text('确定要取消收藏"$gameName"吗?'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context, false),
          child: const Text('取消'),
        ),
        TextButton(
          onPressed: () => Navigator.pop(context, true),
          child: const Text('确定', style: TextStyle(color: Colors.red)),
        ),
      ],
    ),
  ) ?? false;
}

这个方法显示一个确认对话框,询问用户是否确定取消收藏。对话框包含标题、内容和两个按钮。内容使用字符串插值显示游戏名称,让用户清楚地知道要取消收藏哪个游戏。

取消按钮返回false,确定按钮返回true。使用Navigator.pop传递返回值,调用方可以根据返回值决定是否执行取消收藏操作。这种模式让对话框的使用非常灵活,可以在任何需要确认的地方使用。

确定按钮的文字使用红色,表明这是一个破坏性操作。这种视觉提示可以让用户更加谨慎,减少误操作的可能性。红色在UI设计中通常表示警告或危险,用户看到红色会本能地更加小心。

收藏按钮的完整实现

结合确认对话框,实现完整的取消收藏功能:

IconButton(
  icon: const Icon(Icons.favorite, color: Colors.red),
  onPressed: () async {
    final confirmed = await _showRemoveConfirmDialog(game['game_name'] as String);
    if (confirmed) {
      final service = FavoritesService(await DatabaseHelper.database);
      await service.removeFavorite(game['game_id'] as String);
      _loadFavorites();
      
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('已取消收藏"${game['game_name']}"'),
          duration: const Duration(seconds: 2),
        ),
      );
    }
  },
),

点击收藏按钮时,先显示确认对话框。如果用户确认,调用removeFavorite方法从数据库删除收藏,然后重新加载列表更新UI。这个过程是异步的,使用await等待每个操作完成。

操作完成后显示一个SnackBar提示用户操作成功。SnackBar是一个轻量级的提示,会在屏幕底部短暂显示然后自动消失。duration设置为2秒,这是一个合适的时长,既能让用户看到提示,又不会打扰用户太久。

这种完整的交互流程包含了确认、执行、反馈三个步骤,让用户清楚地知道操作的每个阶段。确认避免误操作,执行完成实际功能,反馈告知操作结果。这是优秀用户体验的标准模式。

下拉刷新功能

收藏列表可以添加下拉刷新功能,让用户可以手动刷新列表:

RefreshIndicator(
  onRefresh: _loadFavorites,
  child: ListView.builder(
    padding: EdgeInsets.all(16.w),
    itemCount: _favorites.length,
    itemBuilder: (context, index) {
      // 构建列表项...
    },
  ),
)

RefreshIndicator包裹ListView,提供下拉刷新功能。用户下拉列表时,会显示一个加载指示器,同时调用onRefresh回调。这里我们直接使用_loadFavorites方法,重新从数据库加载收藏列表。

下拉刷新是移动应用中常见的交互模式,用户已经习惯了这种操作。虽然收藏列表通常不需要频繁刷新,但提供这个功能可以让用户在需要时主动更新数据,提升用户体验。特别是在多设备同步的场景下,用户可能需要手动刷新来获取最新的收藏数据。

_loadFavorites方法返回Future,RefreshIndicator会等待这个Future完成后才隐藏加载指示器。这样用户可以看到刷新的过程,知道数据正在更新。整个交互过程流畅自然,符合用户的预期。

总结

本文详细介绍了收藏游戏功能的实现。我们从设计思路开始,确定了列表式布局和简单直观的交互方式。然后实现了FavoritesPage页面,包括空状态处理、收藏列表展示、收藏按钮等核心功能。

我们使用了清晰的视觉设计来区分收藏状态:红色的实心爱心表示已收藏,半透明的空心爱心表示未收藏。这种通用的视觉语言让用户不需要学习就能理解。

我们还讨论了收藏状态管理、交互优化、列表排序、批量管理、数据同步、推荐功能等扩展功能。这些功能可以让收藏系统更加完善,为用户提供更好的体验。

收藏功能是个性化体验的重要组成部分,它让用户可以定制自己的内容列表,快速访问喜欢的内容。一个好的收藏系统不仅要功能完善,还要交互流畅,让用户愿意使用。通过本文的学习,你掌握了收藏功能的实现方法,这些知识可以应用到各种需要收藏功能的应用中。

在下一篇文章中,我们将实现数据统计可视化功能,使用图表展示玩家的游戏数据。数据可视化会涉及到图表库的使用、数据处理、交互设计等内容,敬请期待。


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

Logo

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

更多推荐