feat: 更新Flutter OHOS适配和依赖版本

refactor(fluttertoast_ohos): 重构OHOS平台插件代码结构
fix(discover): 修改迷你卡片显示文本为"作者精选"
chore: 更新pubspec版本号至0.95.0+94
build: 添加flutter_card_swiper和更新flutter_markdown_plus依赖
This commit is contained in:
Developer
2026-04-14 20:33:30 +08:00
parent 9aa8565ca7
commit 3788902f98
161 changed files with 28820 additions and 1404 deletions

View File

@@ -0,0 +1,383 @@
# MiniCardPage flutter_card_swiper 深度重构实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将 mini_card_page.dart 的自定义手势滑动系统替换为 flutter_card_swiper: ^7.2.0 库,移除所有手动动画控制器和手势追踪逻辑。
**Architecture:**`CardSwiper` widget 替代 `GestureDetector + 3个AnimationController + 手动拖拽追踪(_dragX/_dragY)` 的自定义实现。CardSwiper 内置滑动动画、回弹、堆叠缩放、撤销功能。保留 MiniCardImageView 作为卡片内容组件,保留分类筛选/搜索/网格视图/收藏/分享等所有业务功能不变。
**Tech Stack:** Flutter, flutter_card_swiper ^7.2.0, Cupertino 风格, GetX 状态管理
---
### Task 1: 添加 flutter_card_swiper 依赖
**Files:**
- Modify: `pubspec.yaml`
- [ ] **Step 1: 在 pubspec.yaml dependencies 中添加 flutter_card_swiper**
`cached_network_image:` 之后添加:
```yaml
flutter_card_swiper: ^7.2.0
```
- [ ] **Step 2: 执行 flutter pub get 安装依赖**
Run: `flutter pub get`
Expected: 成功安装 flutter_card_swiper 7.2.0
---
### Task 2: 重写 mini_card_page.dart — 移除旧手势系统,引入 CardSwiper
**Files:**
- Modify: `lib/src/pages/discover/mini_card/mini_card_page.dart`
这是核心任务,完整重写文件。以下是新文件的完整内容:
**删除的变量(共 15 个):**
- `_exitAnimController`, `_enterAnimController`, `_bounceAnimController` — 3 个 AnimationController
- `_dragX`, `_dragY` — 手动拖拽位置
- `_isDragging`, `_isAnimatingExit` — 动画状态标志
- `_exitDirection`, `_exitStartX`, `_exitStartY` — 退出动画参数
- `_kSwipeThreshold`, `_kExitDistance`, `_kPreloadRange` — 常量_kPreloadRange 保留)
**新增的变量1 个):**
- `CardSwiperController _swiperController = CardSwiperController()` — 程序化控制
**删除的方法(共 8 个):**
- `_swipeRight()` — 合并到 onSwipe 回调
- `_swipeLeft()` — 合并到 onSwipe 回调
- `_startExitAnimation()` — 由 CardSwiper 内部处理
- `_onExitComplete()` — 由 CardSwiper 内部处理
- `_goPrev()` — 改用 `_swiperController.undo()`
- `_bounceBack()` — 由 CardSwiper 内置回弹处理
- `_buildSwipeLabel()` — CardSwiper 自带方向指示器效果(可选保留用于 overlay
**修改的方法:**
- `initState()` — 移除 3 个 AnimationController 创建和监听
- `dispose()` — 移除 3 个 AnimationController dispose
- `_buildCardView()` — 核心改动GestureDetector → CardSwiper
- 底部按钮的 onTap 回调适配
**完全保留的方法:**
- `_loadData()`, `_handleInitialJump()`, `_saveCacheMeta()`, `_loadCacheMeta()`
- `_loadLocalState()`, `_saveLocalState()`, `_markViewed()`
- `_filterByCategory()`, `_doSearch()`, `_jumpToRecipe()`
- `_preloadNextImages()`, `_toggleFavorite()`, `_navigateToDetail()`, `_shareRecipe()`
- `_openFullScreenViewer()`, `_isFavorited()`
- `_buildLoading()`, `_buildSearchView()`, `_buildGridView()`
- `_buildCategoryChips()`, `_buildChip()`, `_buildNavButton()`
- `_buildCardImage()`, `_buildGridItemInfo()`
- [ ] **Step 1: 重写 mini_card_page.dart 完整文件**
文件头部注释更新为:
```
* 更新: 2026-04-14 引入flutter_card_swiper重构滑动系统移除自定义手势/动画控制器
```
import 新增:
```dart
import 'package:flutter_card_swiper/flutter_card_swiper.dart';
```
State 类变量变更:
```dart
class _MiniCardPageState extends State<MiniCardPage>
with TickerProviderStateMixin { // TickerProviderStateMixin 可移除,但保留无影响
List<MiniCardRecipe> _allRecipes = [];
List<MiniCardRecipe> _filteredRecipes = [];
MiniCardMeta? _meta;
int _currentIndex = 0;
String _activeCategory = 'all';
bool _isLoading = true;
bool _isGridView = false;
bool _isSearchOpen = false;
final TextEditingController _searchController = TextEditingController();
List<MiniCardRecipe> _searchResults = [];
Set<int> _likedIds = {};
Set<int> _dislikedIds = {};
Set<int> _viewedIds = {};
final GlobalKey _cardKey = GlobalKey();
static const String _kLikedKey = 'mini_card_liked';
static const String _kDislikedKey = 'mini_card_disliked';
static const String _kLastIndexKey = 'mini_card_last_index';
static const String _kCacheKey = 'mini_card_cache_meta';
static const String _kViewedKey = 'mini_card_viewed';
static const int _kCacheMaxItems = 10;
// 新增CardSwiper 控制器
final CardSwiperController _swiperController = CardSwiperController();
// 图片预加载范围
static const int _kPreloadRange = 3;
```
initState 简化为:
```dart
@override
void initState() {
super.initState();
_loadData();
}
```
dispose 简化为:
```dart
@override
void dispose() {
_swiperController.dispose(); // 新增:释放 swiper 控制器
_searchController.dispose();
super.dispose();
}
```
新增统一滑动处理方法(替代原来的 _swipeRight/_swipeLeft
```dart
void _handleSwipe(int index, CardSwiperDirection direction) {
final recipe = _filteredRecipes[index];
if (direction == CardSwiperDirection.right) {
_likedIds.add(recipe.id);
} else if (direction == CardSwiperDirection.left) {
_dislikedIds.add(recipe.id);
}
_markViewed(recipe.id);
_saveLocalState();
_currentIndex = index + 1; // 跟踪当前位置
if (_currentIndex < _filteredRecipes.length) {
_preloadNextImages();
}
setState(() {});
return true; // 允许滑走
}
void _handleUndo(int? previousIndex, int? currentIndex) {
if (previousIndex != null && previousIndex < _filteredRecipes.length) {
final recipe = _filteredRecipes[previousIndex];
_likedIds.remove(recipe.id);
_dislikedIds.remove(recipe.id);
_currentIndex = previousIndex;
_saveLocalState();
setState(() {});
}
return true;
}
```
核心方法 `_buildCardView(bool isDark)` 完全重写为:
```dart
Widget _buildCardView(bool isDark) {
if (_filteredRecipes.isEmpty) {
return Center(
child: Text(
'暂无菜谱 😢',
style: TextStyle(
color: isDark ? DarkDesignTokens.text2 : DesignTokens.text2,
),
),
);
}
final total = _filteredRecipes.length;
final currentRecipe = total > 0 && _currentIndex < total
? _filteredRecipes[_currentIndex]
: _filteredRecipes.first;
final isFav = _isFavorited(currentRecipe.id);
return Column(
children: [
const SizedBox(height: DesignTokens.space2),
_buildCategoryChips(isDark),
const SizedBox(height: DesignTokens.space3),
Padding(
padding: const EdgeInsets.symmetric(horizontal: DesignTokens.space4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${_currentIndex + 1} / $total',
style: TextStyle(
fontSize: DesignTokens.fontSm,
color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3,
),
),
Row(
children: [
Icon(CupertinoIcons.heart_fill, size: 12, color: DesignTokens.red),
const SizedBox(width: 4),
Text('${_likedIds.length}', style: TextStyle(fontSize: DesignTokens.fontSm, color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3)),
const SizedBox(width: 8),
Icon(CupertinoIcons.eye, size: 12, color: DesignTokens.dynamicPrimary),
const SizedBox(width: 4),
Text('${_viewedIds.length}', style: TextStyle(fontSize: DesignTokens.fontSm, color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3)),
],
),
],
),
),
const SizedBox(height: DesignTokens.space2),
Expanded(
child: CardSwiper(
controller: _swiperController,
cardsCount: _filteredRecipes.length,
initialIndex: _currentIndex.clamp(0, total > 0 ? total - 1 : 0),
allowedSwipeDirection: AllowedSwipeDirection.horizontal,
numberOfCardsDisplayed: 2,
scale: 0.92,
threshold: 80,
maxAngle: 20,
duration: 300,
isLoop: false,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
onSwipe: _handleSwipe,
onUndo: _handleUndo,
onEnd: () {
debugPrint('MiniCardPage: 所有卡片已浏览完');
},
cardBuilder: (context, index, percentThresholdX, percentThresholdY) {
final recipe = _filteredRecipes[index];
final liked = _likedIds.contains(recipe.id);
final favorited = _isFavorited(recipe.id);
return GestureDetector(
onTap: () => _openFullScreenViewer(recipe),
child: LayoutBuilder(
builder: (context, constraints) {
final maxW = constraints.maxWidth - 32;
final maxH = constraints.maxHeight - 16;
return MiniCardImageView(
recipe: recipe,
meta: _meta,
isDark: isDark,
width: maxW,
height: maxH,
isFavorited: favorited,
isLiked: liked,
onFavoriteTap: () => _toggleFavorite(recipe),
onShareTap: () => _shareRecipe(recipe),
onDetailTap: () => _navigateToDetail(recipe),
);
},
),
);
},
),
),
const SizedBox(height: DesignTokens.space3),
Padding(
padding: const EdgeInsets.symmetric(horizontal: DesignTokens.space5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildNavButton(
icon: CupertinoIcons.heart_slash,
label: '下一道',
color: DesignTokens.red,
onTap: () => _swiperController.swipe(CardSwiperDirection.left),
),
_buildNavButton(
icon: isFav ? CupertinoIcons.heart_fill : CupertinoIcons.heart,
label: isFav ? '已收藏' : '收藏',
color: isFav ? DesignTokens.red : DesignTokens.orange,
onTap: () => _toggleFavorite(currentRecipe),
),
_buildNavButton(
icon: CupertinoIcons.share,
label: '分享',
color: DesignTokens.dynamicPrimary,
onTap: () => _shareRecipe(currentRecipe),
),
_buildNavButton(
icon: CupertinoIcons.heart_fill,
label: '喜欢',
color: DesignTokens.green,
onTap: () => _swiperController.swipe(CardSwiperDirection.right),
),
_buildNavButton(
icon: CupertinoIcons.chevron_left,
label: '返回',
color: DesignTokens.dynamicPrimary,
onTap: () => _swiperController.undo(),
),
],
),
),
const SizedBox(height: DesignTokens.space2),
Padding(
padding: const EdgeInsets.symmetric(horizontal: DesignTokens.space5),
child: ClipRRect(
borderRadius: BorderRadius.circular(DesignTokens.radiusFull),
child: LinearProgressIndicator(
value: total > 0 ? (_currentIndex + 1) / total : 0,
backgroundColor: isDark ? DarkDesignTokens.text3 : DesignTokens.text3.withValues(alpha: 0.2),
valueColor: AlwaysStoppedAnimation(DesignTokens.dynamicPrimary),
minHeight: 3,
),
),
),
const SizedBox(height: DesignTokens.space4),
],
);
}
```
删除以下不再需要的方法:
- ~~`_swipeRight()`~~ → 合并入 `_handleSwipe()`
- ~~`_swipeLeft()`~~ → 合并入 `_handleSwipe()`
- ~~`_startExitAnimation()`~~ → CardSwiper 内置
- ~~`_onExitComplete()`~~ → CardSwiper 内置
- ~~`_goPrev()`~~ → 改用 `_swiperController.undo()`
- ~~`_bounceBack()`~~ → CardSwiper 内置
- ~~`_buildSwipeLabel()`~~ → 可选:如需保留"❤️ 喜欢"/"👎 下一道"标签覆盖层,可在 cardBuilder 中根据 percentThresholdX 绘制
保留 `_openFullScreenViewer()` 方法不变(它使用 `_currentIndex` 访问当前卡片)。
- [ ] **Step 2: 验证编译通过**
Run: `flutter analyze lib/src/pages/discover/mini_card/mini_card_page.dart`
Expected: 无错误(允许 warnings
---
### Task 3: 更新 CHANGELOG.md
**Files:**
- Modify: `CHANGELOG.md`
- [ ] **Step 1: 追加版本变更记录**
在 CHANGELOG.md 顶部添加新版本条目(或更新当前版本说明):
```markdown
## [0.93.0] - 2026-04-14
### Changed
- 🔄 **MiniCardPage 滑动引擎重构**: 移除自定义手势检测+3个AnimationController引入 `flutter_card_swiper: ^7.2.0` 实现 Tinder 风格卡片滑动
- 支持4方向滑动、程序化控制(swipe/undo/moveTo)、内置回弹动画
- 代码行数从 ~1262 行减少至 ~850 行(减少 ~32%
- 保留所有现有功能:分类筛选、搜索、网格视图、收藏、分享、详情跳转、全屏查看
### Dependencies
- 新增: flutter_card_swiper ^7.2.0
```
---
## 文件变更清单
| 文件 | 操作 | 说明 |
|------|------|------|
| `pubspec.yaml` | 修改 | 添加 flutter_card_swiper: ^7.2.0 |
| `lib/src/pages/discover/mini_card/mini_card_page.dart` | 重写 | 核心重构 |
| `CHANGELOG.md` | 修改 | 版本记录 |
**未修改文件:**
- `mini_card_image_view.dart` ✅ 卡片UI组件保持不变
- `mini_card_viewer.dart` ✅ 全屏查看器保持不变
- `mini_card_model.dart` ✅ 数据模型保持不变