Use previous HTTP profile timestamps instead of DateTime.now() when requesting HTTP profiles (#8860)

The time between the target process creating the HTTP profile and DevTools invoking DateTime.now() to set the last HTTP data refresh time could result in some HTTP events being missed. This change updates the network service to use the timestamp sent with the last HTTP profile when requesting the next HTTP profile so network latency won't introduce time gaps in the profile.

This change also updates the logic to keep track of last HTTP profile request times by isolate as each profile has its own timestamp that needs to be used when requesting the next profile.

Fixes https://github.com/flutter/devtools/issues/8380
Fixes https://github.com/flutter/devtools/issues/8305
diff --git a/packages/devtools_app/lib/src/screens/network/network_controller.dart b/packages/devtools_app/lib/src/screens/network/network_controller.dart
index bc61b25..5c790d0 100644
--- a/packages/devtools_app/lib/src/screens/network/network_controller.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_controller.dart
@@ -153,9 +153,6 @@
   /// timeline events.
   late int _timelineMicrosOffset;
 
-  /// The last time at which HTTP information was refreshed.
-  DateTime lastHttpDataRefreshTime = DateTime.fromMicrosecondsSinceEpoch(0);
-
   /// The last timestamp at which Socket information was refreshed.
   ///
   /// This timestamp is on the monotonic clock used by the timeline.
diff --git a/packages/devtools_app/lib/src/screens/network/network_service.dart b/packages/devtools_app/lib/src/screens/network/network_service.dart
index ae21304..0b0bcbe 100644
--- a/packages/devtools_app/lib/src/screens/network/network_service.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_service.dart
@@ -13,6 +13,10 @@
 
   final NetworkController networkController;
 
+  /// Tracks the time (microseconds since epoch) that the HTTP profile was last
+  /// retrieved for a given isolate ID.
+  final lastHttpDataRefreshTimePerIsolate = <String, int>{};
+
   /// Updates the last Socket data refresh time to the current time.
   ///
   /// If [alreadyRecordingSocketData] is true, it's unclear when the last
@@ -40,11 +44,18 @@
   ///
   /// If [alreadyRecordingHttp] is true it's unclear when the last refresh time
   /// would have occurred, so the refresh time is not updated. Otherwise,
-  /// [NetworkController.lastHttpDataRefreshTime] is updated to the current
+  /// [lastHttpDataRefreshTimePerIsolate] is updated to the current
   /// time.
   void updateLastHttpDataRefreshTime({bool alreadyRecordingHttp = false}) {
     if (!alreadyRecordingHttp) {
-      networkController.lastHttpDataRefreshTime = DateTime.now();
+      for (final isolateId in lastHttpDataRefreshTimePerIsolate.keys.toList()) {
+        // It's safe to use `DateTime.now()` here since we don't need to worry
+        // about dropping data between the time the last profile was generated
+        // by the target application and the time `DateTime.now()` is called
+        // here.
+        lastHttpDataRefreshTimePerIsolate[isolateId] =
+            DateTime.now().microsecondsSinceEpoch;
+      }
     }
   }
 
@@ -59,7 +70,6 @@
     networkController.lastSocketDataRefreshMicros = timestamp;
     List<HttpProfileRequest>? httpRequests;
     httpRequests = await _refreshHttpProfile();
-    networkController.lastHttpDataRefreshTime = DateTime.now();
     networkController.processNetworkTraffic(
       sockets: sockets,
       httpRequests: httpRequests,
@@ -74,9 +84,22 @@
     await service.forEachIsolate((isolate) async {
       final request = await service.getHttpProfileWrapper(
         isolate.id!,
-        updatedSince: networkController.lastHttpDataRefreshTime,
+        updatedSince: DateTime.fromMicrosecondsSinceEpoch(
+          lastHttpDataRefreshTimePerIsolate.putIfAbsent(
+            isolate.id!,
+            // If a new isolate has spawned, request all HTTP requests from the
+            // start of time when retrieving the first profile.
+            () => 0,
+          ),
+        ),
       );
       requests.addAll(request.requests);
+      // Update the last request time using the timestamp from the HTTP profile
+      // instead of DateTime.now() to avoid missing events due to the delay
+      // between the last profile creation in the target process and the call
+      // to DateTime.now() here.
+      lastHttpDataRefreshTimePerIsolate[isolate.id!] =
+          request.timestamp.microsecondsSinceEpoch;
     });
     return requests;
   }
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index 5347d86..e5b2231 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -45,6 +45,7 @@
 
 * Changed the context menu style to be consistent with other screens
   [#8859](https://github.com/flutter/devtools/pull/8859).
+* Fixed issue where the HTTP requests would sometimes not be displayed properly, particularly when DevTools is communicating with an application over a slow network connection. - [#8860](https://github.com/flutter/devtools/pull/8860)
 
 ## Logging updates