Repository: Pemuda-Pembuka-Langkah/pockeat-mobile
Pull request #376: Improvement on Homepage loading and refactor skeletonizer
Author: login: DwikyAhmad
Created: 2025-05-20T08:57:18Z | Merged: 2025-05-20T09:24:35Z

This pr changes about the implementation of fetching in pet service and exercise log by removing duplicate query and the use of concurrency for faster loads, the homepage also now uses skeletonizer which automatically creates skeleton loading which is more better than creating from scratch using a base skeleton widget

Screenshots:
<div>
<img src='https://github.com/user-attachments/assets/7f6b7059-1ab2-468b-a5f2-147654aefe55' width=300>
<img src='https://github.com/user-attachments/assets/a88d991d-7a39-4119-b7ef-0f0332a36243' width=500>
<img src='https://github.com/user-attachments/assets/1fafdb34-8740-4926-9ee5-b5d82e2b04e9' width=500>  
<div>

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Refactor**
  - Improved homepage data loading by consolidating asynchronous fetches for better performance and added timing logs.
  - Replaced custom skeleton loading widgets with a unified skeleton loading experience using the Skeletonizer package.
  - Removed all manual loading state flags and conditional rendering of skeleton placeholders from homepage sections.
  - Streamlined calorie and pet information retrieval logic for efficiency.

- **Chores**
  - Updated dependencies: replaced the shimmer package with skeletonizer for loading animations.

- **Bug Fixes**
  - Ensured proper resource cleanup on the homepage by disposing of the scroll controller.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/lib/features/calorie_stats/services/calorie_stats_service.dart b/lib/features/calorie_stats/services/calorie_stats_service.dart
