flutter_mobile_diagnostics 0.2.0
flutter_mobile_diagnostics: ^0.2.0 copied to clipboard
Device health diagnostics for Flutter. Identity plus battery, memory, storage, network, Bluetooth, security, and performance with JSON reports.
import 'package:flutter/material.dart';
import 'package:flutter_mobile_diagnostics/flutter_mobile_diagnostics.dart';
import 'package:flutter_mobile_diagnostics_example/pages/dashboard_page.dart';
import 'package:flutter_mobile_diagnostics_example/pages/domain_detail_page.dart';
import 'package:flutter_mobile_diagnostics_example/pages/json_preview_page.dart';
import 'package:flutter_mobile_diagnostics_example/pages/live_watch_page.dart';
import 'package:flutter_mobile_diagnostics_example/pages/settings_page.dart';
import 'package:flutter_mobile_diagnostics_example/ui/app_theme.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const DiagnosticsExampleApp());
}
class DiagnosticsExampleApp extends StatefulWidget {
const DiagnosticsExampleApp({
super.key,
this.diagnostics = const MobileDiagnostics(),
this.watchInterval = const Duration(seconds: 3),
});
final MobileDiagnostics diagnostics;
final Duration watchInterval;
@override
State<DiagnosticsExampleApp> createState() => _DiagnosticsExampleAppState();
}
class _DiagnosticsExampleAppState extends State<DiagnosticsExampleApp> {
DiagnosticsOptions _options = DiagnosticsOptions.defaults;
DiagnosticsReport? _report;
Object? _error;
bool _loading = true;
int _tabIndex = 0;
String _platformVersion = 'unknown';
@override
void initState() {
super.initState();
_loadPlatformVersion();
_refresh();
}
Future<void> _loadPlatformVersion() async {
try {
final version = await widget.diagnostics.getPlatformVersion();
if (!mounted) {
return;
}
setState(() => _platformVersion = version ?? 'unknown');
} catch (_) {
if (!mounted) {
return;
}
setState(() => _platformVersion = 'unavailable');
}
}
Future<void> _refresh() async {
setState(() {
_loading = true;
_error = null;
});
MobileDiagnostics.clearCache();
try {
final report =
await widget.diagnostics.getFullReport(options: _options);
if (!mounted) {
return;
}
setState(() {
_report = report;
_loading = false;
});
} catch (error) {
if (!mounted) {
return;
}
setState(() {
_error = error;
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Device Diagnostics',
debugShowCheckedModeBanner: false,
theme: buildAppTheme(),
home: HomeShell(
tabIndex: _tabIndex,
onTabChanged: (index) => setState(() => _tabIndex = index),
loading: _loading,
error: _error,
report: _report,
options: _options,
platformVersion: _platformVersion,
diagnostics: widget.diagnostics,
watchInterval: widget.watchInterval,
onRefresh: _refresh,
onOptionsChanged: (options) {
setState(() => _options = options);
},
),
);
}
}
class HomeShell extends StatelessWidget {
const HomeShell({
super.key,
required this.tabIndex,
required this.onTabChanged,
required this.loading,
required this.error,
required this.report,
required this.options,
required this.platformVersion,
required this.diagnostics,
required this.watchInterval,
required this.onRefresh,
required this.onOptionsChanged,
});
final int tabIndex;
final ValueChanged<int> onTabChanged;
final bool loading;
final Object? error;
final DiagnosticsReport? report;
final DiagnosticsOptions options;
final String platformVersion;
final MobileDiagnostics diagnostics;
final Duration watchInterval;
final VoidCallback onRefresh;
final ValueChanged<DiagnosticsOptions> onOptionsChanged;
@override
Widget build(BuildContext context) {
final titles = const [
('Device Diagnostics', 'Your device health at a glance'),
('Live Monitor', 'Battery, memory, and network stream'),
('Diagnostic Report', 'Export JSON for support tickets'),
('Settings', 'Tune collection options'),
];
final header = titles[tabIndex];
return Scaffold(
body: SafeArea(
child: Column(
children: [
_AppHeader(
title: header.$1,
subtitle: header.$2,
loading: loading,
onRefresh: onRefresh,
),
Expanded(
child: switch (tabIndex) {
1 => LiveWatchPage(
diagnostics: diagnostics,
options: options,
interval: watchInterval,
),
2 => JsonPreviewPage(
diagnostics: diagnostics,
options: options,
),
3 => SettingsPage(
options: options,
platformVersion: platformVersion,
onChanged: onOptionsChanged,
),
_ => DashboardPage(
loading: loading,
error: error,
report: report,
onRefresh: onRefresh,
onOpenReport: () => onTabChanged(2),
onOpenDomain: (domain) {
final current = report;
if (current == null) {
return;
}
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => DomainDetailPage(
domain: domain,
diagnostics: diagnostics,
options: options,
report: current,
),
),
);
},
),
},
),
],
),
),
bottomNavigationBar: NavigationBar(
selectedIndex: tabIndex,
onDestinationSelected: onTabChanged,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home_rounded),
label: 'Overview',
),
NavigationDestination(
icon: Icon(Icons.sensors_outlined),
selectedIcon: Icon(Icons.sensors),
label: 'Live',
),
NavigationDestination(
icon: Icon(Icons.description_outlined),
selectedIcon: Icon(Icons.description_rounded),
label: 'Report',
),
NavigationDestination(
icon: Icon(Icons.settings_outlined),
selectedIcon: Icon(Icons.settings_rounded),
label: 'Settings',
),
],
),
);
}
}
class _AppHeader extends StatelessWidget {
const _AppHeader({
required this.title,
required this.subtitle,
required this.loading,
required this.onRefresh,
});
final String title;
final String subtitle;
final bool loading;
final VoidCallback onRefresh;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 8, 4),
child: Row(
children: [
IconButton(
tooltip: 'Menu',
onPressed: () {},
icon: const Icon(Icons.menu_rounded, color: AppColors.title),
),
Expanded(
child: Column(
children: [
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.title,
fontSize: 18,
fontWeight: FontWeight.w800,
),
),
Text(
subtitle,
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.subtitle,
fontSize: 12,
),
),
],
),
),
IconButton(
tooltip: 'Refresh full report',
onPressed: loading ? null : onRefresh,
icon: loading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.refresh_rounded, color: AppColors.title),
),
],
),
);
}
}