chore: 清理HarmonyOS平台无关的依赖文件并更新依赖配置
本次提交包含以下主要变更: 1. 删除所有packages/fluttertoast_ohos下的HarmonyOS原生模块文件与编译配置 2. 更新pubspec.yaml的SDK与Flutter最低版本要求 3. 修复多处代码细节问题: - 替换弃用的Share.shareXFiles为SharePlus新API - 修正ConnectivityResult判断逻辑,使用contains替代直接相等判断 - 修复列表分隔符的unused参数命名 - 调整条件渲染语法为更简洁的空值判断写法 - 统一CupertinoButton的minSize参数写法 - 简化空字符串默认值处理逻辑 4. 更新pubspec.lock依赖版本
This commit is contained in:
@@ -223,7 +223,7 @@ class ComparisonController extends BaseController {
|
||||
'ComparisonController: API返回: title=${apiRecipe.title}, '
|
||||
'nutrition=${apiRecipe.nutrition != null ? "有(${apiRecipe.nutrition!.calories}kcal)" : "NULL"}, '
|
||||
'meta=${apiRecipe.meta != null ? "有" : "NULL"}, '
|
||||
'ingredients=${apiRecipe.ingredients?.length ?? 0}',
|
||||
'ingredients=${apiRecipe.ingredients.length}',
|
||||
);
|
||||
} on TimeoutException {
|
||||
debugPrint('ComparisonController: fetchDetail超时5s,使用源数据');
|
||||
@@ -255,7 +255,7 @@ class ComparisonController extends BaseController {
|
||||
RecipeModel _mergeRecipeData(RecipeModel original, RecipeModel api) {
|
||||
return RecipeModel(
|
||||
id: api.id > 0 ? api.id : original.id,
|
||||
title: (api.title ?? '').isNotEmpty ? api.title : original.title,
|
||||
title: api.title.isNotEmpty ? api.title : original.title,
|
||||
intro: (api.intro ?? '').isNotEmpty ? api.intro : original.intro,
|
||||
cover: (api.cover ?? '').isNotEmpty ? api.cover : original.cover,
|
||||
categoryName: (api.categoryName ?? '').isNotEmpty
|
||||
@@ -263,17 +263,15 @@ class ComparisonController extends BaseController {
|
||||
: original.categoryName,
|
||||
nutrition: api.nutrition ?? original.nutrition,
|
||||
meta: api.meta ?? original.meta,
|
||||
ingredients: (api.ingredients != null && api.ingredients!.isNotEmpty)
|
||||
ingredients: api.ingredients.isNotEmpty
|
||||
? api.ingredients
|
||||
: original.ingredients,
|
||||
categorizedIngredients:
|
||||
api.categorizedIngredients ?? original.categorizedIngredients,
|
||||
statistics: api.statistics ?? original.statistics,
|
||||
rating: api.rating ?? original.rating,
|
||||
tags: api.tags ?? original.tags,
|
||||
allergens: (api.allergens != null && api.allergens!.isNotEmpty)
|
||||
? api.allergens
|
||||
: original.allergens,
|
||||
tags: api.tags,
|
||||
allergens: api.allergens.isNotEmpty ? api.allergens : original.allergens,
|
||||
author: api.author ?? original.author,
|
||||
content: (api.content ?? '').isNotEmpty ? api.content : original.content,
|
||||
);
|
||||
@@ -347,7 +345,9 @@ class ComparisonController extends BaseController {
|
||||
);
|
||||
} on TimeoutException {
|
||||
debugPrint('ComparisonController: API超时5s,使用源数据');
|
||||
} catch (_) {}
|
||||
} catch (_) {
|
||||
// silently ignore - fall back to source data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IngredientModel apiIng = ingredient;
|
||||
@@ -357,7 +357,9 @@ class ComparisonController extends BaseController {
|
||||
.timeout(const Duration(seconds: 5));
|
||||
} on TimeoutException {
|
||||
debugPrint('ComparisonController: API超时5s,使用源数据');
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
// silently ignore - fall back to source data
|
||||
}
|
||||
result = IngredientModel(
|
||||
id: apiIng.id > 0 ? apiIng.id : ingredient.id,
|
||||
name: apiIng.name.isNotEmpty ? apiIng.name : ingredient.name,
|
||||
@@ -751,7 +753,7 @@ class ComparisonSummary {
|
||||
emoji: '⚠️',
|
||||
title: '过敏原对比',
|
||||
description: aAllergens != bAllergens
|
||||
? '$fewer 过敏原更少 (${aAllergens} vs ${bAllergens})'
|
||||
? '$fewer 过敏原更少 ($aAllergens vs $bAllergens)'
|
||||
: '两者过敏原数量相同',
|
||||
type: aAllergens != bAllergens
|
||||
? ComparisonType.green
|
||||
@@ -769,7 +771,7 @@ class ComparisonSummary {
|
||||
ComparisonPoint(
|
||||
emoji: '🥘',
|
||||
title: '用料数量',
|
||||
description: '${a.title} ${aIngCount}种 vs ${b.title} ${bIngCount}种',
|
||||
description: '${a.title} $aIngCount种 vs ${b.title} $bIngCount种',
|
||||
type: ComparisonType.purple,
|
||||
),
|
||||
);
|
||||
@@ -818,7 +820,7 @@ class ComparisonSummary {
|
||||
emoji: '⚠️',
|
||||
title: '过敏原对比',
|
||||
description: aAllergens != bAllergens
|
||||
? '$fewer 过敏原更少 (${aAllergens} vs ${bAllergens})'
|
||||
? '$fewer 过敏原更少 ($aAllergens vs $bAllergens)'
|
||||
: '两者过敏原数量相同',
|
||||
type: aAllergens != bAllergens
|
||||
? ComparisonType.green
|
||||
|
||||
@@ -400,7 +400,7 @@ class WhatToEatController extends BaseController {
|
||||
'title': recipe.title,
|
||||
'categoryName': recipe.categoryName ?? '',
|
||||
'time': DateTime.now().toIso8601String(),
|
||||
'displayIntro': recipe.displayIntro ?? '',
|
||||
'displayIntro': recipe.displayIntro,
|
||||
};
|
||||
eatHistory.insert(0, entry);
|
||||
if (eatHistory.length > _maxHistorySize) {
|
||||
|
||||
@@ -117,7 +117,7 @@ class BrowseHistorySection extends StatelessWidget {
|
||||
return ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: DesignTokens.space1),
|
||||
separatorBuilder: (_, __) => const SizedBox(width: DesignTokens.space2),
|
||||
separatorBuilder: (_, _) => const SizedBox(width: DesignTokens.space2),
|
||||
itemCount: history.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = history[index];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: mini_card_image_view.dart
|
||||
* 名称: 迷你卡片图片组件
|
||||
* 作用: 迷你卡片的图片展示组件,包含液态玻璃效果、分类标签、操作按钮
|
||||
@@ -94,7 +94,7 @@ class MiniCardImageView extends StatelessWidget {
|
||||
maxHeightDiskCache: 800,
|
||||
fadeInDuration: const Duration(milliseconds: 300),
|
||||
fadeInCurve: Curves.easeOut,
|
||||
errorWidget: (_, __, _) => Container(
|
||||
errorWidget: (_, _, _) => Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
@@ -113,7 +113,7 @@ class MiniCardImageView extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
progressIndicatorBuilder: (_, __, progress) => Container(
|
||||
progressIndicatorBuilder: (_, _, progress) => Container(
|
||||
color: DesignTokens.background,
|
||||
child: Center(
|
||||
child: CupertinoActivityIndicator(
|
||||
|
||||
@@ -402,7 +402,9 @@ class _MiniCardPageState extends State<MiniCardPage> {
|
||||
final boundary =
|
||||
_cardKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
|
||||
if (boundary == null) {
|
||||
Share.share('🍳 ${recipe.name} — 来自 小妈厨房 迷你卡片');
|
||||
SharePlus.instance.share(
|
||||
ShareParams(text: '🍳 ${recipe.name} — 来自 小妈厨房 迷你卡片'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -411,7 +413,9 @@ class _MiniCardPageState extends State<MiniCardPage> {
|
||||
image.dispose();
|
||||
|
||||
if (byteData == null || !mounted) {
|
||||
Share.share('🍳 ${recipe.name} — 来自 小妈厨房 迷你卡片');
|
||||
SharePlus.instance.share(
|
||||
ShareParams(text: '🍳 ${recipe.name} — 来自 小妈厨房 迷你卡片'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -425,16 +429,21 @@ class _MiniCardPageState extends State<MiniCardPage> {
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
await Share.shareXFiles([
|
||||
XFile(filePath),
|
||||
], text: '🍳 ${recipe.name} — 来自 小妈厨房');
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: '🍳 ${recipe.name} — 来自 小妈厨房',
|
||||
files: [XFile(filePath)],
|
||||
),
|
||||
);
|
||||
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
if (file.existsSync()) file.deleteSync();
|
||||
});
|
||||
} catch (e) {
|
||||
debugPrint('MiniCardPage: share failed: $e');
|
||||
Share.share('🍳 ${recipe.name} — 来自 小妈厨房 迷你卡片');
|
||||
SharePlus.instance.share(
|
||||
ShareParams(text: '🍳 ${recipe.name} — 来自 小妈厨房 迷你卡片'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: mini_card_viewer.dart
|
||||
* 名称: 迷你卡片全屏查看器
|
||||
* 作用: 迷你卡片的全屏图片查看,基于公共ImageViewerPage,增加液态玻璃信息层
|
||||
@@ -48,7 +48,7 @@ class MiniCardViewer {
|
||||
fadeInCurve: Curves.easeOut,
|
||||
maxWidthDiskCache: 1200,
|
||||
maxHeightDiskCache: 1200,
|
||||
errorWidget: (_, __, _) => Container(
|
||||
errorWidget: (_, _, _) => Container(
|
||||
color: Colors.grey[900],
|
||||
child: const Center(
|
||||
child: Icon(
|
||||
@@ -58,7 +58,7 @@ class MiniCardViewer {
|
||||
),
|
||||
),
|
||||
),
|
||||
progressIndicatorBuilder: (_, __, _) => Container(
|
||||
progressIndicatorBuilder: (_, _, _) => Container(
|
||||
color: Colors.grey[900],
|
||||
child: const Center(
|
||||
child: CupertinoActivityIndicator(radius: 16),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: search_page.dart
|
||||
* 名称: 搜索页面
|
||||
* 作用: iOS 26 风格的菜谱搜索页面,支持多维度搜索+高级筛选
|
||||
@@ -633,7 +633,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: tabs.length,
|
||||
separatorBuilder: (_, __) => SizedBox(width: DesignTokens.space2),
|
||||
separatorBuilder: (_, _) => SizedBox(width: DesignTokens.space2),
|
||||
itemBuilder: (context, index) {
|
||||
final tab = tabs[index];
|
||||
final isSelected =
|
||||
@@ -744,7 +744,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||
vertical: DesignTokens.space2,
|
||||
),
|
||||
itemCount: recipes.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: DesignTokens.space2),
|
||||
separatorBuilder: (_, _) => const SizedBox(height: DesignTokens.space2),
|
||||
itemBuilder: (context, index) {
|
||||
final recipe = recipes[index];
|
||||
return _buildRecipeCard(recipe, isDark);
|
||||
@@ -858,7 +858,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||
vertical: DesignTokens.space2,
|
||||
),
|
||||
itemCount: ingredients.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: DesignTokens.space2),
|
||||
separatorBuilder: (_, _) => const SizedBox(height: DesignTokens.space2),
|
||||
itemBuilder: (context, index) {
|
||||
final ingredient = ingredients[index];
|
||||
return GestureDetector(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: cache_manage_page.dart
|
||||
* 说明: 缓存管理页面
|
||||
* 作用: 管理首页Discover缓存、菜品详情缓存、食材缓存、API缓存与图片缓存
|
||||
@@ -842,7 +842,7 @@ class _CacheManagePageState extends State<CacheManagePage> {
|
||||
child: Image.network(
|
||||
item.cover!,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, _) => Icon(
|
||||
errorBuilder: (_, _, _) => Icon(
|
||||
CupertinoIcons.photo,
|
||||
color: DesignTokens.dynamicPrimary,
|
||||
),
|
||||
|
||||
@@ -579,7 +579,7 @@ class _FeedbackPageState extends State<FeedbackPage> {
|
||||
),
|
||||
trailing: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
minSize: 36,
|
||||
minimumSize: const Size(36, 36),
|
||||
onPressed: _showInfoDialog,
|
||||
child: Icon(
|
||||
CupertinoIcons.info_circle,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: favorites_item_builders.dart
|
||||
* 名称: 收藏项构建器 Mixin
|
||||
* 作用: 收藏页面中各类收藏项的构建方法,拆分自 favorites_page.dart
|
||||
@@ -204,7 +204,7 @@ mixin FavoritesItemBuilders<T extends StatefulWidget> on State<T> {
|
||||
? coverUrl
|
||||
: 'https://eat.wktyl.com/api/assets/$coverUrl',
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, _) => Container(
|
||||
errorBuilder: (_, _, _) => Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: footprints_page.dart
|
||||
* 说明: 浏览记录页面,展示用户浏览菜谱历史
|
||||
* 作用: 展示用户足迹,支持管理、删除、清空
|
||||
@@ -133,7 +133,7 @@ class FootprintsPage extends StatelessWidget {
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(DesignTokens.space4),
|
||||
itemCount: history.length,
|
||||
separatorBuilder: (_, __) =>
|
||||
separatorBuilder: (_, _) =>
|
||||
const SizedBox(height: DesignTokens.space3),
|
||||
itemBuilder: (context, index) =>
|
||||
_buildHistoryCard(context, controller, history[index], isDark),
|
||||
|
||||
@@ -320,7 +320,7 @@ class DishComparisonPage extends StatelessWidget {
|
||||
width: 48,
|
||||
height: 48,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Text(
|
||||
errorBuilder: (_, _, _) => Text(
|
||||
_categoryEmoji(
|
||||
recipe.categoryName ?? '',
|
||||
),
|
||||
@@ -832,7 +832,7 @@ class DishComparisonPage extends StatelessWidget {
|
||||
Widget _buildIndicesSection(List<RecipeModel> slots, bool isDark) {
|
||||
final allKeys = <String>{};
|
||||
for (final r in slots) {
|
||||
allKeys.addAll(r.meta?.indices?.keys ?? <String>[]);
|
||||
allKeys.addAll(r.meta?.indices.keys ?? <String>[]);
|
||||
}
|
||||
if (allKeys.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
@@ -846,8 +846,8 @@ class DishComparisonPage extends StatelessWidget {
|
||||
child: Column(
|
||||
children: sortedKeys.map((key) {
|
||||
if (is1v1) {
|
||||
final scoreA = slots[0].meta?.indices?[key] ?? 0;
|
||||
final scoreB = slots[1].meta?.indices?[key] ?? 0;
|
||||
final scoreA = slots[0].meta?.indices[key] ?? 0;
|
||||
final scoreB = slots[1].meta?.indices[key] ?? 0;
|
||||
final colorA = ComparisonController.slotColors[0];
|
||||
final colorB = ComparisonController.slotColors[1];
|
||||
return Padding(
|
||||
@@ -901,7 +901,7 @@ class DishComparisonPage extends StatelessWidget {
|
||||
...slots.asMap().entries.map((entry) {
|
||||
final i = entry.key;
|
||||
final recipe = entry.value;
|
||||
final score = recipe.meta?.indices?[key] ?? 0;
|
||||
final score = recipe.meta?.indices[key] ?? 0;
|
||||
final color = ComparisonController
|
||||
.slotColors[i % ComparisonController.slotColors.length];
|
||||
return Padding(
|
||||
@@ -1105,7 +1105,6 @@ class DishComparisonPage extends StatelessWidget {
|
||||
final item = recipe.nutrition?.items
|
||||
?.where((e) => e.name == name)
|
||||
.firstOrNull;
|
||||
final hasValue = item != null;
|
||||
final value = item?.value ?? 0;
|
||||
final unit = item?.unit ?? '';
|
||||
final slotColor = ComparisonController
|
||||
@@ -1390,8 +1389,9 @@ class DishComparisonPage extends StatelessWidget {
|
||||
|
||||
Widget _buildSummarySection(ComparisonController ctrl, bool isDark) {
|
||||
final summary = ctrl.dishComparisonSummary;
|
||||
if (summary == null || summary.points.isEmpty)
|
||||
if (summary == null || summary.points.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return _buildSectionCard(
|
||||
isDark,
|
||||
|
||||
@@ -181,7 +181,7 @@ class IngredientComparisonPage extends StatelessWidget {
|
||||
width: 48,
|
||||
height: 48,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Text(
|
||||
errorBuilder: (_, _, _) => Text(
|
||||
ingredient.displayImage,
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
@@ -381,8 +381,9 @@ class IngredientComparisonPage extends StatelessWidget {
|
||||
final ing = entry.value;
|
||||
final color = ComparisonController
|
||||
.slotColors[i % ComparisonController.slotColors.length];
|
||||
if (ing.nutrition == null || ing.nutrition!.isEmpty)
|
||||
if (ing.nutrition == null || ing.nutrition!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: DesignTokens.space3),
|
||||
child: _buildNutritionBlock(
|
||||
@@ -627,8 +628,9 @@ class IngredientComparisonPage extends StatelessWidget {
|
||||
final ing = entry.value;
|
||||
final color = ComparisonController
|
||||
.slotColors[i % ComparisonController.slotColors.length];
|
||||
if (ing.usageTip == null || ing.usageTip!.isEmpty)
|
||||
if (ing.usageTip == null || ing.usageTip!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: DesignTokens.space3),
|
||||
child: _buildTipBlock(
|
||||
@@ -690,8 +692,9 @@ class IngredientComparisonPage extends StatelessWidget {
|
||||
|
||||
Widget _buildSummarySection(ComparisonController ctrl, bool isDark) {
|
||||
final summary = ctrl.ingredientComparisonSummary;
|
||||
if (summary == null || summary.points.isEmpty)
|
||||
if (summary == null || summary.points.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return _buildSectionCard(
|
||||
isDark,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: order_assistant_page.dart
|
||||
* 名称: 点餐助手主页面
|
||||
* 作用: 点餐/推单主界面,支持菜品管理、账单生成、二维码分享、文本/图片分享
|
||||
@@ -851,13 +851,11 @@ class _OrderAssistantPageState extends State<OrderAssistantPage> {
|
||||
}
|
||||
final text = _generateOrderText(order);
|
||||
try {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
await Share.share(
|
||||
text,
|
||||
subject: '点餐助手 - ${order.orderNo}',
|
||||
sharePositionOrigin: box != null
|
||||
? box.localToGlobal(Offset.zero) & box.size
|
||||
: null,
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: text,
|
||||
subject: '点餐助手 - ${order.orderNo}',
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('[OrderPage] share text error: $e');
|
||||
@@ -955,7 +953,7 @@ class _OrderAssistantPageState extends State<OrderAssistantPage> {
|
||||
borderRadius: DesignTokens.borderRadiusMd,
|
||||
onPressed: () async {
|
||||
await _captureAndShare(previewKey, order);
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
if (mounted) Navigator.pop(context);
|
||||
},
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -1310,14 +1308,12 @@ class _OrderAssistantPageState extends State<OrderAssistantPage> {
|
||||
await File(filePath).writeAsBytes(buffer.asUint8List());
|
||||
final xFile = XFile(filePath);
|
||||
if (!mounted) return;
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
await Share.shareXFiles(
|
||||
[xFile],
|
||||
text: '点餐助手 - ${order.orderNo}',
|
||||
subject: '点餐助手 - ${order.orderNo}',
|
||||
sharePositionOrigin: box != null
|
||||
? box.localToGlobal(Offset.zero) & box.size
|
||||
: null,
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: '点餐助手 - ${order.orderNo}',
|
||||
subject: '点餐助手 - ${order.orderNo}',
|
||||
files: [xFile],
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('[OrderPage] capture and share error: $e');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: browse_history_picker.dart
|
||||
* 名称: 浏览记录选择器
|
||||
* 作用: 展示本地浏览记录供用户选择菜品
|
||||
@@ -140,7 +140,7 @@ class BrowseHistoryPicker extends StatelessWidget {
|
||||
width: 44,
|
||||
height: 44,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => _defaultFoodIcon(),
|
||||
errorBuilder: (_, _, _) => _defaultFoodIcon(),
|
||||
)
|
||||
: _defaultFoodIcon(),
|
||||
),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: dish_pick_sheet.dart
|
||||
* 名称: 菜品选择面板
|
||||
* 作用: 底部弹出面板,支持从浏览记录/收藏/手动输入三种方式选择菜品加入Tier List
|
||||
@@ -357,7 +357,7 @@ class _DishPickSheetState extends State<DishPickSheet> {
|
||||
bottom: bottomPadding + DesignTokens.space3,
|
||||
),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => material.Divider(
|
||||
separatorBuilder: (_, _) => material.Divider(
|
||||
height: 1,
|
||||
color: isDark ? material.Colors.white10 : const Color(0x0F000000),
|
||||
),
|
||||
@@ -390,7 +390,7 @@ class _DishPickSheetState extends State<DishPickSheet> {
|
||||
bottom: bottomPadding + DesignTokens.space3,
|
||||
),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => material.Divider(
|
||||
separatorBuilder: (_, _) => material.Divider(
|
||||
height: 1,
|
||||
color: isDark ? material.Colors.white10 : const Color(0x0F000000),
|
||||
),
|
||||
@@ -547,7 +547,7 @@ class _DishPickSheetState extends State<DishPickSheet> {
|
||||
? Image.network(
|
||||
coverUrl,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Center(
|
||||
errorBuilder: (_, _, _) => Center(
|
||||
child: Text(
|
||||
emoji,
|
||||
style: const TextStyle(fontSize: 22),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 文件: dish_ranking_page.dart
|
||||
* 名称: 菜品排名页面
|
||||
* 作用: Tier List主界面,展示5个层级的菜品排名,支持拖拽排序、跨层级移动、添加/删除
|
||||
@@ -325,7 +325,7 @@ class _DishRankingPageState extends State<DishRankingPage>
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(width: 8),
|
||||
separatorBuilder: (_, _) => const SizedBox(width: 8),
|
||||
itemBuilder: (context, index) {
|
||||
return _DishCard(
|
||||
item: items[index],
|
||||
@@ -549,7 +549,7 @@ class _DishCard extends StatelessWidget {
|
||||
item.coverImage!,
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
errorBuilder: (_, __, ___) => Center(
|
||||
errorBuilder: (_, _, _) => Center(
|
||||
child: Text(
|
||||
item.emoji,
|
||||
style: const TextStyle(fontSize: 32),
|
||||
|
||||
@@ -235,7 +235,7 @@ class ApiService {
|
||||
final result = await Connectivity().checkConnectivity().timeout(
|
||||
const Duration(seconds: 3),
|
||||
);
|
||||
return result == ConnectivityResult.none;
|
||||
return result.contains(ConnectivityResult.none);
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -278,10 +278,12 @@ class DataExportService extends GetxService {
|
||||
|
||||
Future<void> shareExportedData(String filePath) async {
|
||||
if (kIsWeb) return;
|
||||
await Share.shareXFiles(
|
||||
[XFile(filePath)],
|
||||
subject: '小妈厨房 - 数据导出',
|
||||
text: '从小妈厨房导出的数据',
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: '从小妈厨房导出的数据',
|
||||
subject: '小妈厨房 - 数据导出',
|
||||
files: [XFile(filePath)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ typedef OnNetworkRestoredCallback = Future<void> Function();
|
||||
|
||||
class ConnectivityService extends GetxService with WidgetsBindingObserver {
|
||||
final Connectivity _connectivity = Connectivity();
|
||||
StreamSubscription<ConnectivityResult>? _subscription;
|
||||
StreamSubscription<List<ConnectivityResult>>? _subscription;
|
||||
|
||||
final Rx<ConnectivityStatus> status = Rx<ConnectivityStatus>(
|
||||
ConnectivityStatus.unknown,
|
||||
@@ -91,8 +91,8 @@ class ConnectivityService extends GetxService with WidgetsBindingObserver {
|
||||
_onChanged(result);
|
||||
}
|
||||
|
||||
void _onChanged(ConnectivityResult result) {
|
||||
final hasConnection = result != ConnectivityResult.none;
|
||||
void _onChanged(List<ConnectivityResult> results) {
|
||||
final hasConnection = !results.contains(ConnectivityResult.none);
|
||||
final newStatus = hasConnection
|
||||
? ConnectivityStatus.online
|
||||
: ConnectivityStatus.offline;
|
||||
@@ -122,8 +122,8 @@ class ConnectivityService extends GetxService with WidgetsBindingObserver {
|
||||
|
||||
Future<bool> checkConnection() async {
|
||||
if (kIsWeb) return true;
|
||||
final result = await _connectivity.checkConnectivity();
|
||||
return result != ConnectivityResult.none;
|
||||
final results = await _connectivity.checkConnectivity();
|
||||
return !results.contains(ConnectivityResult.none);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -366,7 +366,7 @@ class ThemeService extends GetxController {
|
||||
}
|
||||
|
||||
ThemeData getThemeData() {
|
||||
final _font = fontFamilyStyle.value == FontFamilyStyle.notoSansSC
|
||||
final font = fontFamilyStyle.value == FontFamilyStyle.notoSansSC
|
||||
? 'NotoSansSC'
|
||||
: null;
|
||||
return ThemeData(
|
||||
@@ -379,42 +379,42 @@ class ThemeService extends GetxController {
|
||||
bodyLarge: TextStyle(
|
||||
fontSize: fontSize.value,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontSize: fontSize.value - 2,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontSize: fontSize.value - 4,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: fontSize.value + 4,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: fontSize.value + 2,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontSize: fontSize.value,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
CupertinoThemeData get cupertinoThemeData {
|
||||
final _font = fontFamilyStyle.value == FontFamilyStyle.notoSansSC
|
||||
final font = fontFamilyStyle.value == FontFamilyStyle.notoSansSC
|
||||
? 'NotoSansSC'
|
||||
: null;
|
||||
return CupertinoThemeData(
|
||||
@@ -428,14 +428,14 @@ class ThemeService extends GetxController {
|
||||
inherit: false,
|
||||
fontSize: fontSize.value,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
navTitleTextStyle: TextStyle(
|
||||
inherit: false,
|
||||
fontSize: fontSize.value + 2,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor.value,
|
||||
fontFamily: _font,
|
||||
fontFamily: font,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ class AppUtils {
|
||||
}
|
||||
|
||||
static Future<void> shareContent(String text, {String? subject}) async {
|
||||
await Share.share(text, subject: subject);
|
||||
await SharePlus.instance.share(ShareParams(text: text, subject: subject));
|
||||
}
|
||||
|
||||
static void copyToClipboard(BuildContext context, String text) {
|
||||
@@ -153,14 +153,14 @@ class StringUtils {
|
||||
class NetworkUtils {
|
||||
static Future<bool> isConnected() async {
|
||||
final connectivityResult = await Connectivity().checkConnectivity();
|
||||
return connectivityResult != ConnectivityResult.none;
|
||||
return !connectivityResult.contains(ConnectivityResult.none);
|
||||
}
|
||||
|
||||
static Future<String> getConnectionType() async {
|
||||
final connectivityResult = await Connectivity().checkConnectivity();
|
||||
if (connectivityResult == ConnectivityResult.wifi) {
|
||||
if (connectivityResult.contains(ConnectivityResult.wifi)) {
|
||||
return 'WiFi';
|
||||
} else if (connectivityResult == ConnectivityResult.mobile) {
|
||||
} else if (connectivityResult.contains(ConnectivityResult.mobile)) {
|
||||
return 'Mobile';
|
||||
} else {
|
||||
return 'No Connection';
|
||||
|
||||
@@ -63,10 +63,11 @@ class FarmShareUtil {
|
||||
await file.writeAsBytes(bytes);
|
||||
|
||||
// 分享
|
||||
await Share.shareXFiles(
|
||||
[XFile(file.path)],
|
||||
text:
|
||||
'🌾 我在小妈菜园的收获!\n等级: Lv.$level | 收获: $totalHarvest 次 | 金币: $gold 💰',
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: '🌾 我在小妈菜园的收获!\n等级: Lv.$level | 收获: $totalHarvest 次 | 金币: $gold 💰',
|
||||
files: [XFile(file.path)],
|
||||
),
|
||||
);
|
||||
|
||||
// 清理临时文件
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 2026-04-09 | AppPageScaffold | 全局可复用页面骨架组件 | 统一iOS26风格页面结构
|
||||
// 2026-04-09 | AppPageScaffold | 全局可复用页面骨架组件 | 统一iOS26风格页面结构
|
||||
// 2026-04-09 | 初始创建,提供导航栏/加载/空态/错误/内容五种状态
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -200,7 +200,7 @@ class AppSectionHeader extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (trailing != null) trailing!,
|
||||
?trailing,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -225,7 +225,7 @@ class ComparisonLayout extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: DesignTokens.space2),
|
||||
Text(
|
||||
'添加对比项 (${itemCount}/${ComparisonController.maxSlots})',
|
||||
'添加对比项 ($itemCount/${ComparisonController.maxSlots})',
|
||||
style: TextStyle(
|
||||
fontSize: DesignTokens.fontSm,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@@ -93,13 +93,13 @@ class MiniCardDiscoverCard extends StatelessWidget {
|
||||
: CachedNetworkImage(
|
||||
imageUrl: recipe.fullImageUrl,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (_, __) => Container(
|
||||
placeholder: (_, _) => Container(
|
||||
color: DesignTokens.background,
|
||||
child: const Center(
|
||||
child: CupertinoActivityIndicator(),
|
||||
),
|
||||
),
|
||||
errorWidget: (_, __, _) => Container(
|
||||
errorWidget: (_, _, _) => Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
|
||||
@@ -215,7 +215,7 @@ class GlassFadeSwitch extends StatelessWidget {
|
||||
alignment: alignment,
|
||||
children: <Widget>[
|
||||
...previousChildren,
|
||||
if (currentChild != null) currentChild,
|
||||
?currentChild,
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -182,8 +182,9 @@ class _RecipeCoverImageState extends State<RecipeCoverImage> {
|
||||
|
||||
Color _getBadgeBgColor(RecipeRating ratingData) {
|
||||
if (ratingData.isExcellent) return DesignTokens.gold.withValues(alpha: 0.9);
|
||||
if (ratingData.isRecommended)
|
||||
if (ratingData.isRecommended) {
|
||||
return DesignTokens.dynamicPrimary.withValues(alpha: 0.9);
|
||||
}
|
||||
return DesignTokens.orange.withValues(alpha: 0.9);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,8 +87,9 @@ class RecipeExportButton extends StatelessWidget {
|
||||
}
|
||||
if (codeUnit == 0x7F) return true;
|
||||
if (codeUnit >= 0x80 && codeUnit <= 0x9F) return true;
|
||||
if ((codeUnit & 0xFFFE) == 0xFFFE || (codeUnit & 0xFFFE) == 0xFFFF)
|
||||
if ((codeUnit & 0xFFFE) == 0xFFFE || (codeUnit & 0xFFFE) == 0xFFFF) {
|
||||
return true;
|
||||
}
|
||||
if (codeUnit == 0xFFFD) return true;
|
||||
if (codeUnit >= 0xFDD0 && codeUnit <= 0xFDEF) return true;
|
||||
if (codeUnit >= 0xE000 && codeUnit <= 0xF8FF) return true;
|
||||
@@ -591,17 +592,21 @@ class RecipeExportButton extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
final fileSize = await file.length();
|
||||
debugPrint('开始分享文件: $filePath (${fileSize} bytes)');
|
||||
debugPrint('开始分享文件: $filePath ($fileSize bytes)');
|
||||
|
||||
final xFile = XFile(filePath);
|
||||
await Share.shareXFiles([xFile], text: subject ?? '来自小妈厨房的菜谱');
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(text: subject ?? '来自小妈厨房的菜谱', files: [xFile]),
|
||||
);
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint(
|
||||
'分享PlatformException: code=${e.code} msg=${e.message} details=${e.details}',
|
||||
);
|
||||
try {
|
||||
debugPrint('尝试 fallback 纯文本分享...');
|
||||
await Share.share(subject ?? '来自小妈厨房的菜谱', sharePositionOrigin: null);
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(text: subject ?? '来自小妈厨房的菜谱'),
|
||||
);
|
||||
} catch (e2) {
|
||||
debugPrint('fallback 分享也失败: $e2');
|
||||
ToastService.show(
|
||||
@@ -1502,7 +1507,7 @@ class RecipeExportButton extends StatelessWidget {
|
||||
const Spacer(),
|
||||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
minSize: 30,
|
||||
minimumSize: const Size(30, 30),
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: Text(
|
||||
'取消',
|
||||
|
||||
Reference in New Issue
Block a user