index 1d651a33..cdc2b656 100644
--- a/lib/features/calorie_stats/services/calorie_stats_service.dart
+++ b/lib/features/calorie_stats/services/calorie_stats_service.dart
@@ -71,10 +71,6 @@ class CalorieStatsServiceImpl implements CalorieStatsService {
     // Get exercise logs for the day
     final exerciseLogs =
         await _exerciseService.getExerciseLogsByDate(userId, date);
-    for (var log in exerciseLogs) {
-      int caloriesBurned = log.caloriesBurned.toInt();
-      debugPrint("exerciseLog: $caloriesBurned");
-    }
 
     // Calculate calories burned from exercise logs
     final logCaloriesBurned = exerciseLogs.fold<int>(
diff --git a/lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart b/lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart
index 23868814..487617c3 100644
--- a/lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart
+++ b/lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart
@@ -74,40 +74,33 @@ class ExerciseLogHistoryServiceImpl implements ExerciseLogHistoryService {
   Future<List<ExerciseLogHistoryItem>> getExerciseLogsByDate(
       String userId, DateTime date) async {
     try {
-      // Get smart exercise logs for this date and user
-      final smartLogs =
-          await _smartExerciseLogRepository.getAnalysisResultsByDate(date);
-      final filteredSmartLogs =
-          smartLogs.where((log) => log.userId == userId).toList();
-      final smartItems = _convertSmartExerciseLogs(filteredSmartLogs);
+      final results = await Future.wait([
+        _smartExerciseLogRepository.getAnalysisResultsByDate(date),
+        _cardioRepository.filterByDate(date),
+        _weightLiftingRepository.filterByDate(date),
+      ]);
+
+      final smartLogs = results[0] as List<ExerciseAnalysisResult>;
+      final cardioLogs = results[1] as List<CardioActivity>;
+      final weightLiftingLogs = results[2] as List<WeightLifting>;
+
+      final smartItems = _convertSmartExerciseLogs(
+          smartLogs.where((log) => log.userId == userId).toList());
+      final cardioItems = _convertCardioLogs(
+          cardioLogs.where((log) => log.userId == userId).toList());
+      final weightLiftingItems = _convertWeightLiftingLogs(
+          weightLiftingLogs.where((log) => log.userId == userId).toList());
 
-      // Get cardio activities for this date
-      final cardioLogs = await _cardioRepository.filterByDate(date);
-      final filteredCardioLogs =
-          cardioLogs.where((log) => log.userId == userId).toList();
-      final cardioItems = _convertCardioLogs(filteredCardioLogs);
-
-      // Get weightlifting exercises for this date
-      final weightLiftingLogs =
-          await _weightLiftingRepository.filterByDate(date);
-      final filteredWeightLiftingLogs =
-          weightLiftingLogs.where((log) => log.userId == userId).toList();
-      final weightLiftingItems =
-          _convertWeightLiftingLogs(filteredWeightLiftingLogs);
-
-      // Combine all logs
       final allLogs = [
         ...smartItems,
         ...cardioItems,
         ...weightLiftingItems,
       ];
 
-      // Sort by timestamp (newest first)
       allLogs.sort((a, b) => b.timestamp.compareTo(a.timestamp));
       debugPrint("allLogs: $allLogs");
       return allLogs;
     } catch (e) {
-      // Return empty list instead of throwing
       return [];
     }
   }
diff --git a/lib/features/homepage/presentation/loading_skeleton/calories_today_skeleton.dart b/lib/features/homepage/presentation/loading_skeleton/calories_today_skeleton.dart
deleted file mode 100644
index d0ba3723..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/calories_today_skeleton.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Project imports:
-import 'skeleton_loading.dart';
-
-class CaloriesTodaySkeleton extends StatelessWidget {
-  const CaloriesTodaySkeleton({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return const SkeletonLoading(
-        width: double.infinity, height: 350, borderRadius: 15);
-  }
-}
diff --git a/lib/features/homepage/presentation/loading_skeleton/health_connect_skeleton.dart b/lib/features/homepage/presentation/loading_skeleton/health_connect_skeleton.dart
deleted file mode 100644
index 74857f84..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/health_connect_skeleton.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Project imports:
-import 'skeleton_loading.dart';
-
-class HealthConnectSkeleton extends StatelessWidget {
-  const HealthConnectSkeleton({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.all(16),
-      child: const Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        crossAxisAlignment: CrossAxisAlignment.center,
-        children: [
-          SkeletonLoading(width: double.infinity, height: 60, borderRadius: 15),
-          SizedBox(height: 16),
-          SkeletonLoading(width: double.infinity, height: 70, borderRadius: 15),
-          SizedBox(height: 16),
-          SkeletonLoading(width: double.infinity, height: 70, borderRadius: 15),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/features/homepage/presentation/loading_skeleton/heart_bar_skeleton.dart b/lib/features/homepage/presentation/loading_skeleton/heart_bar_skeleton.dart
deleted file mode 100644
index a800f00a..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/heart_bar_skeleton.dart
+++ /dev/null
@@ -1,62 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Project imports:
-import 'skeleton_loading.dart';
-
-class HeartBarSkeleton extends StatelessWidget {
-  const HeartBarSkeleton({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.all(16),
-      decoration: BoxDecoration(
-        color: Colors.white,
-        borderRadius: BorderRadius.circular(12),
-        boxShadow: const [
-          BoxShadow(
-            color: Colors.black12,
-            blurRadius: 5,
-            offset: Offset(0, 2),
-          ),
-        ],
-      ),
-      child: Column(
-        children: [
-          // Title skeleton
-          Row(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: [
-              const SkeletonLoading(height: 16, width: 100),
-              const SizedBox(width: 8),
-              Container(
-                width: 18,
-                height: 18,
-                decoration: const BoxDecoration(
-                  color: Colors.grey,
-                  shape: BoxShape.circle,
-                ),
-              ),
-            ],
-          ),
-          const SizedBox(height: 12),
-          // Hearts row
-          Row(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: List.generate(
-              4,
-              (index) => Container(
-                margin: const EdgeInsets.symmetric(horizontal: 4),
-                child: const SkeletonLoading(height: 32, width: 32),
-              ),
-            ),
-          ),
-          const SizedBox(height: 10),
-          // Skeleton for status text
-          const SkeletonLoading(height: 16, width: 160),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/features/homepage/presentation/loading_skeleton/nutrient_card_skeleton.dart b/lib/features/homepage/presentation/loading_skeleton/nutrient_card_skeleton.dart
deleted file mode 100644
index fce32b36..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/nutrient_card_skeleton.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Project imports:
-import 'skeleton_loading.dart';
-
-class NutrientCardSkeleton extends StatelessWidget {
-  const NutrientCardSkeleton({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.all(16),
-      decoration: BoxDecoration(
-        color: Colors.white,
-        borderRadius: BorderRadius.circular(12),
-        boxShadow: const [
-          BoxShadow(
-            color: Colors.black12,
-            blurRadius: 5,
-            offset: Offset(0, 2),
-          ),
-        ],
-      ),
-      child: const Column(
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: [
-          Row(
-            children: [
-              SkeletonLoading(width: 24, height: 24),
-              SizedBox(width: 8),
-              Expanded(
-                child: SkeletonLoading(width: 32, height: 16),
-              ),
-            ],
-          ),
-          SizedBox(height: 12),
-          SkeletonLoading(width: 80, height: 24),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/features/homepage/presentation/loading_skeleton/pet_companion_skeleton.dart b/lib/features/homepage/presentation/loading_skeleton/pet_companion_skeleton.dart
deleted file mode 100644
index ca089def..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/pet_companion_skeleton.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Project imports:
-import 'skeleton_loading.dart';
-
-class PetCompanionSkeleton extends StatelessWidget {
-  const PetCompanionSkeleton({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return const Column(children: [
-      SkeletonLoading(
-        width: double.infinity,
-        height: 350,
-        borderRadius: 45,
-      ),
-      SizedBox(
-        height: 20,
-      ),
-      SizedBox(
-        height: 30,
-        width: double.infinity,
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: [
-            SkeletonLoading(width: 100, height: 30),
-          ],
-        ),
-      ),
-    ]);
-  }
-}
diff --git a/lib/features/homepage/presentation/loading_skeleton/skeleton_loading.dart b/lib/features/homepage/presentation/loading_skeleton/skeleton_loading.dart
deleted file mode 100644
index 940b5b89..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/skeleton_loading.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Package imports:
-import 'package:shimmer/shimmer.dart';
-
-class SkeletonLoading extends StatelessWidget {
-  final double width;
-  final double height;
-  final double borderRadius;
-
-  const SkeletonLoading({
-    super.key,
-    required this.width,
-    required this.height,
-    this.borderRadius = 8,
-  });
-
-  @override
-  Widget build(BuildContext context) {
-    return Shimmer.fromColors(
-      baseColor: Colors.grey[300]!,
-      highlightColor: Colors.grey[100]!,
-      child: Container(
-        width: width,
-        height: height,
-        decoration: BoxDecoration(
-          color: Colors.white,
-          borderRadius: BorderRadius.circular(borderRadius),
-        ),
-      ),
-    );
-  }
-}
diff --git a/lib/features/homepage/presentation/loading_skeleton/streak_counter_skeleton.dart b/lib/features/homepage/presentation/loading_skeleton/streak_counter_skeleton.dart
deleted file mode 100644
index b45849d4..00000000
--- a/lib/features/homepage/presentation/loading_skeleton/streak_counter_skeleton.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-// Flutter imports:
-import 'package:flutter/material.dart';
-
-// Project imports:
-import 'skeleton_loading.dart';
-
-class StreakCounterSkeleton extends StatelessWidget {
-  const StreakCounterSkeleton({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      height: 90,
-      padding: const EdgeInsets.all(16),
-      decoration: BoxDecoration(
-        color: Colors.white,
-        borderRadius: BorderRadius.circular(15),
-        boxShadow: const [
-          BoxShadow(
-            color: Colors.black12,
-            blurRadius: 5,
-            offset: Offset(0, 2),
-          ),
-        ],
-      ),
-      child: const Row(
-        mainAxisAlignment: MainAxisAlignment.start,
-        children: [
-          SkeletonLoading(width: 24, height: 24),
-          SizedBox(width: 15),
-          Column(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: [
-              SkeletonLoading(width: 150, height: 15),
-              SizedBox(height: 10),
-              SkeletonLoading(width: 150, height: 15),
-            ],
-          ),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/features/homepage/presentation/screens/homepage.dart b/lib/features/homepage/presentation/screens/homepage.dart
index ae4a50dc..a0c33caf 100644
--- a/lib/features/homepage/presentation/screens/homepage.dart
+++ b/lib/features/homepage/presentation/screens/homepage.dart
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
 // Package imports:
 import 'package:firebase_auth/firebase_auth.dart';
 import 'package:get_it/get_it.dart';
+import 'package:skeletonizer/skeletonizer.dart';
 
 // Project imports:
 import 'package:pockeat/component/navigation.dart';
@@ -61,24 +62,74 @@ class _HomePageState extends State<HomePage>
     _loadData();
   }
 
+  @override
+  void dispose() {
+    _scrollController.dispose();
+    super.dispose();
+  }
+
   void _loadData() {
-    _petInformation = _petService.getPetInformation(userId);
-    _dayStreak = _foodLogHistoryService.getFoodStreakDays(userId);
-    _statsFuture =
-        _calorieStatsService.calculateStatsForDate(userId, DateTime.now());
-    _targetCalories = caloricRequirementRepository
+    _petInformation = _measureFutureTime(
+      'Pet Information', 
+      _petService.getPetInformation(userId)
+    );
+
+    _dayStreak = _measureFutureTime(
+      'Day Streak', 
+      _foodLogHistoryService.getFoodStreakDays(userId)
+    );
+
+    _statsFuture = _measureFutureTime(
+      'Stats',
+      _calorieStatsService.calculateStatsForDate(userId, DateTime.now())
+    );
+
+    _targetCalories = _measureFutureTime(
+      'Target Calories',
+      caloricRequirementRepository
         .getCaloricRequirement(userId)
-        .then((value) => value?.tdee.toInt() ?? 0);
-    _rolloverCaloriesFuture = preferencesService.getRolloverCalories();
-    _isCalorieCompensationEnabledFuture =
-        preferencesService.isExerciseCalorieCompensationEnabled();
-    _isRolloverCaloriesEnabledFuture =
-        preferencesService.isRolloverCaloriesEnabled();
+        .then((value) => value?.tdee.toInt() ?? 0)
+    );
+
+    _rolloverCaloriesFuture = _measureFutureTime(
+      'Rollover Calories',
+      preferencesService.getRolloverCalories()
+    );
+
+    _isCalorieCompensationEnabledFuture = _measureFutureTime(
+      'Calorie Compensation Setting',
+      preferencesService.isExerciseCalorieCompensationEnabled()
+    );
+
+    _isRolloverCaloriesEnabledFuture = _measureFutureTime(
+      'Rollover Calories Setting',
+      preferencesService.isRolloverCaloriesEnabled()
+    );
 
     // Initialize the new futures
-    _caloricRequirementModelFuture =
-        caloricRequirementRepository.getCaloricRequirement(userId);
-    _currentMacrosFuture = _calculateCurrentMacros();
+    _caloricRequirementModelFuture = _measureFutureTime(
+      'Caloric Requirement Model',
+      caloricRequirementRepository.getCaloricRequirement(userId)
+    );
+
+    _currentMacrosFuture = _measureFutureTime(
+      'Current Macros',
+      _calculateCurrentMacros()
+    );
+  }
+
+  // Helper method to measure the time taken by a Future
+  Future<T> _measureFutureTime<T>(String name, Future<T> future) {
+    final stopwatch = Stopwatch()..start();
+    return future.then((value) {
+      stopwatch.stop();
+      debugPrint('⏱️ $name fetch completed in ${stopwatch.elapsed.inMilliseconds}ms');
+      return value;
+    }).catchError((error) {
+      stopwatch.stop();
+      debugPrint('❌ $name fetch failed after ${stopwatch.elapsed.inMilliseconds}ms: $error');
+      throw error;
+    });
   }
 
   // New method to calculate current macronutrients consumed today
@@ -107,6 +158,29 @@ class _HomePageState extends State<HomePage>
       return {'protein': 0, 'carbs': 0, 'fat': 0};
     }
   }
+
+  Future<List<dynamic>> _getHomePageFutures() async {
+    debugPrint('🕒 Homepage data fetch started at ${DateTime.now()}');
+    final stopwatch = Stopwatch()..start();
+    
+    final result = await Future.wait([
+      _petInformation,
+      _statsFuture,
+      _dayStreak,
+      _targetCalories,
+      _isCalorieCompensationEnabledFuture,
+      _isRolloverCaloriesEnabledFuture,
+      _rolloverCaloriesFuture,
+      _currentMacrosFuture,
+      _caloricRequirementModelFuture,
+    ]);
+    
+    stopwatch.stop();
+    debugPrint('⏱️ Homepage data fetch completed in ${stopwatch.elapsed.inMilliseconds}ms');
+    
+    return result;
+  }
+
   // coverage:ignore-start
   // Show exit confirmation dialog
   Future<bool> _onWillPop() async {
@@ -166,18 +240,11 @@ class _HomePageState extends State<HomePage>
             ),
           ],
           body: FutureBuilder<List<dynamic>>(
-            future: Future.wait([
-              _petInformation,
-              _statsFuture,
-              _dayStreak,
-              _targetCalories,
-              _isCalorieCompensationEnabledFuture,
-              _isRolloverCaloriesEnabledFuture,
-              _rolloverCaloriesFuture,
-              _currentMacrosFuture,
-              _caloricRequirementModelFuture,
-            ]),
+            future: _getHomePageFutures(),
             builder: (context, snapshot) {
+              // Use skeletonizer for loading state
+              final bool isLoading = snapshot.connectionState == ConnectionState.waiting;
+              
               return RefreshIndicator(
                 onRefresh: () async {
                   setState(() {
@@ -186,31 +253,39 @@ class _HomePageState extends State<HomePage>
                 },
                 color: primaryPink, // Use app color theme
                 backgroundColor: Colors.white,
-                child: ListView(
-                  padding:
-                      const EdgeInsets.symmetric(horizontal: 0, vertical: 20),
-                  children: [
-                    PetHomepageSection(
-                      isLoading:
-                          snapshot.connectionState == ConnectionState.waiting,
-                      petInfo: snapshot.data?[0] as PetInformation?,
-                      stats: snapshot.data?[1] as DailyCalorieStats?,
-                      streakDays: snapshot.data?[2] as int?,
-                      targetCalories: snapshot.data?[3] as int?,
-                    ),
-                    OverviewSection(
-                      isLoading:
-                          snapshot.connectionState == ConnectionState.waiting,
-                      stats: snapshot.data?[1] as DailyCalorieStats?,
-                      targetCalories: snapshot.data?[3] as int?,
-                      isCalorieCompensationEnabled: snapshot.data?[4] as bool?,
-                      isRolloverCaloriesEnabled: snapshot.data?[5] as bool?,
-                      rolloverCalories: snapshot.data?[6] as int?,
-                      currentMacros: snapshot.data?[7] as Map<String, int>?,
-                      targetMacros:
-                          snapshot.data?[8] as CaloricRequirementModel?,
-                    ),
-                  ],
+                child: Skeletonizer(
+                  enabled: isLoading,
+                  // Optimize skeletonizer for better performance
+                  effect: const ShimmerEffect(
+                    baseColor: Color(0xFFE0E0E0),
+                    highlightColor: Color(0xFFF5F5F5), 
+                    duration: Duration(milliseconds: 1500),
+                  ),
+                  // Ignore problematic widgets
+                  ignoreContainers: false,
+                  // Customize skeleton appearance
+                  justifyMultiLineText: true,
+                  containersColor: const Color(0xFFEEEEEE),
+                  child: ListView(
+                    padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20),
+                    children: [
+                      PetHomepageSection(
+                        petInfo: snapshot.data?[0] as PetInformation?,
+                        stats: snapshot.data?[1] as DailyCalorieStats?,
+                        streakDays: snapshot.data?[2] as int?,
+                        targetCalories: snapshot.data?[3] as int?,
+                      ),
+                      OverviewSection(
+                        stats: snapshot.data?[1] as DailyCalorieStats?,
+                        targetCalories: snapshot.data?[3] as int?,
+                        isCalorieCompensationEnabled: snapshot.data?[4] as bool?,
+                        isRolloverCaloriesEnabled: snapshot.data?[5] as bool?,
+                        rolloverCalories: snapshot.data?[6] as int?,
+                        currentMacros: snapshot.data?[7] as Map<String, int>?,
+                        targetMacros: snapshot.data?[8] as CaloricRequirementModel?,
+                      ),
+                    ],
+                  ),
                 ),
               );
             },
diff --git a/lib/features/homepage/presentation/screens/overview_section.dart b/lib/features/homepage/presentation/screens/overview_section.dart
index eeb7cf2a..6f838ed5 100644
--- a/lib/features/homepage/presentation/screens/overview_section.dart
+++ b/lib/features/homepage/presentation/screens/overview_section.dart
@@ -4,16 +4,12 @@ import 'package:flutter/material.dart';
 // Project imports:
 import 'package:pockeat/features/caloric_requirement/domain/models/caloric_requirement_model.dart';
 import 'package:pockeat/features/calorie_stats/domain/models/daily_calorie_stats.dart';
-import 'package:pockeat/features/homepage/presentation/loading_skeleton/calories_today_skeleton.dart';
-import 'package:pockeat/features/homepage/presentation/loading_skeleton/health_connect_skeleton.dart';
-import 'package:pockeat/features/homepage/presentation/loading_skeleton/nutrient_card_skeleton.dart';
 import 'package:pockeat/features/homepage/presentation/widgets/calories_today_widget.dart';
 import 'package:pockeat/features/sync_fitness_tracker/widgets/health_connect_widget.dart';
 
 class OverviewSection extends StatefulWidget {
   final DailyCalorieStats? stats;
   final int? targetCalories;
-  final bool isLoading;
   final bool? isCalorieCompensationEnabled;
   final bool? isRolloverCaloriesEnabled;
   final int? rolloverCalories;
@@ -22,7 +18,6 @@ class OverviewSection extends StatefulWidget {
 
   const OverviewSection({
     super.key,
-    this.isLoading = false,
     this.stats,
     this.targetCalories,
     this.isCalorieCompensationEnabled,
@@ -85,23 +80,19 @@ class _OverviewSectionState extends State<OverviewSection> {
   }
 
   Widget _buildCaloriesToday() {
-    return widget.isLoading
-        ? const CaloriesTodaySkeleton()
-        : CaloriesTodayWidget(
-            stats: widget.stats,
-            targetCalories: widget.targetCalories ?? 0,
-            isCalorieCompensationEnabled:
-                widget.isCalorieCompensationEnabled ?? false,
-            isRolloverCaloriesEnabled:
-                widget.isRolloverCaloriesEnabled ?? false,
-            rolloverCalories: widget.rolloverCalories ?? 0,
-          );
+    return CaloriesTodayWidget(
+      stats: widget.stats,
+      targetCalories: widget.targetCalories ?? 0,
+      isCalorieCompensationEnabled:
+          widget.isCalorieCompensationEnabled ?? false,
+      isRolloverCaloriesEnabled:
+          widget.isRolloverCaloriesEnabled ?? false,
+      rolloverCalories: widget.rolloverCalories ?? 0,
+    );
   }
 
   Widget _buildFitnessTrackerSection() {
-    return widget.isLoading
-        ? const HealthConnectSkeleton()
-        : const HealthConnectWidget();
+    return const HealthConnectWidget();
   }
 
   Widget _buildNutrientCard({
@@ -118,73 +109,69 @@ class _OverviewSectionState extends State<OverviewSection> {
     final displayIconColor = isExceeded ? warningColor : iconColor;
     final displayBgColor = isExceeded ? warningColor.withOpacity(0.1) : bgColor;
 
-    return widget.isLoading
-        ? const NutrientCardSkeleton()
-        : Container(
-            padding: const EdgeInsets.all(16),
-            decoration: BoxDecoration(
-              color: Colors.white,
-              borderRadius: BorderRadius.circular(12),
-              boxShadow: const [
-                BoxShadow(
-                  color: Colors.black12,
-                  blurRadius: 5,
-                  offset: Offset(0, 2),
+    return Container(
+      padding: const EdgeInsets.all(16),
+      decoration: BoxDecoration(
+        color: Colors.white,
+        borderRadius: BorderRadius.circular(12),
+        boxShadow: const [
+          BoxShadow(
+            color: Colors.black12,
+            blurRadius: 5,
+            offset: Offset(0, 2),
+          ),
+        ],
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              Container(
+                padding: const EdgeInsets.all(6),
+                decoration: BoxDecoration(
+                  color: displayBgColor,
+                  borderRadius: BorderRadius.circular(8),
                 ),
-              ],
-            ),
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.start,
+                child: Icon(displayIcon, color: displayIconColor, size: 12),
+              ),
+              const SizedBox(width: 4),
+              Text(
+                title,
+                style: TextStyle(
+                  color: isExceeded ? warningColor : Colors.black54,
+                  fontSize: 12,
+                  fontWeight: isExceeded ? FontWeight.bold : FontWeight.normal,
+                ),
+              ),
+            ],
+          ),
+          const SizedBox(height: 12),
+          RichText(
+            text: TextSpan(
               children: [
-                Row(
-                  children: [
-                    Container(
-                      padding: const EdgeInsets.all(6),
-                      decoration: BoxDecoration(
-                        color: displayBgColor,
-                        borderRadius: BorderRadius.circular(8),
-                      ),
-                      child:
-                          Icon(displayIcon, color: displayIconColor, size: 12),
-                    ),
-                    const SizedBox(width: 4),
-                    Text(
-                      title,
-                      style: TextStyle(
-                        color: isExceeded ? warningColor : Colors.black54,
-                        fontSize: 12,
-                        fontWeight:
-                            isExceeded ? FontWeight.bold : FontWeight.normal,
-                      ),
-                    ),
-                  ],
+                TextSpan(
+                  text: current,
+                  style: TextStyle(
+                    color: isExceeded ? warningColor : Colors.black87,
+                    fontSize: 20,
+                    fontWeight: FontWeight.bold,
+                  ),
                 ),
-                const SizedBox(height: 12),
-                RichText(
-                  text: TextSpan(
-                    children: [
-                      TextSpan(
-                        text: current,
-                        style: TextStyle(
-                          color: isExceeded ? warningColor : Colors.black87,
-                          fontSize: 20,
-                          fontWeight: FontWeight.bold,
-                        ),
-                      ),
-                      TextSpan(
-                        text: '/$target',
-                        style: const TextStyle(
-                          color: Colors.black38,
-                          fontSize: 14,
-                          fontWeight: FontWeight.w500,
-                        ),
-                      ),
-                    ],
+                TextSpan(
+                  text: '/$target',
+                  style: const TextStyle(
+                    color: Colors.black38,
+                    fontSize: 14,
+                    fontWeight: FontWeight.w500,
                   ),
                 ),
               ],
             ),
-          );
+          ),
+        ],
+      ),
+    );
   }
 
   @override
@@ -225,7 +212,7 @@ class _OverviewSectionState extends State<OverviewSection> {
             ],
           ),
 
-          // Nutrients Grid - now using dynamic values
+          // Nutrients Grid
           const SizedBox(height: 16),
           Row(
             children: [
diff --git a/lib/features/homepage/presentation/screens/pet_homepage_section.dart b/lib/features/homepage/presentation/screens/pet_homepage_section.dart
index 572dcff0..f17fa8cc 100644
--- a/lib/features/homepage/presentation/screens/pet_homepage_section.dart
+++ b/lib/features/homepage/presentation/screens/pet_homepage_section.dart
@@ -5,9 +5,6 @@ import 'package:flutter/material.dart';
 
 // Project imports:
 import 'package:pockeat/features/calorie_stats/domain/models/daily_calorie_stats.dart';
-import 'package:pockeat/features/homepage/presentation/loading_skeleton/heart_bar_skeleton.dart';
-import 'package:pockeat/features/homepage/presentation/loading_skeleton/pet_companion_skeleton.dart';
-import 'package:pockeat/features/homepage/presentation/loading_skeleton/streak_counter_skeleton.dart';
 import 'package:pockeat/features/homepage/presentation/widgets/heart_bar_widget.dart';
 import 'package:pockeat/features/homepage/presentation/widgets/pet_companion_widget.dart';
 import 'package:pockeat/features/homepage/presentation/widgets/streak_counter_widget.dart';
@@ -18,7 +15,6 @@ class PetHomepageSection extends StatelessWidget {
   final PetInformation? petInfo;
   final DailyCalorieStats? stats;
   final int? streakDays;
-  final bool isLoading;
   final int? targetCalories;
 
   const PetHomepageSection({
@@ -27,7 +23,6 @@ class PetHomepageSection extends StatelessWidget {
     this.petInfo,
     this.stats,
     this.streakDays,
-    this.isLoading = false,
     this.targetCalories,
   });
 
@@ -48,32 +43,26 @@ class PetHomepageSection extends StatelessWidget {
       child: Column(
         children: [
           // Heart Bar
-          isLoading
-              ? const HeartBarSkeleton()
-              : HeartBarWidget(
-                  heart: petInfo?.heart ?? 0,
-                  isCalorieOverTarget: petInfo?.isCalorieOverTarget ?? false,
-                ),
+          HeartBarWidget(
+            heart: petInfo?.heart ?? 0,
+            isCalorieOverTarget: petInfo?.isCalorieOverTarget ?? false,
+          ),
           const SizedBox(height: 16),
 
           // Pet Companion Widget
           Center(
-            child: isLoading
-                ? const PetCompanionSkeleton()
-                : PetCompanionWidget(
-                    petName: petName,
-                    petImagePath: petInfo?.mood == 'happy'
-                        ? 'assets/images/panda_happy.json'
-                        : 'assets/images/panda_sad.json',
-                    calorieProgress: calorieProgress,
-                  ),
+            child: PetCompanionWidget(
+              petName: petName,
+              petImagePath: petInfo?.mood == 'happy'
+                  ? 'assets/images/panda_happy.json'
+                  : 'assets/images/panda_sad.json',
+              calorieProgress: calorieProgress,
+            ),
           ),
           const SizedBox(height: 24),
 
           // Streak Counter
-          isLoading
-              ? const StreakCounterSkeleton()
-              : StreakCounterWidget(streakDays: streakDays ?? 0),
+          StreakCounterWidget(streakDays: streakDays ?? 0),
           const SizedBox(height: 16),
         ],
       ),
diff --git a/lib/features/pet_companion/domain/services/pet_service_impl.dart b/lib/features/pet_companion/domain/services/pet_service_impl.dart
index 3349b72c..3b2ebe43 100644
--- a/lib/features/pet_companion/domain/services/pet_service_impl.dart
+++ b/lib/features/pet_companion/domain/services/pet_service_impl.dart
@@ -134,12 +134,52 @@ class PetServiceImpl implements PetService {
 
   @override
   Future<PetInformation> getPetInformation(String userId) async {
-    final isCalorieOverTarget = await getIsPetCalorieOverTarget(userId);
-    final heart = await getPetHeart(userId);
-    var mood = await getPetMood(userId);
+    final caloricRequirementDoc =
+        await firestore.collection('caloric_requirements').doc(userId).get();
+
+    final stats =
+        await calorieStatsService.calculateStatsForDate(userId, DateTime.now());
+
+    final today =
+        DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
+    final isLogToday =
+        await foodLogHistoryService.getFoodLogsByDate(userId, today);
+
+    final data = caloricRequirementDoc.data();
+
+    final tdee = data?['tdee'] as num;
+
+    final caloriesConsumed = stats.caloriesConsumed;
+
+    final percentage = caloriesConsumed / tdee;
+
+    final isCalorieOverTarget = percentage > 1;
+
+    var heart = 0;
+    var mood = '';
+
+    if (percentage > 0.75) {
+      heart = 4;
+    } else if (percentage > 0.5) {
+      heart = 3;
+    } else if (percentage > 0.25) {
+      heart = 2;
+    } else if (percentage > 0) {
+      heart = 1;
+    } else {
+      heart = 0;
+    }
+
+    if (isLogToday.isNotEmpty) {
+      mood = 'happy';
+    } else {
+      mood = 'sad';
+    }
+
     if (isCalorieOverTarget) {
       mood = 'sad';
     }
+
     return PetInformation(
       isCalorieOverTarget: isCalorieOverTarget,
       heart: heart,
diff --git a/pubspec.lock b/pubspec.lock
index d442b04c..dfa174b1 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1432,14 +1432,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.1"
-  shimmer:
-    dependency: "direct main"
-    description:
-      name: shimmer
-      sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
-      url: "https://pub.dev"
-    source: hosted
-    version: "3.0.0"
   simple_gesture_detector:
     dependency: transitive
     description:
@@ -1448,6 +1440,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.2.1"
+  skeletonizer:
+    dependency: "direct main"
+    description:
+      name: skeletonizer
+      sha256: "0dcacc51c144af4edaf37672072156f49e47036becbc394d7c51850c5c1e884b"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.3"
   sky_engine:
     dependency: transitive
     description: flutter
diff --git a/pubspec.yaml b/pubspec.yaml
index eaf42edf..a6aae508 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -76,7 +76,7 @@ dependencies:
   firebase_analytics: ^10.10.7
   flutter_launcher_icons: ^0.14.3
   supabase_flutter: ^2.8.4
-  shimmer: ^3.0.0
+  skeletonizer: ^1.1.0
   path: any
   logger: any
   confetti: ^0.8.0


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 79a37b5a50ba0e2edf44280cbe011639d18fc06d and 32fc94e25f93702cd3958e4ae9b9359f200171c7.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `pubspec.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (14)</summary>

* `lib/features/calorie_stats/services/calorie_stats_service.dart` (0 hunks)
* `lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart` (1 hunks)
* `lib/features/homepage/presentation/loading_skeleton/calories_today_skeleton.dart` (0 hunks)
* `lib/features/homepage/presentation/loading_skeleton/health_connect_skeleton.dart` (0 hunks)
* `lib/features/homepage/presentation/loading_skeleton/heart_bar_skeleton.dart` (0 hunks)
* `lib/features/homepage/presentation/loading_skeleton/nutrient_card_skeleton.dart` (0 hunks)
* `lib/features/homepage/presentation/loading_skeleton/pet_companion_skeleton.dart` (0 hunks)
* `lib/features/homepage/presentation/loading_skeleton/skeleton_loading.dart` (0 hunks)
* `lib/features/homepage/presentation/loading_skeleton/streak_counter_skeleton.dart` (0 hunks)
* `lib/features/homepage/presentation/screens/homepage.dart` (5 hunks)
* `lib/features/homepage/presentation/screens/overview_section.dart` (3 hunks)
* `lib/features/homepage/presentation/screens/pet_homepage_section.dart` (1 hunks)
* `lib/features/pet_companion/domain/services/pet_service_impl.dart` (1 hunks)
* `pubspec.yaml` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (8)</summary>

* lib/features/homepage/presentation/loading_skeleton/calories_today_skeleton.dart
* lib/features/calorie_stats/services/calorie_stats_service.dart
* lib/features/homepage/presentation/loading_skeleton/heart_bar_skeleton.dart
* lib/features/homepage/presentation/loading_skeleton/health_connect_skeleton.dart
* lib/features/homepage/presentation/loading_skeleton/nutrient_card_skeleton.dart
* lib/features/homepage/presentation/loading_skeleton/pet_companion_skeleton.dart
* lib/features/homepage/presentation/loading_skeleton/streak_counter_skeleton.dart
* lib/features/homepage/presentation/loading_skeleton/skeleton_loading.dart

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.dart`: Selain review default dan issue pada kode, Review kode juga perlu berdasarkan prinsip lanjutan dengan fokus pada pengembangan berkelanjutan dan maintenance (OOP Princi...</summary>

> `**/*.dart`: Selain review default dan issue pada kode, Review kode juga perlu berdasarkan prinsip lanjutan dengan fokus pada pengembangan berkelanjutan dan maintenance (OOP Principle, Good Data Structure, SOLID Principle, Design Pattern):
> 
> 1. OOP Principles:
>    - Encapsulation: Pastikan state internal objek disembunyikan dan diakses melalui getter/setter yang tepat
>    - Inheritance: Verifikasi penggunaan extends/implements yang tepat, hindari deep inheritance
>    - Polymorphism: Cek penggunaan interface dan abstract class yang konsisten
>    - Abstraction: Identifikasi apakah detail implementasi tersembunyi dengan baik
> 
> 2. Good Data Structure:
>    - Pastikan pemilihan struktur data efisien (List, Map, Set)
>    - Cek immutability pada data yang tidak perlu berubah (final, const)
>    - Verifikasi penggunaan strong typing vs dynamic
>    - Validasi null safety implementation
> 
> 3. SOLID Principles:
>    - Single Responsibility: Setiap class memiliki satu alasan untuk berubah
>    - Open/Closed: Class terbuka untuk ekstensi tapi tertutup untuk modifikasi
>    - Liskov Substitution: Subclass dapat menggantikan parent tanpa breaking changes
>    - Interface Segregation: Client tidak dipaksa bergantung pada interface yang tidak digunakan
>    - Dependency Inversion: High-level modules tidak bergantung pada low-level modules
> 
> 4. Design Patterns:
>    - Creational Patterns:
>      * Factory/Abstract Factory: Untuk pembuatan objek tanpa menentukan class konkret
>      * Builder: Untuk konstruksi objek kompleks step-by-step
>      * Singleton: Untuk memastikan class memiliki hanya satu instance
>      * Dependency Injection: Untuk inversion of control
>    
>    - Structural Patterns:
>      * Adapter: Untuk mengkonversi interface class agar kompatibel dengan class lain
>      * Decorator: Untuk menambah fungsionalitas tanpa inheritance
>      * Composite: Untuk komposisi hierarki tree-like
>      * Proxy: Untuk control akses ke objek
>      * Bridge: Untuk memisahkan abstraksi dari implementasi
>      * Facade: Untuk menyederhanakan interface kompleks
>    
>    - Behavioral Patterns:
>      * Observer/Stream: Untuk reactive programming
>      * Strategy: Untuk menukar algoritma secara runtime
>      * Command: Untuk encapsulation request sebagai objek
>      * State: Untuk mengubah behavior saat state berubah
>      * Mediator: Untuk komunikasi terpusat antar objek
>      * Chain of Responsibility: Untuk passing request di chain handler
>    
>    - Architectural Patterns:
>      * Repository: Untuk abstraksi data access
>      * Service: Untuk encapsulation business logic
>      * BLoC/Provider/Redux: Untuk state management
>      * MVC/MVVM/MVP: Untuk separation of concerns
> 
> Berikan contoh konkret ketika menemukan pelanggaran dan jelaskan bagaimana ini dapat mempengaruhi maintenance jangka panjang.

- `lib/features/homepage/presentation/screens/pet_homepage_section.dart`
- `lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart`
- `lib/features/homepage/presentation/screens/overview_section.dart`
- `lib/features/pet_companion/domain/services/pet_service_impl.dart`
- `lib/features/homepage/presentation/screens/homepage.dart`

</details>

</details><details>
<summary>🪛 GitHub Check: codecov/patch</summary>

<details>
<summary>lib/features/pet_companion/domain/services/pet_service_impl.dart</summary>

[warning] 165-165: lib/features/pet_companion/domain/services/pet_service_impl.dart#L165
Added line #L165 was not covered by tests

---

[warning] 167-167: lib/features/pet_companion/domain/services/pet_service_impl.dart#L167
Added line #L167 was not covered by tests

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Tests & Analysis
* GitHub Check: Tests & Analysis

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>pubspec.yaml (1)</summary>

`79-79`: **Good choice for using Skeletonizer package!**

Adding the `skeletonizer` package is a solid decision for streamlining your loading UI approach. This centralized solution is more maintainable than custom skeleton widgets for each component.

</details>
<details>
<summary>lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart (1)</summary>

`77-92`: **Excellent use of concurrency with Future.wait!**

Great job refactoring to fetch exercise logs concurrently. This pattern improves performance by running multiple repository queries in parallel rather than sequentially. The code is also more readable with clear type casting in lines 83-85.


This change exemplifies good application of the Single Responsibility Principle by keeping the method focused on coordinating data retrieval, while delegating the actual fetching to repository implementations.

</details>
<details>
<summary>lib/features/homepage/presentation/screens/pet_homepage_section.dart (4)</summary>

`46-49`: **Clean removal of conditional rendering**

Nice simplification of the HeartBarWidget implementation by removing the conditional loading state. This aligns with the new Skeletonizer approach and makes the code more straightforward.

---

`54-61`: **Improved widget structure**

Good job removing the conditional rendering for PetCompanionWidget. The code is now cleaner and follows the centralized loading approach with Skeletonizer.

---

`65-65`: **Clean simplification**

Nice concise implementation for StreakCounterWidget. Removing the conditional rendering makes the code more maintainable.

---

`13-27`: **Good constructor parameter cleanup**

You've correctly removed the isLoading parameter from the constructor, which aligns with your new approach using Skeletonizer. This makes the component API cleaner and more focused.

</details>
<details>
<summary>lib/features/homepage/presentation/screens/overview_section.dart (4)</summary>

`83-92`: **Clean implementation of CaloriesTodayWidget**

Excellent job simplifying the CaloriesTodayWidget implementation by removing conditional rendering. The code is now cleaner and more maintainable.

---

`95-95`: **Simplified HealthConnectWidget rendering**

Good refactoring to direct widget rendering without conditional fallbacks. This change properly aligns with the centralized Skeletonizer pattern.

---

`112-174`: **Well-structured nutrient card implementation**

Clean refactoring of the nutrient card implementation. Removing conditional rendering makes the code more straightforward while maintaining the same visual output with proper handling of exceeded values.


The container implementation follows good UI patterns with proper padding, shadow, and spacing. The isExceeded flag properly controls visual feedback for the user.

---

`10-28`: **Good removal of unnecessary properties**

The removal of isLoading flags from the widget properties improves encapsulation by eliminating state that's no longer needed with the Skeletonizer approach.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T09:03:15Z
user: login: isaui
body: Saya sudah merasakn perbedaannya di Homepage tetapi menurut saya masih  belum clean code. Kamu kayak langsung bypass layer gitu loh. Kan ga bagus tuh. Mungkin solusi sekarang tampak bermanfaat, tapi seiring berkembangnya project akan menyulitkan tim. Untuk kedepannya tolong refactor dependency dependency yang bermasalah bukan di bypass kocak
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T09:11:23Z

Review comments:
diff_hunk: @@ -134,12 +134,52 @@
 
   @override
   Future<PetInformation> getPetInformation(String userId) async {
-    final isCalorieOverTarget = await getIsPetCalorieOverTarget(userId);
-    final heart = await getPetHeart(userId);
-    var mood = await getPetMood(userId);
+    final caloricRequirementDoc =
+        await firestore.collection('caloric_requirements').doc(userId).get();
+
+    final stats =
+        await calorieStatsService.calculateStatsForDate(userId, DateTime.now());
+
+    final today =
+        DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
+    final isLogToday =
+        await foodLogHistoryService.getFoodLogsByDate(userId, today);
+
path: lib/features/pet_companion/domain/services/pet_service_impl.dart
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Fire the three queries in parallel for a 3× speed-up.**

Fetching the caloric requirement, stats, and food logs sequentially incurs ~3 network RTTs.  
Wrap them in a `Future.wait` to unblock each other:

```dart
final results = await Future.wait([
  firestore.collection('caloric_requirements').doc(userId).get(),
  calorieStatsService.calculateStatsForDate(userId, DateTime.now()),
  foodLogHistoryService.getFoodLogsByDate(userId, today),
]);

final caloricRequirementDoc = results[0] as DocumentSnapshot;
final stats                 = results[1] as DailyCalorieStats;
final isLogToday            = results[2] as List<FoodLog>;
```

This micro-optimisation translates directly to a snappier homepage.  



Also applies to: 152-154

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In lib/features/pet_companion/domain/services/pet_service_impl.dart around lines
137 to 147, the three asynchronous calls to fetch caloricRequirementDoc, stats,
and isLogToday are executed sequentially, causing unnecessary delay. Refactor
the code to run these three futures in parallel using Future.wait, then extract
the results by index and cast them to their respective types. Apply the same
pattern to lines 152-154 to optimize those queries as well.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-20T09:03:12Z
author_association: NONE
diff_hunk: @@ -134,12 +134,52 @@
 
   @override
   Future<PetInformation> getPetInformation(String userId) async {
-    final isCalorieOverTarget = await getIsPetCalorieOverTarget(userId);
-    final heart = await getPetHeart(userId);
-    var mood = await getPetMood(userId);
+    final caloricRequirementDoc =
+        await firestore.collection('caloric_requirements').doc(userId).get();
+
+    final stats =
+        await calorieStatsService.calculateStatsForDate(userId, DateTime.now());
+
+    final today =
+        DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
+    final isLogToday =
+        await foodLogHistoryService.getFoodLogsByDate(userId, today);
+
+    final data = caloricRequirementDoc.data();
+
+    final tdee = data?['tdee'] as num;
+
path: lib/features/pet_companion/domain/services/pet_service_impl.dart
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Add robust null-safety & zero-division guards before using `tdee`.**

`caloricRequirementDoc.data()` can be `null`, or the `tdee` field can be missing / zero.  
Unlike the earlier helper methods, this block skips the defensive checks and will crash with a `TypeError` or a `NaN` percentage.

```diff
-  final data = caloricRequirementDoc.data();
-  final tdee = data?['tdee'] as num;
+  final data = caloricRequirementDoc.data();
+  if (data == null || !data.containsKey('tdee')) {
+    debugPrint('TDEE not found for user $userId – falling back to 0');
+    return PetInformation(isCalorieOverTarget: false, heart: 0, mood: 'sad');
+  }
+
+  final tdee = data['tdee'] as num;
+  if (tdee <= 0) {
+    debugPrint('Invalid TDEE ($tdee) for user $userId – avoiding division by zero');
+    return PetInformation(isCalorieOverTarget: false, heart: 0, mood: 'sad');
+  }
```

Fail-fast guards keep the app alive and the companion predictable.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
    final caloricRequirementDoc =
        await firestore.collection('caloric_requirements').doc(userId).get();

    final stats =
        await calorieStatsService.calculateStatsForDate(userId, DateTime.now());

    final today =
        DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
    final isLogToday =
        await foodLogHistoryService.getFoodLogsByDate(userId, today);

    final data = caloricRequirementDoc.data();
    if (data == null || !data.containsKey('tdee')) {
      debugPrint('TDEE not found for user $userId – falling back to 0');
      return PetInformation(isCalorieOverTarget: false, heart: 0, mood: 'sad');
    }

    final tdee = data['tdee'] as num;
    if (tdee <= 0) {
      debugPrint('Invalid TDEE ($tdee) for user $userId – avoiding division by zero');
      return PetInformation(isCalorieOverTarget: false, heart: 0, mood: 'sad');
    }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In lib/features/pet_companion/domain/services/pet_service_impl.dart around lines
137 to 151, add null-safety checks to ensure caloricRequirementDoc.data() is not
null before accessing 'tdee', and verify that 'tdee' is present and non-zero
before using it. If 'tdee' is missing or zero, handle this case gracefully by
providing a default value or returning early to prevent TypeError or NaN
calculations, thus making the code more robust and preventing crashes.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-20T09:03:13Z
author_association: NONE
diff_hunk: @@ -134,12 +134,52 @@
 
   @override
   Future<PetInformation> getPetInformation(String userId) async {
-    final isCalorieOverTarget = await getIsPetCalorieOverTarget(userId);
-    final heart = await getPetHeart(userId);
-    var mood = await getPetMood(userId);
+    final caloricRequirementDoc =
+        await firestore.collection('caloric_requirements').doc(userId).get();
+
+    final stats =
+        await calorieStatsService.calculateStatsForDate(userId, DateTime.now());
+
+    final today =
+        DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
+    final isLogToday =
+        await foodLogHistoryService.getFoodLogsByDate(userId, today);
+
+    final data = caloricRequirementDoc.data();
+
+    final tdee = data?['tdee'] as num;
+
+    final caloriesConsumed = stats.caloriesConsumed;
+
+    final percentage = caloriesConsumed / tdee;
+
+    final isCalorieOverTarget = percentage > 1;
+
+    var heart = 0;
+    var mood = '';
+
+    if (percentage > 0.75) {
+      heart = 4;
+    } else if (percentage > 0.5) {
+      heart = 3;
+    } else if (percentage > 0.25) {
+      heart = 2;
+    } else if (percentage > 0) {
+      heart = 1;
+    } else {
+      heart = 0;
+    }
+
+    if (isLogToday.isNotEmpty) {
+      mood = 'happy';
+    } else {
+      mood = 'sad';
+    }
path: lib/features/pet_companion/domain/services/pet_service_impl.dart
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Stop duplicating the heart & mood calculation – delegate instead.**

The heart-tier ladder and basic mood logic are now copy-pasted from `getPetHeart`/`getPetMood`.  
Duplicated decision trees violate DRY and increase the chance of behavioural drift (e.g., if one ladder is tweaked but the other isn’t).

Refactor so `getPetInformation` simply awaits the existing helpers:

```diff
-    var heart = 0;
-    var mood = '';
-
-    if (percentage > 0.75) {
-      heart = 4;
-    } …
-
-    if (isLogToday.isNotEmpty) {
-      mood = 'happy';
-    } else {
-      mood = 'sad';
-    }
+    final heart = await getPetHeart(userId);
+    var mood   = await getPetMood(userId);
```

You keep one source of truth, shrink the method, and follow the Single Responsibility principle.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
    final heart = await getPetHeart(userId);
    var mood  = await getPetMood(userId);
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 165-165: lib/features/pet_companion/domain/services/pet_service_impl.dart#L165
Added line #L165 was not covered by tests

---

[warning] 167-167: lib/features/pet_companion/domain/services/pet_service_impl.dart#L167
Added line #L167 was not covered by tests

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In lib/features/pet_companion/domain/services/pet_service_impl.dart between
lines 158 and 177, the heart and mood calculation logic is duplicated from the
existing getPetHeart and getPetMood methods. To fix this, refactor
getPetInformation to call and await getPetHeart and getPetMood instead of
duplicating their logic. This will centralize the decision logic, reduce code
duplication, and adhere to the Single Responsibility principle.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-20T09:03:13Z
author_association: NONE
diff_hunk: @@ -107,6 +158,29 @@ class _HomePageState extends State<HomePage>
       return {'protein': 0, 'carbs': 0, 'fat': 0};
     }
   }
+
+  Future<List<dynamic>> _getHomePageFutures() async {
+    debugPrint('🕒 Homepage data fetch started at ${DateTime.now()}');
+    final stopwatch = Stopwatch()..start();
+    
+    final result = await Future.wait([
+      _petInformation,
+      _statsFuture,
+      _dayStreak,
+      _targetCalories,
+      _isCalorieCompensationEnabledFuture,
+      _isRolloverCaloriesEnabledFuture,
+      _rolloverCaloriesFuture,
+      _currentMacrosFuture,
+      _caloricRequirementModelFuture,
+    ]);
+    
+    stopwatch.stop();
+    debugPrint('⏱️ Homepage data fetch completed in ${stopwatch.elapsed.inMilliseconds}ms');
+    
+    return result;
path: lib/features/homepage/presentation/screens/homepage.dart
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider returning a typed DTO instead of `List<dynamic>` for safer access.**

Index-based extraction (`snapshot.data?[3] as int?`) is brittle:

• Easy to mis-order arguments when the list grows  
• No compiler help – a silent off-by-one shows up only at runtime  
• Hurts readability & long-term maintainability (violates SRP / OOP abstraction)

Recommended pattern:

```dart
class _HomePageData {
  final PetInformation petInfo;
  final DailyCalorieStats stats;
  final int dayStreak;
  // … other fields
}

Future<_HomePageData> _getHomePageData() async {
  final results = await Future.wait([ … ]);
  return _HomePageData(
    petInfo:              results[0] as PetInformation,
    stats:                results[1] as DailyCalorieStats,
    dayStreak:            results[2] as int,
    // …
  );
}
```

The builder then becomes type-safe (`snapshot.data?.petInfo`) and self-documenting.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In lib/features/homepage/presentation/screens/homepage.dart between lines 162
and 181, the method _getHomePageFutures returns a List<dynamic> which leads to
brittle, index-based access and poor type safety. Refactor this method to return
a strongly typed DTO class (e.g., _HomePageData) that encapsulates all the
individual future results as named fields. Create the DTO class with appropriate
typed fields matching each future's result, then map the results list to this
DTO before returning. This will improve type safety, readability, and
maintainability by enabling named access to the data instead of relying on list
indices.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-20T09:03:14Z
author_association: NONE
diff_hunk: @@ -61,24 +62,74 @@
     _loadData();
   }
 
+  @override
+  void dispose() {
+    _scrollController.dispose();
+    super.dispose();
+  }
+
   void _loadData() {
-    _petInformation = _petService.getPetInformation(userId);
-    _dayStreak = _foodLogHistoryService.getFoodStreakDays(userId);
-    _statsFuture =
-        _calorieStatsService.calculateStatsForDate(userId, DateTime.now());
-    _targetCalories = caloricRequirementRepository
+    _petInformation = _measureFutureTime(
+      'Pet Information', 
+      _petService.getPetInformation(userId)
+    );
+
+    _dayStreak = _measureFutureTime(
+      'Day Streak', 
+      _foodLogHistoryService.getFoodStreakDays(userId)
+    );
+
+    _statsFuture = _measureFutureTime(
+      'Stats',
+      _calorieStatsService.calculateStatsForDate(userId, DateTime.now())
+    );
+
+    _targetCalories = _measureFutureTime(
+      'Target Calories',
+      caloricRequirementRepository
         .getCaloricRequirement(userId)
-        .then((value) => value?.tdee.toInt() ?? 0);
-    _rolloverCaloriesFuture = preferencesService.getRolloverCalories();
-    _isCalorieCompensationEnabledFuture =
-        preferencesService.isExerciseCalorieCompensationEnabled();
-    _isRolloverCaloriesEnabledFuture =
-        preferencesService.isRolloverCaloriesEnabled();
+        .then((value) => value?.tdee.toInt() ?? 0)
+    );
+
+    _rolloverCaloriesFuture = _measureFutureTime(
+      'Rollover Calories',
+      preferencesService.getRolloverCalories()
+    );
+
+    _isCalorieCompensationEnabledFuture = _measureFutureTime(
+      'Calorie Compensation Setting',
+      preferencesService.isExerciseCalorieCompensationEnabled()
+    );
+
+    _isRolloverCaloriesEnabledFuture = _measureFutureTime(
+      'Rollover Calories Setting',
+      preferencesService.isRolloverCaloriesEnabled()
+    );
 
     // Initialize the new futures
-    _caloricRequirementModelFuture =
-        caloricRequirementRepository.getCaloricRequirement(userId);
-    _currentMacrosFuture = _calculateCurrentMacros();
+    _caloricRequirementModelFuture = _measureFutureTime(
+      'Caloric Requirement Model',
+      caloricRequirementRepository.getCaloricRequirement(userId)
+    );
+
+    _currentMacrosFuture = _measureFutureTime(
+      'Current Macros',
+      _calculateCurrentMacros()
+    );
+  }
+
+  // Helper method to measure the time taken by a Future
+  Future<T> _measureFutureTime<T>(String name, Future<T> future) {
+    final stopwatch = Stopwatch()..start();
+    return future.then((value) {
+      stopwatch.stop();
+      debugPrint('⏱️ $name fetch completed in ${stopwatch.elapsed.inMilliseconds}ms');
+      return value;
+    }).catchError((error) {
+      stopwatch.stop();
+      debugPrint('❌ $name fetch failed after ${stopwatch.elapsed.inMilliseconds}ms: $error');
+      throw error;
+    });
   }
path: lib/features/homepage/presentation/screens/homepage.dart
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**`_measureFutureTime` rethrows without preserving stack trace.**

Using `throw error` discards the original stack trace.  
Switch to `Error.throwWithStackTrace` (Dart 3) or `Future.error(error, stackTrace)` to keep diagnostics intact.

```diff
-    }).catchError((error) {
+    }).catchError((error, stackTrace) {
       stopwatch.stop();
       debugPrint('❌ $name fetch failed after ${stopwatch.elapsed.inMilliseconds}ms: $error');
-      throw error;
+      Error.throwWithStackTrace(error, stackTrace);
     });
```

This tiny tweak makes bug-hunting less painful.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In lib/features/homepage/presentation/screens/homepage.dart around lines 121 to
133, the _measureFutureTime method rethrows errors using 'throw error', which
discards the original stack trace. To preserve the stack trace for better
debugging, modify the catchError block to rethrow the error with its stack trace
using either Error.throwWithStackTrace(error, stackTrace) if using Dart 3, or
return Future.error(error, stackTrace) if not. Capture the stack trace in the
catchError callback and use it when rethrowing the error.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-20T09:03:14Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-20T08:57:26Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update removes all custom skeleton loading widgets and their usages from the homepage, replacing them with a unified `Skeletonizer` package for loading placeholders. Data fetching logic is refactored for improved concurrency and performance measurement, and resource cleanup is enhanced. Several widget constructors and parameters related to loading states are simplified or removed.

## Changes

| File(s)                                                                                          | Change Summary                                                                                                                                                                                                                                                                                                                                                                                      |
|--------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `lib/features/homepage/presentation/loading_skeleton/*.dart`                                     | Deleted all custom skeleton loading widgets: `CaloriesTodaySkeleton`, `HealthConnectSkeleton`, `HeartBarSkeleton`, `NutrientCardSkeleton`, `PetCompanionSkeleton`, `SkeletonLoading`, and `StreakCounterSkeleton`.                                                                                                                                            |
| `lib/features/homepage/presentation/screens/homepage.dart`                                       | Introduced performance measurement for homepage data loading, consolidated all homepage futures into `_getHomePageFutures`, replaced manual loading flags with `Skeletonizer` for UI placeholders, and added proper scroll controller disposal.                                                                       |
| `lib/features/homepage/presentation/screens/overview_section.dart`                               | Removed `isLoading` property and all skeleton widget usages; now always renders actual widgets directly. Refactored nutrient card widget structure and removed related imports.                                                                                                                                                                               |
| `lib/features/homepage/presentation/screens/pet_homepage_section.dart`                           | Removed `isLoading` property, constructor parameter, and all skeleton widget usages; now always renders actual widgets directly. Removed related imports and simplified control flow.                                                                                                                                                                         |
| `lib/features/calorie_stats/services/calorie_stats_service.dart`                                 | Removed a debug print loop for exercise logs; main logic and outputs remain unchanged.                                                                                                                                                                                                                                                                                                              |
| `lib/features/exercise_log_history/services/exercise_log_history_service_impl.dart`              | Refactored `getExerciseLogsByDate` to fetch exercise logs concurrently using `Future.wait`, removed redundant variables, and maintained error handling. No change to method signature.                                                                                                                                                                        |
| `lib/features/pet_companion/domain/services/pet_service_impl.dart`                               | Inlined logic for calorie and mood evaluation in `getPetInformation`, removing calls to other service methods and consolidating asynchronous operations. No change to method signature.                                                                                                                                |
| `pubspec.yaml`                                                                                   | Replaced `shimmer` dependency with `skeletonizer` for loading animations. No other dependency changes.                                                                                                                                                                                                                                                       |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant HomePage
    participant DataFetchers
    participant Skeletonizer

    User->>HomePage: Open homepage
    HomePage->>DataFetchers: Start all data fetch futures (concurrently)
    HomePage->>Skeletonizer: Show skeleton UI while loading
    DataFetchers-->>HomePage: Return all data (when ready)
    HomePage->>Skeletonizer: Disable skeleton UI
    HomePage->>User: Show actual homepage content
```

## Poem

> Out with the shimmer, in with the new—  
> Skeletonizer shines, loading dreams come true!  
> Gone are the widgets, each custom-made,  
> Now one sleek shimmer, perfectly displayed.  
> Futures race, logs now timed,  
> Homepage loads, performance primed!  
> 🦴✨

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Pemuda-Pembuka-Langkah/pockeat-mobile&utm_content=376):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-20T08:58:57Z
body: ## [Codecov](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah) Report
Attention: Patch coverage is `92.59259%` with `2 lines` in your changes missing coverage. Please review.

:white_check_mark: All tests successful. No failed tests found.

| [Files with missing lines](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah) | Patch % | Lines |
|---|---|---|
| [...et\_companion/domain/services/pet\_service\_impl.dart](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?src=pr&el=tree&filepath=lib%2Ffeatures%2Fpet_companion%2Fdomain%2Fservices%2Fpet_service_impl.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah#diff-bGliL2ZlYXR1cmVzL3BldF9jb21wYW5pb24vZG9tYWluL3NlcnZpY2VzL3BldF9zZXJ2aWNlX2ltcGwuZGFydA==) | 85.71% | [2 Missing :warning: ](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah) |

[![Impacted file tree graph](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376/graphs/tree.svg?width=650&height=150&src=pr&token=HJS2C9MW9B&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah)](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah)

```diff
@@             Coverage Diff             @@
##           staging     #376      +/-   ##
===========================================
- Coverage    93.14%   93.09%   -0.06%     
===========================================
  Files          220      220              
  Lines        13445    13460      +15     
===========================================
+ Hits         12523    12530       +7     
- Misses         922      930       +8     
```

| [Files with missing lines](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah) | Coverage Δ | |
|---|---|---|
| [...ry/services/exercise\_log\_history\_service\_impl.dart](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?src=pr&el=tree&filepath=lib%2Ffeatures%2Fexercise_log_history%2Fservices%2Fexercise_log_history_service_impl.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah#diff-bGliL2ZlYXR1cmVzL2V4ZXJjaXNlX2xvZ19oaXN0b3J5L3NlcnZpY2VzL2V4ZXJjaXNlX2xvZ19oaXN0b3J5X3NlcnZpY2VfaW1wbC5kYXJ0) | `98.75% <100.00%> (+0.06%)` | :arrow_up: |
| [...et\_companion/domain/services/pet\_service\_impl.dart](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?src=pr&el=tree&filepath=lib%2Ffeatures%2Fpet_companion%2Fdomain%2Fservices%2Fpet_service_impl.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah#diff-bGliL2ZlYXR1cmVzL3BldF9jb21wYW5pb24vZG9tYWluL3NlcnZpY2VzL3BldF9zZXJ2aWNlX2ltcGwuZGFydA==) | `93.22% <85.71%> (-2.62%)` | :arrow_down: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah)

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah). Last update [b24a8f7...32fc94e](https://app.codecov.io/gh/Pemuda-Pembuka-Langkah/pockeat-mobile/pull/376?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pemuda-Pembuka-Langkah).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE