flutter_performance_monitor_plus 0.0.3 copy "flutter_performance_monitor_plus: ^0.0.3" to clipboard
flutter_performance_monitor_plus: ^0.0.3 copied to clipboard

Flutter in-app performance monitor overlay: live FPS, jank, build/raster times, rebuilds, memory, CPU, and network logging via draggable bubble.

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_performance_monitor_plus/flutter_performance_monitor_plus.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(
    PerformanceMonitorPlus(
      config: const PerformanceMonitorConfig(
        enableNetworkLogging: true,
        enableMemory: true,
        expandedByDefault: false,
      ),
      child: const DemoApp(),
    ),
  );
}

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Performance Monitor Plus',
      theme: ThemeData(
        brightness: Brightness.light,
        scaffoldBackgroundColor: const Color(
          0xFFF3F4F6,
        ), // light gray background
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.indigo,
          brightness: Brightness.light,
        ),
      ),
      home: const DemoHomePage(),
    );
  }
}

class DemoHomePage extends StatefulWidget {
  const DemoHomePage({super.key});

  @override
  State<DemoHomePage> createState() => _DemoHomePageState();
}

class _DemoHomePageState extends State<DemoHomePage>
    with SingleTickerProviderStateMixin {
  late final MonitoredHttpClient _client;
  late final AnimationController _controller;
  String _status = 'Idle';
  int _rebuildTicker = 0;
  Timer? _rebuildTimer;
  StreamSubscription<PerformanceMetrics>? _metricsSub;
  PerformanceMetrics? _latestMetrics;

  @override
  void initState() {
    super.initState();
    _client = MonitoredHttpClient();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    )..repeat(reverse: true);
    _rebuildTimer = Timer.periodic(const Duration(milliseconds: 500), (_) {
      setState(() => _rebuildTicker++);
    });
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _metricsSub = PerformanceMonitorPlus.metricsStream?.listen((metrics) {
        setState(() => _latestMetrics = metrics);
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    _rebuildTimer?.cancel();
    _client.close();
    _metricsSub?.cancel();
    super.dispose();
  }

  Future<void> _fetch() async {
    setState(() => _status = 'Requesting...');
    try {
      final response = await _client.get(
        Uri.parse('https://jsonplaceholder.typicode.com/todos/1'),
      );
      setState(() => _status = 'Status ${response.statusCode}');
    } catch (e) {
      setState(() => _status = 'Error $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Performance Monitor Plus'),
        actions: [
          IconButton(
            tooltip: 'Log manual request',
            onPressed: () {
              PerformanceMonitorPlus.logNetworkRequest(
                method: 'CUSTOM',
                url: 'app://local/action',
                duration: const Duration(milliseconds: 42),
                statusCode: 200,
              );
              setState(() => _status = 'Manual log added');
            },
            icon: const Icon(Icons.bug_report),
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Live rebuilds: $_rebuildTicker',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 12),
            FilledButton.icon(
              onPressed: _fetch,
              icon: const Icon(Icons.cloud_download),
              label: const Text('Trigger sample HTTP call'),
            ),
            const SizedBox(height: 8),
            Text(_status),
            const SizedBox(height: 24),
            Text(
              'Animated box (keeps the UI busy):',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 8),
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                return Transform.scale(
                  scale: 0.8 + _controller.value * 0.4,
                  child: child,
                );
              },
              child: Container(
                width: 120,
                height: 120,
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    colors: [Colors.blue.shade300, Colors.purple.shade400],
                  ),
                  borderRadius: BorderRadius.circular(16),
                ),
              ),
            ),
            const Spacer(),
            _MetricsSnapshot(metrics: _latestMetrics, statusText: _status),
            const Text(
              'Drag the bubble to reposition. Tap it to expand the live panel.',
            ),
          ],
        ),
      ),
    );
  }
}

class _MetricsSnapshot extends StatelessWidget {
  const _MetricsSnapshot({required this.metrics, required this.statusText});

  final PerformanceMetrics? metrics;
  final String statusText;
  static final Uri _metricsDocs = Uri.parse(
    'https://docs.flutter.dev/perf/metrics',
  );

  String _fmtDouble(double? value, {int fractionDigits = 1}) {
    if (value == null) return 'n/a';
    return value.toStringAsFixed(fractionDigits);
  }

  @override
  Widget build(BuildContext context) {
    final m = metrics;
    return Card(
      color: Colors.black.withValues(alpha: 0.1),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Text(
                  'Example outputs (developer API stream):',
                  style: Theme.of(context).textTheme.titleMedium,
                ),
                const Spacer(),
                IconButton(
                  tooltip: 'Open perf metrics guide',
                  icon: const Icon(Icons.help_outline),
                  onPressed: () => _openDocs(context),
                ),
              ],
            ),
            const SizedBox(height: 8),
            if (m == null)
              const Text('Waiting for metrics stream...')
            else
              Wrap(
                spacing: 12,
                runSpacing: 8,
                children: [
                  _chip('FPS', _fmtDouble(m.fps)),
                  _chip('Build ms', _fmtDouble(m.averageBuildTimeMs)),
                  _chip('Raster ms', _fmtDouble(m.averageRasterTimeMs)),
                  _chip('Rebuilds/s', '${m.rebuildsPerSecond}'),
                  _chip('Jank/s', '${m.jankPerSecond}'),
                  _chip('Memory MB', _fmtDouble(m.memoryInMB)),
                  _chip('CPU %', _fmtDouble(m.cpuUsagePercent)),
                  _chip('Hot reloads', '${m.hotReloadCount}'),
                  _chip('Hot restarts', '${m.hotRestartCount}'),
                  _chip('Status', statusText),
                ],
              ),
          ],
        ),
      ),
    );
  }

  Widget _chip(String label, String value) {
    return Chip(label: Text('$label: $value'));
  }

  Future<void> _openDocs(BuildContext context) async {
    bool launched = false;
    try {
      launched = await launchUrl(
        _metricsDocs,
        mode: LaunchMode.externalApplication,
      );
    } catch (_) {
      // Swallow plugin errors (e.g., MissingPluginException) and fall back below.
    }
    if (launched) return;

    Clipboard.setData(ClipboardData(text: _metricsDocs.toString()));
    if (!context.mounted) return;
    final messenger = ScaffoldMessenger.maybeOf(context);
    messenger?.showSnackBar(
      const SnackBar(
        content: Text(
          'Could not open Flutter performance metrics guide. Link copied.',
        ),
      ),
    );
  }
}
0
likes
160
points
27
downloads

Publisher

verified publisherkhokan.me

Weekly Downloads

Flutter in-app performance monitor overlay: live FPS, jank, build/raster times, rebuilds, memory, CPU, and network logging via draggable bubble.

Repository (GitHub)
View/report issues

Topics

#performance #monitoring #overlay #diagnostics #network

Documentation

API reference

License

MIT (license)

Dependencies

flutter, http, url_launcher

More

Packages that depend on flutter_performance_monitor_plus