mirror of
https://github.com/flutter/samples.git
synced 2025-11-09 06:18:49 +00:00
Add web_startup_analyzer to material_3_demo (#2152)
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user