1
0
mirror of https://github.com/flutter/samples.git synced 2025-11-08 13:58:47 +00:00

Add web_startup_analyzer to material_3_demo (#2152)

This commit is contained in:
John Ryan
2024-02-01 06:04:47 -08:00
committed by GitHub
parent 6c10c75e2b
commit b05384a4a6
8 changed files with 214 additions and 109 deletions

View File

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

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 => {};
}

View File

@@ -2,96 +2,5 @@
// 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 '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;
}
}
export 'src/web_startup_analyzer_io.dart'
if (dart.library.js_interop) 'src/web_startup_analyzer.dart';