feat(crash): add app version and device info to crash reports
Issue reports now include: - App version (from package_info_plus) - OS name and version (non-personal, from dart:io Platform) - Error and stack trace wrapped in triple-backtick code blocks so Codeberg renders them as preformatted text Closes #59 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
6f030213a7
commit
26a9a5e6f3
@@ -1,5 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class CrashScreen extends StatelessWidget {
|
||||
@@ -12,6 +15,20 @@ class CrashScreen extends StatelessWidget {
|
||||
final Object exception;
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
Future<String> _buildReport() async {
|
||||
String version = 'unknown';
|
||||
try {
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
version = '${info.version}+${info.buildNumber}';
|
||||
} catch (_) {}
|
||||
final platform =
|
||||
'${Platform.operatingSystem} ${Platform.operatingSystemVersion}';
|
||||
return 'App Version: $version\n'
|
||||
'Platform: $platform\n\n'
|
||||
'Error:\n```\n$exception\n```\n\n'
|
||||
'Stack Trace:\n```\n$stackTrace\n```';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
@@ -74,7 +91,7 @@ class CrashScreen extends StatelessWidget {
|
||||
const SizedBox(height: 24),
|
||||
FilledButton.icon(
|
||||
onPressed: () async {
|
||||
final data = 'Error: $exception\n\nStack Trace:\n$stackTrace';
|
||||
final data = await _buildReport();
|
||||
await Clipboard.setData(ClipboardData(text: data));
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -91,12 +108,11 @@ class CrashScreen extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final report = await _buildReport();
|
||||
final title = Uri.encodeComponent(
|
||||
'Crash: ${exception.toString().split('\n').first}',
|
||||
);
|
||||
final body = Uri.encodeComponent(
|
||||
'Error: $exception\n\nStack Trace:\n$stackTrace',
|
||||
);
|
||||
final body = Uri.encodeComponent(report);
|
||||
final url = Uri.parse(
|
||||
'https://codeberg.org/guettli/sharedinbox/issues/new?title=$title&body=$body',
|
||||
);
|
||||
@@ -115,9 +131,7 @@ class CrashScreen extends StatelessWidget {
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
duration: const Duration(seconds: 5),
|
||||
content: Text('Error: $e'),
|
||||
|
||||
@@ -49,6 +49,9 @@ dependencies:
|
||||
flutter_local_notifications: ^18.0.1
|
||||
workmanager: ^0.9.0
|
||||
|
||||
# App version metadata for crash reports
|
||||
package_info_plus: ^8.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
import 'package:sharedinbox/ui/screens/crash_screen.dart';
|
||||
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
|
||||
@@ -23,6 +24,16 @@ class MockUrlLauncher extends Mock
|
||||
}
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
PackageInfo.setMockInitialValues(
|
||||
appName: 'SharedInbox',
|
||||
packageName: 'org.sharedinbox',
|
||||
version: '1.0.0',
|
||||
buildNumber: '42',
|
||||
buildSignature: '',
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('CrashScreen shows error details and has a report button', (
|
||||
tester,
|
||||
) async {
|
||||
@@ -56,9 +67,7 @@ void main() {
|
||||
mock.launchedUrl,
|
||||
contains('title=Crash%3A%20TestException%3A%20something%20broke'),
|
||||
);
|
||||
expect(
|
||||
mock.launchedUrl,
|
||||
contains('body=Error%3A%20TestException%3A%20something%20broke'),
|
||||
);
|
||||
expect(mock.launchedUrl, contains('App%20Version%3A%201.0.0%2B42'));
|
||||
expect(mock.launchedUrl, contains('TestException%3A%20something%20broke'));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user