frame_insights 0.1.0
frame_insights: ^0.1.0 copied to clipboard
In-app frame timing for Flutter: a live FPS HUD, a frame-time chart, and a dashboard that ties each janky frame to the traced work that produced it.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:frame_insights/ui.dart';
/// A tiny app that exercises everything the package shows.
///
/// Run it with `flutter run --profile` on a device or simulator: frame timings
/// are only meaningful there, and profile builds are the closest to what users
/// feel.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Attach the store before starting, and start before runApp, so the startup
// measurement covers everything up to the first frame.
PerformanceMonitorStore.instance.attach(PerformanceMonitor.instance);
await PerformanceHudController.instance.restore();
PerformanceMonitor.instance.start(scene: 'app_startup');
runApp(const ExampleApp());
}
final _navigatorKey = GlobalKey<NavigatorState>();
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'frame_insights example',
navigatorKey: _navigatorKey,
// Label every measurement with the route that produced it.
navigatorObservers: [PerformanceRouteObserver()],
// Show the Chinese wording instead of the English default.
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF285943)),
extensions: const [
PerformanceTheme(strings: PerformanceStrings.chinese),
],
),
builder: (context, child) => PerformanceHud(
store: PerformanceMonitorStore.instance,
controller: PerformanceHudController.instance,
onOpenDashboard: () =>
_navigatorKey.currentState?.push(performanceDashboardRoute()),
child: child ?? const SizedBox.shrink(),
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _count = 0;
/// Blocking the UI thread on purpose, so the dashboard has something to
/// attribute.
void _burnFrames() {
PerformanceTracer.instance.trace('example.burn', () {
var sum = 0;
for (var index = 0; index < 400000; index++) {
sum += index;
}
return sum;
});
setState(() => _count++);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('frame_insights')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Wrapped so the dashboard reports how often it rebuilds.
PerformanceRebuildCounter(
name: 'ExampleCounter',
child: Text(
'$_count',
style: Theme.of(context).textTheme.displaySmall,
),
),
const SizedBox(height: 16),
FilledButton(
onPressed: _burnFrames,
child: const Text('Burn some frames'),
),
const SizedBox(height: 8),
TextButton(
onPressed: () =>
_navigatorKey.currentState?.push(performanceDashboardRoute()),
child: const Text('Open the dashboard'),
),
const SizedBox(height: 8),
TextButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute<void>(
settings: const RouteSettings(name: 'checkout'),
builder: (_) => const Scaffold(
body: Center(child: Text('a named route')),
),
),
),
child: const Text('Push a named route'),
),
],
),
),
);
}
}