Fix persistent and disappearing error badges for Inspector (#9805)

* Fix persistent and disappearing error badges for Inspector

* Add release note for error badge fix

* Increase timeout for asyncEval tests on Windows CI

* Revert "Increase timeout for asyncEval tests on Windows CI"

This reverts commit 9b461c3e96af82f2cd6aeba152d13d355dc50511.

* Update packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* update error badge unread count incrementally

* remove bad merge

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
index e1db0b5..1f1ed0c 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
@@ -157,9 +157,8 @@
     // TODO(kenz): When this method is called outside  createState(), this post
     // frame callback can be removed.
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      serviceConnection.errorBadgeManager.clearErrorCount(InspectorScreen.id);
+      serviceConnection.errorBadgeManager.clearErrors(InspectorScreen.id);
     });
-    filterErrors();
   }
 
   void _handleConnectionStop() {
@@ -381,8 +380,6 @@
       return;
     }
 
-    filterErrors();
-
     return _waitForPendingUpdateDone();
   }
 
@@ -404,13 +401,6 @@
     await onForceRefresh();
   }
 
-  void filterErrors() {
-    serviceConnection.errorBadgeManager.filterErrors(
-      InspectorScreen.id,
-      (id) => hasDiagnosticsValue(InspectorInstanceRef(id)),
-    );
-  }
-
   void setActivate(bool enabled) {
     if (!enabled) {
       onIsolateStopped();
@@ -977,17 +967,12 @@
     _selectedErrorIndex.value = errorIndex;
 
     if (errorIndex != null) {
-      // Mark the error as "seen" as this will render slightly differently
-      // so the user can track which errored nodes they've viewed.
+      // Marking an error as read will automatically update the badge count to
+      // reflect the remaining unread errors.
       serviceConnection.errorBadgeManager.markErrorAsRead(
         InspectorScreen.id,
         errors[inspectorRef!]!,
       );
-      // Also clear the error badge since new errors may have arrived while
-      // the inspector was visible (normally they're cleared when visiting
-      // the screen) and visiting an errored node seems an appropriate
-      // acknowledgement of the errors.
-      serviceConnection.errorBadgeManager.clearErrorCount(InspectorScreen.id);
     }
   }
 
diff --git a/packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart b/packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart
index 16950eb..10a12fb 100644
--- a/packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart
+++ b/packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart
@@ -66,7 +66,6 @@
 
       final inspectableError = _extractInspectableError(e);
       if (inspectableError != null) {
-        incrementBadgeCount(InspectorScreen.id);
         appendError(InspectorScreen.id, inspectableError);
       }
     }
@@ -113,6 +112,10 @@
   }
 
   void incrementBadgeCount(String screenId) {
+    if (_activeErrors.containsKey(screenId)) {
+      return;
+    }
+
     final notifier = _errorCountNotifier(screenId);
     if (notifier == null) return;
 
@@ -124,12 +127,40 @@
     final errors = _activeErrors[screenId];
     if (errors == null) return;
 
+    final previousError = errors.value[error.id];
+
     // Build a new map with the new error. Adding to the existing map
     // won't cause the ValueNotifier to fire (and it's not permitted to call
     // notifyListeners() directly).
     final newValue = LinkedHashMap<String, DevToolsError>.of(errors.value);
     newValue[error.id] = error;
     errors.value = newValue;
+
+    if (previousError == null) {
+      if (!error.read) {
+        _incrementUnreadCount(screenId);
+      }
+      return;
+    }
+
+    if (previousError.read && !error.read) {
+      _incrementUnreadCount(screenId);
+    } else if (!previousError.read && error.read) {
+      _decrementUnreadCount(screenId);
+    }
+  }
+
+  void _incrementUnreadCount(String screenId) {
+    final notifier = _errorCountNotifier(screenId);
+    if (notifier == null) return;
+    notifier.value = notifier.value + 1;
+  }
+
+  void _decrementUnreadCount(String screenId) {
+    final notifier = _errorCountNotifier(screenId);
+    if (notifier == null) return;
+    if (notifier.value == 0) return;
+    notifier.value = notifier.value - 1;
   }
 
   ValueListenable<int> errorCountNotifier(String screenId) {
@@ -150,25 +181,20 @@
   }
 
   void clearErrorCount(String screenId) {
+    if (_activeErrors.containsKey(screenId)) {
+      return;
+    }
     _activeErrorCounts[screenId]?.value = 0;
   }
 
   void clearErrors(String screenId) {
-    clearErrorCount(screenId);
-    _activeErrors[screenId]?.value = LinkedHashMap<String, DevToolsError>();
-  }
-
-  void filterErrors(String screenId, bool Function(String id) isValid) {
-    final errors = _activeErrors[screenId];
-    if (errors == null) return;
-
-    final oldCount = errors.value.length;
-    final newValue = Map.fromEntries(
-      errors.value.entries.where((e) => isValid(e.key)),
-    );
-    if (newValue.length != oldCount) {
-      errors.value = newValue as LinkedHashMap<String, DevToolsError>;
+    if (!_activeErrors.containsKey(screenId)) {
+      clearErrorCount(screenId);
+      return;
     }
+
+    _activeErrors[screenId]?.value = LinkedHashMap<String, DevToolsError>();
+    _activeErrorCounts[screenId]?.value = 0;
   }
 
   void markErrorAsRead(String screenId, DevToolsError error) {
@@ -188,6 +214,7 @@
         return MapEntry(e.key, e.value.asRead());
       }),
     );
+    _decrementUnreadCount(screenId);
   }
 }
 
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index 982483f..7d6a008 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -21,7 +21,7 @@
 
 ## Inspector updates
 
-TODO: Remove this section if there are not any updates.
+- Fixed an issue where the Inspector error badge count would improperly increase or disappear during navigation. [#9524](https://github.com/flutter/devtools/issues/9524)
 
 ## Performance updates
 
diff --git a/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart b/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart
index e5f17d1..06388d2 100644
--- a/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart
+++ b/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart
@@ -13,11 +13,7 @@
 import 'package:devtools_app/src/shared/managers/error_badge_manager.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-final supportedScreenIds = [
-  InspectorScreen.id,
-  PerformanceScreen.id,
-  NetworkScreen.id,
-];
+final screensWithCountOnly = [PerformanceScreen.id, NetworkScreen.id];
 
 final allScreenIds = [
   InspectorScreen.id,
@@ -55,7 +51,7 @@
       allScreenIds.forEach(errorBadgeManager.incrementBadgeCount);
 
       for (final id in allScreenIds) {
-        if (supportedScreenIds.contains(id)) {
+        if (screensWithCountOnly.contains(id)) {
           expect(errorBadgeManager.errorCountNotifier(id).value, equals(1));
         } else {
           expect(errorBadgeManager.errorCountNotifier(id).value, equals(0));
@@ -67,7 +63,7 @@
       allScreenIds.forEach(errorBadgeManager.incrementBadgeCount);
 
       for (final id in allScreenIds) {
-        if (supportedScreenIds.contains(id)) {
+        if (screensWithCountOnly.contains(id)) {
           expect(errorBadgeManager.errorCountNotifier(id).value, equals(1));
         } else {
           expect(errorBadgeManager.errorCountNotifier(id).value, equals(0));
@@ -108,7 +104,6 @@
         InspectorScreen.id,
         DevToolsError('An error', InspectorScreen.id),
       );
-      errorBadgeManager.incrementBadgeCount(InspectorScreen.id);
 
       expect(getActiveErrorCount(InspectorScreen.id), equals(1));
       expect(