flawsnap 0.0.1
flawsnap: ^0.0.1 copied to clipboard
In-app bug reporting for Flutter with annotated screenshots, device information, logs, and network request summaries.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flawsnap/flawsnap.dart';
// Run with: flutter run --dart-define-from-file=dart_defines.env
const _apiUrl = String.fromEnvironment('SNAPLOG_API', defaultValue: 'http://localhost:8080/api/v1');
const _projectKey = String.fromEnvironment('SNAPLOG_KEY');
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Snaplog.init(apiUrl: _apiUrl, projectKey: _projectKey);
Snaplog.identify(name: 'Example Tester');
runApp(Snaplog.wrap(const ExampleApp()));
}
final _http = Snaplog.httpClient();
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'BrightCart',
theme: ThemeData(colorSchemeSeed: Colors.teal, useMaterial3: true),
navigatorObservers: [Snaplog.navigatorObserver],
initialRoute: '/shop',
routes: {
'/shop': (_) => const ShopScreen(),
'/checkout': (_) => const CheckoutScreen(),
},
);
}
}
class ShopScreen extends StatelessWidget {
const ShopScreen({super.key});
@override
Widget build(BuildContext context) {
const items = [('Sun hat', 24.0), ('Beach towel', 18.0), ('Sandals', 39.0)];
return Scaffold(
appBar: AppBar(title: const Text('BrightCart')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
for (final (name, price) in items)
Card(
child: ListTile(
title: Text(name),
subtitle: Text('\$${price.toStringAsFixed(2)}'),
trailing: FilledButton(
onPressed: () => debugPrint('Added $name to cart'),
child: const Text('Add'),
),
),
),
const SizedBox(height: 16),
OutlinedButton(
onPressed: () async {
// Recorded in the report's network tab (404 from the local API).
await _http.get(Uri.parse('http://localhost:8080/api/v1/cart/does-not-exist'));
debugPrint('Cart sync failed: server returned 404');
},
child: const Text('Sync cart (fails)'),
),
const SizedBox(height: 8),
FilledButton.tonal(
onPressed: () => Navigator.pushNamed(context, '/checkout'),
child: const Text('Go to checkout'),
),
],
),
);
}
}
class CheckoutScreen extends StatelessWidget {
const CheckoutScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Checkout')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Total: NaN', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600)),
const SizedBox(height: 16),
FilledButton(
onPressed: () => Snaplog.log('Pay tapped with total NaN', level: 'error'),
child: const Text('Pay now'),
),
const SizedBox(height: 8),
TextButton(
onPressed: Snaplog.show,
child: const Text('Report a problem'),
),
],
),
),
);
}
}