From dd9b8141747b0ffbe9af7e7104c600a4ba26bd93 Mon Sep 17 00:00:00 2001 From: Ponshu Developer Date: Fri, 17 Apr 2026 23:48:32 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20TextEditingController=E3=83=AA?= =?UTF-8?q?=E3=83=BC=E3=82=AF=E8=A7=A3=E6=B6=88=E3=83=BB=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E6=AD=A3=E8=A6=8F=E5=8C=96=E3=83=BB=E3=83=87=E3=82=B6?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=83=88=E3=83=BC=E3=82=AF=E3=83=B3=E6=95=B4?= =?UTF-8?q?=E5=82=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sake_detail_screen: _showTagEditDialog/TextEditDialog/BreweryEditDialog に try/finally + controller.dispose() を追加(メモリリーク修正) - sake_detail_screen: State フィールドを build() より前に移動 - 生例外の SnackBar 露出を人間可読メッセージに正規化(6ファイル・10箇所) - camera_analysis_mixin: Colors.orange を appColors.warning に置換、 ガミフィケーション色を brandAccent/success/textTertiary に統一 - sake_detail_screen: ハードコード hex 色グラデーションをトークン化 - scan_screen / pdf_preview_screen / add_set_item_dialog: 絵文字 debugPrint を除去 - sake_basic_info_section: unnecessary_non_null_assertion (warning) を解消 - license_service: revoked 永続キャッシュの意図をコメントで明確化 - dart analyze: warning 0 / error 0 Co-Authored-By: Claude Sonnet 4.6 --- lib/screens/camera_analysis_mixin.dart | 68 ++++--- lib/screens/features/sommelier_screen.dart | 5 +- lib/screens/pdf_preview_screen.dart | 21 ++- lib/screens/pending_analysis_screen.dart | 7 +- .../sections/sake_basic_info_section.dart | 4 +- .../widgets/sake_photo_edit_modal.dart | 3 +- lib/screens/sake_detail_screen.dart | 176 +++++++++--------- lib/screens/scan_screen.dart | 4 +- lib/services/license_service.dart | 15 +- lib/widgets/add_set_item_dialog.dart | 2 +- 10 files changed, 160 insertions(+), 145 deletions(-) diff --git a/lib/screens/camera_analysis_mixin.dart b/lib/screens/camera_analysis_mixin.dart index 6d9eed1..154d564 100644 --- a/lib/screens/camera_analysis_mixin.dart +++ b/lib/screens/camera_analysis_mixin.dart @@ -32,6 +32,7 @@ mixin CameraAnalysisMixin on ConsumerState // async gap 前に context 依存オブジェクトをキャプチャ final messenger = ScaffoldMessenger.of(context); final navigator = Navigator.of(context); + final appColors = Theme.of(context).extension()!; final isOnline = await NetworkService.isOnline(); if (!isOnline) { @@ -44,25 +45,25 @@ mixin CameraAnalysisMixin on ConsumerState if (!mounted) return; messenger.showSnackBar( - const SnackBar( + SnackBar( content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Icon(LucideIcons.wifiOff, color: Colors.orange, size: 16), - SizedBox(width: 8), - Text('オフライン検知', style: TextStyle(fontWeight: FontWeight.bold)), + Icon(LucideIcons.wifiOff, color: Colors.white, size: 16), + const SizedBox(width: 8), + const Text('オフライン検知', style: TextStyle(fontWeight: FontWeight.bold)), ], ), - SizedBox(height: 4), - Text('写真を「解析待ち」として保存しました。'), - Text('オンライン復帰後、ホーム画面から解析できます。'), + const SizedBox(height: 4), + const Text('写真を「解析待ち」として保存しました。'), + const Text('オンライン復帰後、ホーム画面から解析できます。'), ], ), - duration: Duration(seconds: 5), - backgroundColor: Colors.orange, + duration: const Duration(seconds: 5), + backgroundColor: appColors.warning, ), ); @@ -72,7 +73,7 @@ mixin CameraAnalysisMixin on ConsumerState debugPrint('Draft save error: $e'); if (!mounted) return; messenger.showSnackBar( - SnackBar(content: Text('Draft保存エラー: $e')), + const SnackBar(content: Text('写真の一時保存に失敗しました。再度お試しください。')), ); return; } @@ -93,11 +94,11 @@ mixin CameraAnalysisMixin on ConsumerState mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ - Icon(LucideIcons.zap, color: Colors.orange, size: 16), - SizedBox(width: 8), - Text('本日のAI解析上限(20回)に達しました', + const Icon(LucideIcons.zap, color: Colors.white, size: 16), + const SizedBox(width: 8), + const Text('本日のAI解析上限(20回)に達しました', style: TextStyle(fontWeight: FontWeight.bold)), ], ), @@ -107,7 +108,7 @@ mixin CameraAnalysisMixin on ConsumerState ], ), duration: const Duration(seconds: 6), - backgroundColor: Colors.orange, + backgroundColor: appColors.warning, ), ); navigator.pop(); // カメラ画面を閉じてホームへ @@ -115,7 +116,7 @@ mixin CameraAnalysisMixin on ConsumerState debugPrint('Draft save error (quota): $e'); if (!mounted) return; messenger.showSnackBar( - SnackBar(content: Text('保存エラー: $e')), + const SnackBar(content: Text('写真の一時保存に失敗しました。再度お試しください。')), ); } return; @@ -224,30 +225,27 @@ mixin CameraAnalysisMixin on ConsumerState navigator.pop(); // Close Camera Screen (Return to Home) // Success Message - final isDark = Theme.of(context).brightness == Brightness.dark; final List messageWidgets = [ Text('${sakeItem.displayData.displayName} を登録しました!'), ]; if (result.isFromCache) { messageWidgets.add(const SizedBox(height: 4)); - messageWidgets.add(const Text( + messageWidgets.add(Text( '※ 解析済みの結果を使用(経験値なし)', - style: TextStyle(fontSize: 12, color: Colors.grey), + style: TextStyle(fontSize: 12, color: appColors.textTertiary), )); } else { messageWidgets.add(const SizedBox(height: 4)); messageWidgets.add(Row( children: [ - Icon(LucideIcons.sparkles, - color: isDark ? Colors.yellow.shade300 : Colors.yellow, - size: 16), + Icon(LucideIcons.sparkles, color: appColors.brandAccent, size: 16), const SizedBox(width: 8), Text( '経験値 +$expGained GET!${isLevelUp ? " (Level UP!)" : ""}', style: TextStyle( fontWeight: FontWeight.bold, - color: isDark ? Colors.yellow.shade200 : Colors.yellowAccent, + color: appColors.brandAccent, ), ), ], @@ -266,7 +264,7 @@ mixin CameraAnalysisMixin on ConsumerState 'バッジ獲得: ${badge.name}', style: TextStyle( fontWeight: FontWeight.bold, - color: isDark ? Colors.green.shade300 : Colors.greenAccent, + color: appColors.success, ), ), ], @@ -297,25 +295,25 @@ mixin CameraAnalysisMixin on ConsumerState if (!mounted) return; navigator.pop(); // Close camera screen messenger.showSnackBar( - const SnackBar( + SnackBar( content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( + const Row( children: [ Icon(LucideIcons.cloudOff, color: Colors.white, size: 16), SizedBox(width: 8), Text('AIサーバー混雑', style: TextStyle(fontWeight: FontWeight.bold)), ], ), - SizedBox(height: 4), - Text('写真を「解析待ち」として保存しました。'), - Text('時間をおいてホーム画面から解析できます。'), + const SizedBox(height: 4), + const Text('写真を「解析待ち」として保存しました。'), + const Text('時間をおいてホーム画面から解析できます。'), ], ), - duration: Duration(seconds: 5), - backgroundColor: Colors.orange, + duration: const Duration(seconds: 5), + backgroundColor: appColors.warning, ), ); } catch (draftError) { @@ -345,7 +343,7 @@ mixin CameraAnalysisMixin on ConsumerState children: [ const Row( children: [ - Icon(LucideIcons.zap, color: Colors.orange, size: 16), + Icon(LucideIcons.zap, color: Colors.white, size: 16), SizedBox(width: 8), Text('本日のAI解析上限(20回)に達しました', style: TextStyle(fontWeight: FontWeight.bold)), @@ -357,7 +355,7 @@ mixin CameraAnalysisMixin on ConsumerState ], ), duration: const Duration(seconds: 6), - backgroundColor: Colors.orange, + backgroundColor: appColors.warning, ), ); } catch (draftError) { @@ -370,10 +368,10 @@ mixin CameraAnalysisMixin on ConsumerState return; } - final appColors = Theme.of(context).extension()!; + debugPrint('Analysis error: $e'); messenger.showSnackBar( SnackBar( - content: Text('解析エラー: $e'), + content: const Text('解析に失敗しました。時間をおいて再試行してください。'), duration: const Duration(seconds: 5), backgroundColor: appColors.error, ), diff --git a/lib/screens/features/sommelier_screen.dart b/lib/screens/features/sommelier_screen.dart index c7e9241..85193fa 100644 --- a/lib/screens/features/sommelier_screen.dart +++ b/lib/screens/features/sommelier_screen.dart @@ -58,8 +58,9 @@ class _SommelierScreenState extends ConsumerState { ); } catch (e) { if (mounted) { + debugPrint('Share error: $e'); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('シェアに失敗しました: $e')), + const SnackBar(content: Text('シェアに失敗しました。再度お試しください。')), ); } } finally { @@ -541,7 +542,7 @@ class _SommelierScreenState extends ConsumerState { debugPrint('Diagnosis Error: $e'); if (!mounted) return; navigator.pop(); - messenger.showSnackBar(SnackBar(content: Text('エラー: $e'))); + messenger.showSnackBar(const SnackBar(content: Text('診断に失敗しました。時間をおいて再試行してください。'))); } } diff --git a/lib/screens/pdf_preview_screen.dart b/lib/screens/pdf_preview_screen.dart index 016b8bd..6ad2c70 100644 --- a/lib/screens/pdf_preview_screen.dart +++ b/lib/screens/pdf_preview_screen.dart @@ -95,7 +95,7 @@ class PdfPreviewScreen extends ConsumerWidget { ], ), loadingWidget: const Center(child: CircularProgressIndicator()), - onError: (context, error) => Center(child: Text('エラーが発生しました: $error')), + onError: (context, error) => const Center(child: Text('PDFの表示に失敗しました')), ), bottomNavigationBar: SafeArea( child: Column( @@ -246,9 +246,10 @@ class PdfPreviewScreen extends ConsumerWidget { filename: 'oshinagaki_${DateTime.now().toString().split(' ')[0]}.pdf', ); } catch (e) { + debugPrint('PDF share error: $e'); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('共有エラー: $e')), + const SnackBar(content: Text('PDFの共有に失敗しました。再度お試しください。')), ); } } @@ -284,7 +285,7 @@ class PdfPreviewScreen extends ConsumerWidget { ..name = fileName ..mimeType = 'application/pdf'; - debugPrint('[PDF_DRIVE] 📤 アップロード開始: $fileName (${bytes.length} bytes)'); + debugPrint('[PDF_DRIVE] Upload start: $fileName (${bytes.length} bytes)'); final uploadedFile = await driveApi.files.create( driveFile, uploadMedia: drive.Media( @@ -294,11 +295,11 @@ class PdfPreviewScreen extends ConsumerWidget { ); if (uploadedFile.id == null) { - debugPrint('[PDF_DRIVE] ❌ アップロード失敗: ID取得不可'); + debugPrint('[PDF_DRIVE] Upload failed: no file ID returned'); throw Exception('アップロードに失敗しました(IDなし)'); } - debugPrint('[PDF_DRIVE] ✅ アップロード完了: ID=${uploadedFile.id}'); + debugPrint('[PDF_DRIVE] Upload complete: ID=${uploadedFile.id}'); // 5. Success notification if (context.mounted) { @@ -314,11 +315,12 @@ class PdfPreviewScreen extends ConsumerWidget { ); } } catch (e) { + debugPrint('Drive upload error: $e'); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Driveアップロードエラー: $e'), - duration: const Duration(seconds: 4), + const SnackBar( + content: Text('Google Driveへの保存に失敗しました。再度お試しください。'), + duration: Duration(seconds: 4), ), ); } @@ -336,9 +338,10 @@ class PdfPreviewScreen extends ConsumerWidget { format: _getPageFormat(pdfSize, ref.read(pdfIsPortraitProvider)), ); } catch (e) { + debugPrint('Print error: $e'); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('印刷エラー: $e')), + const SnackBar(content: Text('印刷に失敗しました。再度お試しください。')), ); } } diff --git a/lib/screens/pending_analysis_screen.dart b/lib/screens/pending_analysis_screen.dart index b2ad4e2..4e13d86 100644 --- a/lib/screens/pending_analysis_screen.dart +++ b/lib/screens/pending_analysis_screen.dart @@ -194,10 +194,11 @@ class _PendingAnalysisScreenState extends ConsumerState { } } catch (e) { if (!mounted) return; + debugPrint('Draft delete error: $e'); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('削除エラー: $e'), - duration: const Duration(seconds: 5), + const SnackBar( + content: Text('削除に失敗しました。再度お試しください。'), + duration: Duration(seconds: 5), ), ); } diff --git a/lib/screens/sake_detail/sections/sake_basic_info_section.dart b/lib/screens/sake_detail/sections/sake_basic_info_section.dart index 1cc620e..ca241f8 100644 --- a/lib/screens/sake_detail/sections/sake_basic_info_section.dart +++ b/lib/screens/sake_detail/sections/sake_basic_info_section.dart @@ -101,7 +101,7 @@ class SakeBasicInfoSection extends ConsumerWidget { decoration: BoxDecoration( color: badgeColor!.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(20), - border: Border.all(color: badgeColor!.withValues(alpha: 0.4)), + border: Border.all(color: badgeColor.withValues(alpha: 0.4)), ), child: Row( mainAxisSize: MainAxisSize.min, @@ -109,7 +109,7 @@ class SakeBasicInfoSection extends ConsumerWidget { Icon(LucideIcons.brainCircuit, size: 12, color: badgeColor), const SizedBox(width: 4), Text( - '$mbtiType ${mbtiResult!.starDisplay}', + '$mbtiType ${mbtiResult.starDisplay}', style: TextStyle( color: badgeColor, fontWeight: FontWeight.bold, diff --git a/lib/screens/sake_detail/widgets/sake_photo_edit_modal.dart b/lib/screens/sake_detail/widgets/sake_photo_edit_modal.dart index aadbf07..30bf091 100644 --- a/lib/screens/sake_detail/widgets/sake_photo_edit_modal.dart +++ b/lib/screens/sake_detail/widgets/sake_photo_edit_modal.dart @@ -234,9 +234,10 @@ class _SakePhotoEditModalState extends State { await _saveNewPhoto(savedPath); } catch (e) { + debugPrint('Photo pick error: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('エラー: $e')), + const SnackBar(content: Text('写真の追加に失敗しました。再度お試しください。')), ); } } diff --git a/lib/screens/sake_detail_screen.dart b/lib/screens/sake_detail_screen.dart index a76265c..187f112 100644 --- a/lib/screens/sake_detail_screen.dart +++ b/lib/screens/sake_detail_screen.dart @@ -36,10 +36,10 @@ class SakeDetailScreen extends ConsumerStatefulWidget { } class _SakeDetailScreenState extends ConsumerState { - // To trigger rebuilds if we don't switch to a stream late SakeItem _sake; int _currentImageIndex = 0; - // Memo logic moved to SakeDetailMemo + bool _isAnalyzing = false; + DateTime? _quotaLockoutTime; @override @@ -119,15 +119,12 @@ class _SakeDetailScreenState extends ConsumerState { gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: Theme.of(context).brightness == Brightness.dark - ? [ - const Color(0xFF121212), // Scaffold Background - const Color(0xFF1E1E1E), // Slightly lighter surface - ] - : [ - Theme.of(context).scaffoldBackgroundColor, - Theme.of(context).primaryColor.withValues(alpha: 0.05), - ], + colors: [ + Theme.of(context).scaffoldBackgroundColor, + Theme.of(context).brightness == Brightness.dark + ? appColors.brandSurface + : Theme.of(context).primaryColor.withValues(alpha: 0.05), + ], ), ), padding: const EdgeInsets.all(24.0), @@ -281,10 +278,6 @@ class _SakeDetailScreenState extends ConsumerState { ); } - bool _isAnalyzing = false; - DateTime? _quotaLockoutTime; - - Future _toggleFavorite() async { HapticFeedback.mediumImpact(); final box = Hive.box('sake_items'); @@ -401,8 +394,9 @@ class _SakeDetailScreenState extends ConsumerState { }); } + debugPrint('Reanalyze error: $e'); messenger.showSnackBar( - SnackBar(content: Text('エラー: $e')), + const SnackBar(content: Text('再解析に失敗しました。時間をおいて再試行してください。')), ); } } finally { @@ -413,11 +407,11 @@ class _SakeDetailScreenState extends ConsumerState { } - void _showTagEditDialog(BuildContext context) { + Future _showTagEditDialog(BuildContext context) async { final TextEditingController tagController = TextEditingController(); final allTags = _sake.hiddenSpecs.flavorTags.toSet(); - - showDialog( + try { + await showDialog( context: context, builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { @@ -498,6 +492,9 @@ class _SakeDetailScreenState extends ConsumerState { } ), ); + } finally { + tagController.dispose(); + } } Future _updateTags(List newTags) async { @@ -581,33 +578,37 @@ class _SakeDetailScreenState extends ConsumerState { required Future Function(String) onSave, }) async { final controller = TextEditingController(text: initialValue); - await showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text(title), - content: TextField( - controller: controller, - decoration: const InputDecoration( - border: OutlineInputBorder(), + try { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(title), + content: TextField( + controller: controller, + decoration: const InputDecoration( + border: OutlineInputBorder(), + ), + autofocus: true, + maxLines: null, ), - autofocus: true, - maxLines: null, + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () async { + await onSave(controller.text); + if (context.mounted) Navigator.pop(context); + }, + child: const Text('保存'), + ), + ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('キャンセル'), - ), - ElevatedButton( - onPressed: () async { - await onSave(controller.text); - if (context.mounted) Navigator.pop(context); - }, - child: const Text('保存'), - ), - ], - ), - ); + ); + } finally { + controller.dispose(); + } } /// MBTI相性詳細ダイアログを表示 @@ -732,52 +733,57 @@ class _SakeDetailScreenState extends ConsumerState { Future _showBreweryEditDialog(BuildContext context) async { final breweryController = TextEditingController(text: _sake.displayData.displayBrewery); final prefectureController = TextEditingController(text: _sake.displayData.displayPrefecture); - await showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('酒蔵・都道府県を編集'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: breweryController, - decoration: const InputDecoration( - labelText: '酒蔵', - border: OutlineInputBorder(), + try { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('酒蔵・都道府県を編集'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: breweryController, + decoration: const InputDecoration( + labelText: '酒蔵', + border: OutlineInputBorder(), + ), ), + const SizedBox(height: 16), + TextField( + controller: prefectureController, + decoration: const InputDecoration( + labelText: '都道府県', + border: OutlineInputBorder(), + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('キャンセル'), ), - const SizedBox(height: 16), - TextField( - controller: prefectureController, - decoration: const InputDecoration( - labelText: '都道府県', - border: OutlineInputBorder(), - ), + ElevatedButton( + onPressed: () async { + final box = Hive.box('sake_items'); + final updated = _sake.copyWith( + brand: breweryController.text, + prefecture: prefectureController.text, + isUserEdited: true, + ); + await box.put(_sake.key, updated); + setState(() => _sake = updated); + if (context.mounted) Navigator.pop(context); + }, + child: const Text('保存'), ), ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('キャンセル'), - ), - ElevatedButton( - onPressed: () async { - final box = Hive.box('sake_items'); - final updated = _sake.copyWith( - brand: breweryController.text, - prefecture: prefectureController.text, - isUserEdited: true, - ); - await box.put(_sake.key, updated); - setState(() => _sake = updated); - if (context.mounted) Navigator.pop(context); - }, - child: const Text('保存'), - ), - ], - ), - ); + ); + } finally { + breweryController.dispose(); + prefectureController.dispose(); + } } /// 写真編集モーダルを表示 diff --git a/lib/screens/scan_screen.dart b/lib/screens/scan_screen.dart index 4017072..3209521 100644 --- a/lib/screens/scan_screen.dart +++ b/lib/screens/scan_screen.dart @@ -77,10 +77,10 @@ class _ScanARScreenState extends ConsumerState _isInitializing = false; }); } - debugPrint('✅ Scanner: Controller created successfully'); + debugPrint('[Scanner] Controller created successfully'); } catch (e) { - debugPrint('❌ Scanner: Error during initialization: $e'); + debugPrint('[Scanner] Error during initialization: $e'); if (mounted) { setState(() { _isInitializing = false; diff --git a/lib/services/license_service.dart b/lib/services/license_service.dart index 79c1734..63ce4d9 100644 --- a/lib/services/license_service.dart +++ b/lib/services/license_service.dart @@ -148,17 +148,22 @@ class LicenseService { if (cached == null) return LicenseStatus.free; - // キャッシュが古すぎる場合はfreeにフォールバック - // pro と revoked は期限切れにしない(proは購入者を締め出さない、revokedは誤って復活させない) + // オンライン時は _validateKeyWithServer が常に上書きするため、 + // _getCachedStatus はオフライン時専用のフォールバックとして動作する。 + // + // TTL 判定(_cacheValidSeconds = 24h): + // - free / offline は期限切れで free にフォールバック + // - pro : 購入者をオフライン時に締め出さないため永続扱い + // - revoked: 不正防止を優先するため永続扱い + // (将来 TTL を設けたい場合は isNoExpiryStatus を条件分岐ごと差し替える) if (cachedAt != null) { final age = DateTime.now().difference(DateTime.parse(cachedAt)); - final isPermanentStatus = cached == LicenseStatus.pro.name || cached == LicenseStatus.revoked.name; - if (age.inSeconds > _cacheValidSeconds && !isPermanentStatus) { + final isNoExpiryStatus = cached == LicenseStatus.pro.name || cached == LicenseStatus.revoked.name; + if (age.inSeconds > _cacheValidSeconds && !isNoExpiryStatus) { return LicenseStatus.free; } } - // Pro キャッシュはオフラインでも維持(購入者を締め出さない) return LicenseStatus.values.firstWhere( (s) => s.name == cached, orElse: () => LicenseStatus.free, diff --git a/lib/widgets/add_set_item_dialog.dart b/lib/widgets/add_set_item_dialog.dart index eaface9..ea79b35 100644 --- a/lib/widgets/add_set_item_dialog.dart +++ b/lib/widgets/add_set_item_dialog.dart @@ -104,7 +104,7 @@ class _AddSetItemDialogState extends ConsumerState { final newlyUnlockedBadges = await GamificationService.checkAndUnlockBadges(ref); if (newlyUnlockedBadges.isNotEmpty) { - debugPrint('🏅 Badges Unlocked: ${newlyUnlockedBadges.map((b) => b.name).join(", ")}'); + debugPrint('[Gamification] Badges Unlocked: ${newlyUnlockedBadges.map((b) => b.name).join(", ")}'); } if (mounted) {