Add web_startup_analyzer to material_3_demo (#2152)

pull/2156/head
John Ryan 11 months ago committed by GitHub
parent 6c10c75e2b
commit b05384a4a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2,12 +2,31 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:web_startup_analyzer/web_startup_analyzer.dart';
import 'constants.dart'; import 'constants.dart';
import 'home.dart'; import 'home.dart';
void main() { void main() async {
var analyzer = WebStartupAnalyzer(additionalFrameCount: 10);
debugPrint(json.encode(analyzer.startupTiming));
analyzer.onFirstFrame.addListener(() {
debugPrint(json.encode({'firstFrame': analyzer.onFirstFrame.value}));
});
analyzer.onFirstPaint.addListener(() {
debugPrint(json.encode({
'firstPaint': analyzer.onFirstPaint.value?.$1,
'firstContentfulPaint': analyzer.onFirstPaint.value?.$2,
}));
});
analyzer.onAdditionalFrames.addListener(() {
debugPrint(json.encode({
'additionalFrames': analyzer.onAdditionalFrames.value,
}));
});
runApp( runApp(
const App(), const App(),
); );

@ -9,6 +9,7 @@ version: 1.0.0+1
environment: environment:
sdk: ^3.2.0 sdk: ^3.2.0
flutter: ^3.16.0
dependencies: dependencies:
flutter: flutter:
@ -16,6 +17,8 @@ dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
url_launcher: ^6.1.8 url_launcher: ^6.1.8
web_startup_analyzer:
path: ../web/_packages/web_startup_analyzer
dev_dependencies: dev_dependencies:
analysis_defaults: analysis_defaults:

@ -38,17 +38,25 @@
</script> </script>
<!-- This script adds the flutter initialization JS code --> <!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script> <script src="flutter.js" defer></script>
<script type="text/javascript" src="assets/packages/web_startup_analyzer/lib/web_startup_analyzer.js"></script>
</head> </head>
<body> <body>
<script> <script>
var flutterWebStartupAnalyzer = new FlutterWebStartupAnalyzer();
var analyzer = flutterWebStartupAnalyzer;
window.addEventListener('load', function(ev) { window.addEventListener('load', function(ev) {
// Download main.dart.js analyzer.markStart("loadEntrypoint");
_flutter.loader.loadEntrypoint({ _flutter.loader.loadEntrypoint({
serviceWorker: { serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion, serviceWorkerVersion: serviceWorkerVersion,
}, },
onEntrypointLoaded: function(engineInitializer) { onEntrypointLoaded: function(engineInitializer) {
analyzer.markFinished("loadEntrypoint");
analyzer.markStart("initializeEngine");
engineInitializer.initializeEngine().then(function(appRunner) { engineInitializer.initializeEngine().then(function(appRunner) {
analyzer.markFinished("initializeEngine");
analyzer.markStart("appRunnerRunApp");
appRunner.runApp(); appRunner.runApp();
}); });
} }

@ -3,7 +3,8 @@ description: "flutter_web_startup_analyzer example"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: '>=3.4.0-16.0.dev <4.0.0' sdk: ^3.2.0
flutter: ^3.16.0
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter

@ -0,0 +1,103 @@
// Copyright 2021 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'package:flutter/widgets.dart';
import 'package:web_startup_analyzer/src/web_startup_analyzer_base.dart';
import 'frame_analyzer.dart';
import 'startup_analyzer.dart';
class WebStartupAnalyzer extends WebStartupAnalyzerBase {
final WidgetsBinding _widgetsBinding;
late final FrameAnalyzer _frameAnalyzer;
List<int>? _additionalFrames;
@override
Map<String, dynamic> startupTiming = {};
@override
double get domContentLoaded =>
(flutterWebStartupAnalyzer.timings['domContentLoaded'] as JSNumber)
.toDartDouble;
@override
double get loadEntrypoint =>
(flutterWebStartupAnalyzer.timings['loadEntrypoint'] as JSNumber)
.toDartDouble;
@override
double get initializeEngine =>
(flutterWebStartupAnalyzer.timings['initializeEngine'] as JSNumber)
.toDartDouble;
@override
double get appRunnerRunApp =>
(flutterWebStartupAnalyzer.timings['appRunnerRunApp'] as JSNumber)
.toDartDouble;
@override
double? get firstFrame =>
(flutterWebStartupAnalyzer.timings['firstFrame'] as JSNumber?)
?.toDartDouble;
@override
double? get firstPaint =>
(flutterWebStartupAnalyzer.timings['first-paint'] as JSNumber?)
?.toDartDouble;
@override
double? get firstContentfulPaint =>
(flutterWebStartupAnalyzer.timings['first-contentful-paint'] as JSNumber?)
?.toDartDouble;
@override
List<int>? get additionalFrames => _additionalFrames;
WebStartupAnalyzer({int additionalFrameCount = 5})
: _widgetsBinding = WidgetsFlutterBinding.ensureInitialized() {
_frameAnalyzer =
FrameAnalyzer(_widgetsBinding, additionalFrames: additionalFrameCount);
_captureStartupMetrics();
startupTiming = {
'domContentLoaded': domContentLoaded,
'loadEntrypoint': loadEntrypoint,
'initializeEngine': initializeEngine,
'appRunnerRunApp': appRunnerRunApp,
};
_captureFirstFrame().then((value) {
flutterWebStartupAnalyzer.captureAll();
onFirstFrame.value = firstFrame;
// Capture first-paint and first-contentful-paint
Future.delayed(const Duration(milliseconds: 200)).then((_) {
flutterWebStartupAnalyzer.capturePaint();
onFirstPaint.value = (firstPaint!, firstContentfulPaint!);
});
});
captureFlutterFrameData().then((value) {
_additionalFrames = value;
onAdditionalFrames.value = value;
});
onChange =
Listenable.merge([onFirstFrame, onFirstPaint, onAdditionalFrames]);
}
_captureStartupMetrics() {
flutterWebStartupAnalyzer.markFinished('appRunnerRunApp');
flutterWebStartupAnalyzer.captureAll();
}
Future<void> _captureFirstFrame() {
final completer = Completer();
flutterWebStartupAnalyzer.markStart('firstFrame');
_widgetsBinding.addPostFrameCallback((timeStamp) {
flutterWebStartupAnalyzer.markFinished('firstFrame');
flutterWebStartupAnalyzer.capture('firstFrame');
completer.complete();
});
return completer.future;
}
Future<List<int>> captureFlutterFrameData() async {
await _frameAnalyzer.captureAdditionalFrames();
return _frameAnalyzer.additionalFrameTimes;
}
}

@ -0,0 +1,25 @@
// Copyright 2021 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
// Base class for the web (real) implementation and dart:io (stub)
// implementation.
abstract class WebStartupAnalyzerBase {
late final Listenable onChange;
ValueNotifier<double?> onFirstFrame = ValueNotifier(null);
ValueNotifier<(double, double)?> onFirstPaint = ValueNotifier(null);
ValueNotifier<List<int>?> onAdditionalFrames = ValueNotifier(null);
double get domContentLoaded;
double get loadEntrypoint;
double get initializeEngine;
double get appRunnerRunApp;
double? get firstFrame;
double? get firstPaint;
double? get firstContentfulPaint;
List<int>? get additionalFrames;
Map<String, dynamic> get startupTiming;
}

@ -0,0 +1,37 @@
// Copyright 2021 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'web_startup_analyzer_base.dart';
// This class is a stub so that unit tests can run without importing
// dart:js_interop and related packages.
class WebStartupAnalyzer extends WebStartupAnalyzerBase {
WebStartupAnalyzer({int additionalFrameCount = 0});
List<int>? get additionalFrames => [];
@override
double get appRunnerRunApp => 0.0;
@override
double get domContentLoaded => 0.0;
@override
double? get firstContentfulPaint => 0.0;
@override
double? get firstFrame => 0.0;
@override
double? get firstPaint => 0.0;
@override
double get initializeEngine => 0.0;
@override
double get loadEntrypoint => 0.0;
@override
Map<String, dynamic> get startupTiming => {};
}

@ -2,96 +2,5 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; export 'src/web_startup_analyzer_io.dart'
import 'dart:js_interop'; if (dart.library.js_interop) 'src/web_startup_analyzer.dart';
import 'dart:js_interop_unsafe';
import 'package:flutter/widgets.dart';
import 'src/frame_analyzer.dart';
import 'src/startup_analyzer.dart';
class WebStartupAnalyzer {
final WidgetsBinding _widgetsBinding;
late final FrameAnalyzer _frameAnalyzer;
List<int>? _additionalFrames;
late final Listenable onChange;
Map<String, dynamic> startupTiming = {};
ValueNotifier<double?> onFirstFrame = ValueNotifier(null);
ValueNotifier<(double, double)?> onFirstPaint = ValueNotifier(null);
ValueNotifier<List<int>?> onAdditionalFrames = ValueNotifier(null);
double get domContentLoaded =>
(flutterWebStartupAnalyzer.timings['domContentLoaded'] as JSNumber)
.toDartDouble;
double get loadEntrypoint =>
(flutterWebStartupAnalyzer.timings['loadEntrypoint'] as JSNumber)
.toDartDouble;
double get initializeEngine =>
(flutterWebStartupAnalyzer.timings['initializeEngine'] as JSNumber)
.toDartDouble;
double get appRunnerRunApp =>
(flutterWebStartupAnalyzer.timings['appRunnerRunApp'] as JSNumber)
.toDartDouble;
double? get firstFrame =>
(flutterWebStartupAnalyzer.timings['firstFrame'] as JSNumber?)
?.toDartDouble;
double? get firstPaint =>
(flutterWebStartupAnalyzer.timings['first-paint'] as JSNumber?)
?.toDartDouble;
double? get firstContentfulPaint =>
(flutterWebStartupAnalyzer.timings['first-contentful-paint'] as JSNumber?)
?.toDartDouble;
List<int>? get additionalFrames => _additionalFrames;
WebStartupAnalyzer({int additionalFrameCount = 5})
: _widgetsBinding = WidgetsFlutterBinding.ensureInitialized() {
_frameAnalyzer =
FrameAnalyzer(_widgetsBinding, additionalFrames: additionalFrameCount);
_captureStartupMetrics();
startupTiming = {
'domContentLoaded': domContentLoaded,
'loadEntrypoint': loadEntrypoint,
'initializeEngine': initializeEngine,
'appRunnerRunApp': appRunnerRunApp,
};
_captureFirstFrame().then((value) {
flutterWebStartupAnalyzer.captureAll();
onFirstFrame.value = firstFrame;
// Capture first-paint and first-contentful-paint
Future.delayed(const Duration(milliseconds: 200)).then((_) {
flutterWebStartupAnalyzer.capturePaint();
onFirstPaint.value = (firstPaint!, firstContentfulPaint!);
});
});
captureFlutterFrameData().then((value) {
_additionalFrames = value;
onAdditionalFrames.value = value;
});
onChange =
Listenable.merge([onFirstFrame, onFirstPaint, onAdditionalFrames]);
}
_captureStartupMetrics() {
flutterWebStartupAnalyzer.markFinished('appRunnerRunApp');
flutterWebStartupAnalyzer.captureAll();
}
Future<void> _captureFirstFrame() {
final completer = Completer();
flutterWebStartupAnalyzer.markStart('firstFrame');
_widgetsBinding.addPostFrameCallback((timeStamp) {
flutterWebStartupAnalyzer.markFinished('firstFrame');
flutterWebStartupAnalyzer.capture('firstFrame');
completer.complete();
});
return completer.future;
}
Future<List<int>> captureFlutterFrameData() async {
await _frameAnalyzer.captureAdditionalFrames();
return _frameAnalyzer.additionalFrameTimes;
}
}

Loading…
Cancel
Save