ios_device_monitor 0.1.0
ios_device_monitor: ^0.1.0 copied to clipboard
Comprehensive iOS device monitoring plugin with CPU, GPU, thermal, uptime, and advanced CPU usage tracking.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:ios_device_monitor/ios_device_monitor.dart';
import 'package:ios_device_monitor/models/models.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'iOS Device Monitor',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
),
cardTheme: CardThemeData(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
cardTheme: CardThemeData(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
),
home: const DeviceMonitorScreen(),
);
}
}
class DeviceMonitorScreen extends StatefulWidget {
const DeviceMonitorScreen({super.key});
@override
State<DeviceMonitorScreen> createState() => _DeviceMonitorScreenState();
}
class _DeviceMonitorScreenState extends State<DeviceMonitorScreen> {
CpuInfo? _cpuInfo;
GpuInfo? _gpuInfo;
ThermalState? _thermalState;
SystemUptime? _systemUptime;
AdvancedCpuUsage? _advancedCpuUsage;
bool _isLoading = false;
String? _errorMessage;
Timer? _refreshTimer;
@override
void initState() {
super.initState();
_loadAllData();
}
@override
void dispose() {
_refreshTimer?.cancel();
super.dispose();
}
Future<void> _loadAllData() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final results = await Future.wait([
IosDeviceMonitor.getCpuInfo(),
IosDeviceMonitor.getGpuInfo(),
IosDeviceMonitor.getThermalState(),
IosDeviceMonitor.getSystemUptime(),
IosDeviceMonitor.getAdvancedCpuUsage(),
]);
setState(() {
_cpuInfo = results[0] as CpuInfo;
_gpuInfo = results[1] as GpuInfo;
_thermalState = results[2] as ThermalState;
_systemUptime = results[3] as SystemUptime;
_advancedCpuUsage = results[4] as AdvancedCpuUsage;
_isLoading = false;
});
} on PlatformException catch (e) {
setState(() {
_errorMessage = 'Platform Error: ${e.message}';
_isLoading = false;
});
} catch (e) {
setState(() {
_errorMessage = 'Error: $e';
_isLoading = false;
});
}
}
Future<void> _refreshCpuUsage() async {
try {
final cpuUsage = await IosDeviceMonitor.getAdvancedCpuUsage();
setState(() {
_advancedCpuUsage = cpuUsage;
});
} catch (e) {
debugPrint('Failed to refresh CPU usage: $e');
}
}
void _toggleAutoRefresh() {
setState(() {
if (_refreshTimer != null) {
_refreshTimer?.cancel();
_refreshTimer = null;
} else {
_refreshTimer = Timer.periodic(
const Duration(seconds: 2),
(_) => _refreshCpuUsage(),
);
}
});
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
debugPrint('cpu');
debugPrint(_cpuInfo?.architecture.toString());
debugPrint(_cpuInfo?.coreCount.toString());
debugPrint(_cpuInfo?.coreCountDescription.toString());
debugPrint('\n\n\ngpu');
debugPrint(_gpuInfo?.maxThreadsPerThreadgroup.toString());
debugPrint(_gpuInfo?.name.toString());
debugPrint(_gpuInfo?.recommendedMaxWorkingSetSize.toString());
debugPrint(_gpuInfo?.registryID.toString());
debugPrint(_gpuInfo?.supportedFeatures.toString());
debugPrint(_gpuInfo?.supportsFamily.toString());
debugPrint('\n\n\ntemperature');
debugPrint(_thermalState?.description.toString());
debugPrint(_thermalState?.isCritical.toString());
debugPrint(_thermalState?.isThrottling.toString());
debugPrint(_thermalState?.isWarning.toString());
debugPrint(_thermalState?.level.toString());
debugPrint(_thermalState?.state.toString());
debugPrint(_thermalState?.statusMessage.toString());
debugPrint(_thermalState?.thermalLevel.toString());
debugPrint('\n\n\nsystemup time');
debugPrint(_systemUptime?.bootDate.toString());
debugPrint(_systemUptime?.bootDateFormatted.toString());
debugPrint(_systemUptime?.bootTime.toString());
debugPrint(_systemUptime?.hasBeenRunningForADay.toString());
debugPrint(_systemUptime?.hasBeenRunningForAWeek.toString());
debugPrint(_systemUptime?.shortUptimeDescription.toString());
debugPrint(_systemUptime?.uptime.toString());
debugPrint(_systemUptime?.uptimeDays.toString());
debugPrint(_systemUptime?.uptimeFormatted.toString());
debugPrint(_systemUptime?.uptimeHours.toString());
debugPrint('\n\n\nadvanced');
debugPrint(_advancedCpuUsage?.activeThreadCount.toString());
debugPrint(_advancedCpuUsage?.averageCoreUsage.toString());
debugPrint(_advancedCpuUsage?.coreCount.toString());
debugPrint(_advancedCpuUsage?.detailedCoreUsages.toString());
debugPrint(_advancedCpuUsage?.isHighUsage.toString());
debugPrint(_advancedCpuUsage?.isLowUsage.toString());
debugPrint(_advancedCpuUsage?.isMediumUsage.toString());
debugPrint(_advancedCpuUsage?.systemTime.toString());
debugPrint(_advancedCpuUsage?.threadDetails.toString());
debugPrint(_advancedCpuUsage?.timestamp.toString());
debugPrint(_advancedCpuUsage?.timestampDate.toString());
debugPrint(_advancedCpuUsage?.timestampFormatted.toString());
debugPrint(_advancedCpuUsage?.totalThreadCount.toString());
debugPrint(_advancedCpuUsage?.totalTime.toString());
debugPrint(_advancedCpuUsage?.usage.toString());
debugPrint(_advancedCpuUsage?.usageLevel.toString());
debugPrint(_advancedCpuUsage?.usagePercentageString.toString());
debugPrint(_advancedCpuUsage?.userTime.toString());
}),
appBar: AppBar(
title: const Text('iOS Device Monitor'),
centerTitle: true,
elevation: 0,
backgroundColor: colorScheme.surface,
foregroundColor: colorScheme.onSurface,
actions: [
IconButton(
icon: Icon(_refreshTimer != null ? Icons.pause : Icons.play_arrow),
tooltip: _refreshTimer != null
? 'Stop Auto-Refresh'
: 'Start Auto-Refresh',
onPressed: _advancedCpuUsage != null ? _toggleAutoRefresh : null,
),
],
),
body: RefreshIndicator(
onRefresh: _loadAllData,
child: _buildBody(context, colorScheme),
),
);
}
Widget _buildBody(BuildContext context, ColorScheme colorScheme) {
if (_isLoading && _cpuInfo == null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
color: colorScheme.primary,
),
const SizedBox(height: 16),
Text(
'Loading device information...',
style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.7),
),
),
],
),
);
}
if (_errorMessage != null && _cpuInfo == null) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_outline,
size: 64,
color: colorScheme.error,
),
const SizedBox(height: 16),
Text(
'Failed to Load Data',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: colorScheme.error,
),
),
const SizedBox(height: 8),
Text(
_errorMessage!,
textAlign: TextAlign.center,
style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.7),
),
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: _loadAllData,
icon: const Icon(Icons.refresh),
label: const Text('Retry'),
),
],
),
),
);
}
return ListView(
padding: const EdgeInsets.all(16.0),
children: [
_buildCpuInfoSection(colorScheme),
const SizedBox(height: 16),
_buildGpuInfoSection(colorScheme),
const SizedBox(height: 16),
_buildThermalStateSection(colorScheme),
const SizedBox(height: 16),
_buildSystemUptimeSection(colorScheme),
const SizedBox(height: 16),
_buildAdvancedCpuUsageSection(colorScheme),
const SizedBox(height: 16),
Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Pull down to refresh all data',
style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.5),
fontSize: 12,
),
),
),
),
],
);
}
Widget _buildCpuInfoSection(ColorScheme colorScheme) {
if (_cpuInfo == null) return const SizedBox.shrink();
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.memory,
color: colorScheme.primary,
size: 28,
),
const SizedBox(width: 12),
Text(
'CPU Information',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const Divider(height: 24),
_buildInfoRow(
'Architecture',
_cpuInfo!.architecture ?? 'Unknown',
Icons.architecture,
colorScheme,
),
_buildInfoRow(
'Core Count',
_formatCoreCount(_cpuInfo!.coreCountDescription),
Icons.grid_view,
colorScheme,
),
if (_cpuInfo!.architecture?.toLowerCase().contains('arm64') ??
false)
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.apple,
size: 16,
color: colorScheme.onPrimaryContainer,
),
const SizedBox(width: 6),
Text(
'Apple Silicon',
style: TextStyle(
color: colorScheme.onPrimaryContainer,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
],
),
),
),
],
),
),
);
}
Widget _buildGpuInfoSection(ColorScheme colorScheme) {
if (_gpuInfo == null) return const SizedBox.shrink();
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.videogame_asset,
color: colorScheme.secondary,
size: 28,
),
const SizedBox(width: 12),
Text(
'GPU Information',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const Divider(height: 24),
_buildInfoRow(
'GPU Name',
_gpuInfo!.name,
Icons.label,
colorScheme,
),
if (_formatBytes(_gpuInfo!.recommendedMaxWorkingSetSize) != null)
_buildInfoRow(
'Max Working Set',
_formatBytes(_gpuInfo!.registryID)!,
Icons.storage,
colorScheme,
),
if (_gpuInfo!.registryID != null)
_buildInfoRow(
'Registry ID',
'${_gpuInfo!.registryID}',
Icons.fingerprint,
colorScheme,
),
if (_gpuInfo!.maxThreadsPerThreadgroup != null)
_buildInfoRow(
'Max Threads/Group',
'${_gpuInfo!.maxThreadsPerThreadgroup!.width}×'
'${_gpuInfo!.maxThreadsPerThreadgroup!.height}×'
'${_gpuInfo!.maxThreadsPerThreadgroup!.depth}',
Icons.apps,
colorScheme,
),
if (_gpuInfo!.supportedFeatures.isNotEmpty) ...[
const SizedBox(height: 12),
Text(
'Metal GPU Families',
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: colorScheme.onSurface.withValues(alpha: 0.7),
),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: _gpuInfo!.supportedFeatures.map((feature) {
return Chip(
label: Text(
feature,
style: TextStyle(
fontSize: 11,
color: colorScheme.onSecondaryContainer,
),
),
backgroundColor: colorScheme.secondaryContainer,
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
);
}).toList(),
),
],
],
),
),
);
}
Widget _buildThermalStateSection(ColorScheme colorScheme) {
if (_thermalState == null) return const SizedBox.shrink();
Color thermalColor;
IconData thermalIcon;
if (_thermalState!.isCritical) {
thermalColor = Colors.red;
thermalIcon = Icons.whatshot;
} else if (_thermalState!.isWarning) {
thermalColor = Colors.orange;
thermalIcon = Icons.warning_amber;
} else {
thermalColor = Colors.green;
thermalIcon = Icons.check_circle;
}
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.thermostat,
color: colorScheme.tertiary,
size: 28,
),
const SizedBox(width: 12),
Text(
'Thermal State',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const Divider(height: 24),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: thermalColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: thermalColor.withValues(alpha: 0.3),
width: 2,
),
),
child: Row(
children: [
Icon(
thermalIcon,
color: thermalColor,
size: 48,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_thermalState!.statusMessage ?? _thermalState!.state,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(
color: thermalColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
_thermalState!.description ??
'No description available',
style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.7),
),
),
],
),
),
],
),
),
const SizedBox(height: 16),
_buildInfoRow(
'Thermal State',
_thermalState!.state,
Icons.info_outline,
colorScheme,
),
_buildInfoRow(
'Level',
'${_thermalState!.level}',
Icons.layers,
colorScheme,
),
_buildInfoRow(
'Throttling',
_thermalState!.isThrottling ? 'Yes' : 'No',
_thermalState!.isThrottling
? Icons.speed
: Icons.check_circle_outline,
colorScheme,
),
],
),
),
);
}
Widget _buildSystemUptimeSection(ColorScheme colorScheme) {
if (_systemUptime == null) return const SizedBox.shrink();
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.access_time,
color: colorScheme.primary,
size: 28,
),
const SizedBox(width: 12),
Text(
'System Uptime',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const Divider(height: 24),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: colorScheme.primaryContainer.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
Icons.timer,
color: colorScheme.primary,
size: 48,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_systemUptime!.uptimeFormatted,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
_systemUptime!.shortUptimeDescription,
style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.7),
),
),
],
),
),
],
),
),
const SizedBox(height: 16),
_buildInfoRow(
'Boot Date',
_systemUptime!.bootDateFormatted,
Icons.calendar_today,
colorScheme,
),
_buildInfoRow(
'Uptime (seconds)',
'${_systemUptime!.uptime.toStringAsFixed(0)}s',
Icons.hourglass_bottom,
colorScheme,
),
_buildInfoRow(
'Uptime (hours)',
'${_systemUptime!.uptimeHours.toStringAsFixed(2)}h',
Icons.schedule,
colorScheme,
),
_buildInfoRow(
'Uptime (days)',
'${_systemUptime!.uptimeDays.toStringAsFixed(2)} days',
Icons.event,
colorScheme,
),
if (_systemUptime!.hasBeenRunningForAWeek)
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.amber.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.amber,
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.battery_alert,
size: 16,
color: Colors.amber,
),
const SizedBox(width: 6),
Text(
'Consider restarting your device',
style: TextStyle(
color: colorScheme.onSurface,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
],
),
),
),
],
),
),
);
}
Widget _buildAdvancedCpuUsageSection(ColorScheme colorScheme) {
if (_advancedCpuUsage == null) return const SizedBox.shrink();
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.show_chart,
color: colorScheme.secondary,
size: 28,
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Advanced CPU Usage',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
if (_refreshTimer != null)
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.green.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.refresh, size: 14, color: Colors.green),
SizedBox(width: 4),
Text(
'Live',
style: TextStyle(
color: Colors.green,
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
const Divider(height: 24),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
colorScheme.secondaryContainer.withValues(alpha: 0.5),
colorScheme.secondaryContainer.withValues(alpha: 0.2),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(
_advancedCpuUsage!.usagePercentageString,
style: Theme.of(context).textTheme.displayMedium?.copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
_advancedCpuUsage!.usageLevel,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: colorScheme.onSurface.withValues(alpha: 0.7),
),
),
const SizedBox(height: 16),
LinearProgressIndicator(
value: _advancedCpuUsage!.usage / 100,
minHeight: 8,
borderRadius: BorderRadius.circular(4),
backgroundColor:
colorScheme.onSurface.withValues(alpha: 0.1),
),
],
),
),
const SizedBox(height: 16),
_buildInfoRow(
'User Time',
'${_advancedCpuUsage!.userTime.toStringAsFixed(2)}s',
Icons.person,
colorScheme,
),
_buildInfoRow(
'System Time',
'${_advancedCpuUsage!.systemTime.toStringAsFixed(2)}s',
Icons.settings,
colorScheme,
),
_buildInfoRow(
'Active Threads',
'${_advancedCpuUsage!.activeThreadCount} / ${_advancedCpuUsage!.totalThreadCount}',
Icons.account_tree,
colorScheme,
),
_buildInfoRow(
'Core Count',
'${_advancedCpuUsage!.coreCount}',
Icons.grid_view,
colorScheme,
),
if (_advancedCpuUsage!.detailedCoreUsages.isNotEmpty) ...[
const SizedBox(height: 16),
Text(
'Per-Core Usage',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
..._advancedCpuUsage!.detailedCoreUsages.map((core) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Core ${core.coreIndex}',
style: TextStyle(
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
Text(
core.usageString,
style: TextStyle(
fontWeight: FontWeight.bold,
color: colorScheme.primary,
),
),
],
),
const SizedBox(height: 6),
LinearProgressIndicator(
value: core.usage / 100,
minHeight: 6,
borderRadius: BorderRadius.circular(3),
backgroundColor:
colorScheme.onSurface.withValues(alpha: 0.1),
color: _getCoreColor(core.usage, colorScheme),
),
],
),
);
}),
],
if (_advancedCpuUsage!.threadDetails
.where((t) => t.isActive && t.cpuUsage > 0)
.isNotEmpty) ...[
const SizedBox(height: 16),
Text(
'Active Threads (${_advancedCpuUsage!.threadDetails.where((t) => t.isActive && t.cpuUsage > 0).length})',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Container(
constraints: const BoxConstraints(maxHeight: 300),
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _advancedCpuUsage!.threadDetails
.where((t) => t.isActive && t.cpuUsage > 0)
.length >
10
? 10
: _advancedCpuUsage!.threadDetails
.where((t) => t.isActive && t.cpuUsage > 0)
.length,
itemBuilder: (context, index) {
final activeThreads = _advancedCpuUsage!.threadDetails
.where((t) => t.isActive && t.cpuUsage > 0)
.toList();
final thread = activeThreads[index];
return Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
thread.displayName,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
thread.cpuUsageString,
style: TextStyle(
fontWeight: FontWeight.bold,
color: colorScheme.primary,
fontSize: 13,
),
),
],
),
],
),
);
},
),
),
if (_advancedCpuUsage!.threadDetails
.where((t) => t.isActive && t.cpuUsage > 0)
.length >
10)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'Showing 10 of ${_advancedCpuUsage!.threadDetails.where((t) => t.isActive && t.cpuUsage > 0).length} active threads',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurface.withValues(alpha: 0.5),
fontStyle: FontStyle.italic,
),
),
),
],
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.access_time,
size: 14,
color: colorScheme.onSurface.withValues(alpha: 0.5),
),
const SizedBox(width: 6),
Text(
'Last updated: ${_formatTimestamp(_advancedCpuUsage!.timestamp)}',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurface.withValues(alpha: 0.5),
),
),
],
),
],
),
),
);
}
Widget _buildInfoRow(
String label,
String value,
IconData icon,
ColorScheme colorScheme,
) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Row(
children: [
Icon(
icon,
size: 20,
color: colorScheme.onSurface.withValues(alpha: 0.6),
),
const SizedBox(width: 12),
Expanded(
child: Text(
label,
style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.7),
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(width: 12),
Flexible(
child: Text(
value,
textAlign: TextAlign.end,
style: TextStyle(
color: colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
),
],
),
);
}
Color _getCoreColor(double usage, ColorScheme colorScheme) {
if (usage > 80) return Colors.red;
if (usage > 50) return Colors.orange;
if (usage > 25) return colorScheme.primary;
return Colors.green;
}
String _formatTimestamp(double timestamp) {
final date =
DateTime.fromMillisecondsSinceEpoch((timestamp * 1000).toInt());
return '${date.hour.toString().padLeft(2, '0')}:'
'${date.minute.toString().padLeft(2, '0')}:'
'${date.second.toString().padLeft(2, '0')}';
}
String _formatCoreCount(Map<String, int?> cores) {
final physical = cores['physical'];
final logical = cores['logical'];
final total = cores['total'];
if (physical != null && logical != null) {
return '$physical Physical, $logical Logical';
}
return '${total ?? 0} Cores';
}
String? _formatBytes(int? bytes) {
if (bytes == null) return null;
if (bytes >= 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
} else if (bytes >= 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB';
}
return '$bytes B';
}
}