Dispatch a Flutter.Navigation event each time navigation occurs. (#23126)
Dispatch a Flutter.Navigation event each time navigation occurs.
diff --git a/dev/devicelab/bin/tasks/service_extensions_test.dart b/dev/devicelab/bin/tasks/service_extensions_test.dart
index a76b37c..d946f40 100644
--- a/dev/devicelab/bin/tasks/service_extensions_test.dart
+++ b/dev/devicelab/bin/tasks/service_extensions_test.dart
@@ -59,6 +59,8 @@
final VMIsolateRef isolate = vm.isolates.first;
final Stream<VMExtensionEvent> frameEvents = isolate.onExtensionEvent.where(
(VMExtensionEvent e) => e.kind == 'Flutter.Frame');
+ final Stream<VMExtensionEvent> navigationEvents = isolate.onExtensionEvent.where(
+ (VMExtensionEvent e) => e.kind == 'Flutter.Navigation');
print('reassembling app...');
final Future<VMExtensionEvent> frameFuture = frameEvents.first;
@@ -77,6 +79,13 @@
expect(event.data['elapsed'] is int);
expect(event.data['elapsed'] >= 0);
+ final Future<VMExtensionEvent> navigationFuture = navigationEvents.first;
+ // This tap triggers a navigation event.
+ device.tap(100, 100);
+ final VMExtensionEvent navigationEvent = await navigationFuture;
+ // Validate that there are not any fields.
+ expect(navigationEvent.data.isEmpty);
+
run.stdin.write('q');
final int result = await run.exitCode;
if (result != 0)
diff --git a/dev/integration_tests/ui/lib/main.dart b/dev/integration_tests/ui/lib/main.dart
index 2b93a84..822d956 100644
--- a/dev/integration_tests/ui/lib/main.dart
+++ b/dev/integration_tests/ui/lib/main.dart
@@ -3,8 +3,41 @@
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_driver/driver_extension.dart';
-void main() => runApp(const Center(child: Text(
- 'flutter drive lib/xxx.dart',
- textDirection: TextDirection.ltr,
-)));
+void main() {
+ enableFlutterDriverExtension();
+
+ runApp(MaterialApp(
+ home: Material(
+ child: Builder(
+ builder: (BuildContext context) {
+ return FlatButton(
+ child: const Text(
+ 'flutter drive lib/xxx.dart',
+ textDirection: TextDirection.ltr,
+ ),
+ onPressed: () {
+ Navigator.push<Object>(
+ context,
+ MaterialPageRoute<dynamic>(
+ builder: (BuildContext context) {
+ return const Material(
+ child: Center(
+ child: Text(
+ 'navigated here',
+ textDirection: TextDirection.ltr,
+ ),
+ ),
+ );
+ },
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ),
+ ));
+}
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index 48aa6c9..f3c9324 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+import 'dart:developer' as developer;
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
@@ -1545,10 +1546,23 @@
for (NavigatorObserver observer in widget.observers)
observer.didPush(route, oldRoute);
assert(() { _debugLocked = false; return true; }());
- _cancelActivePointers();
+ _afterNavigation();
return route.popped;
}
+ void _afterNavigation() {
+ const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
+ if (!isReleaseMode) {
+ // This event is used by performance tools that show stats for the
+ // time interval since the last navigation event occurred ensuring that
+ // stats only reflect the current page.
+ // These tools do not need to know exactly what the new route is so no
+ // attempt is made to describe the current route as part of the event.
+ developer.postEvent('Flutter.Navigation', <String, dynamic>{});
+ }
+ _cancelActivePointers();
+ }
+
/// Replace the current route of the navigator by pushing the given route and
/// then disposing the previous route once the new route has finished
/// animating in.
@@ -1597,7 +1611,7 @@
for (NavigatorObserver observer in widget.observers)
observer.didReplace(newRoute: newRoute, oldRoute: oldRoute);
assert(() { _debugLocked = false; return true; }());
- _cancelActivePointers();
+ _afterNavigation();
return newRoute.popped;
}
@@ -1650,7 +1664,7 @@
observer.didRemove(removedRoute, oldRoute);
}
assert(() { _debugLocked = false; return true; }());
- _cancelActivePointers();
+ _afterNavigation();
return newRoute.popped;
}
@@ -1800,7 +1814,7 @@
assert(!debugPredictedWouldPop);
}
assert(() { _debugLocked = false; return true; }());
- _cancelActivePointers();
+ _afterNavigation();
return true;
}
@@ -1841,7 +1855,7 @@
observer.didRemove(route, previousRoute);
route.dispose();
assert(() { _debugLocked = false; return true; }());
- _cancelActivePointers();
+ _afterNavigation();
}
/// Immediately remove a route from the navigator, and [Route.dispose] it. The