http_monitor 0.0.8
http_monitor: ^0.0.8 copied to clipboard
A comprehensive HTTP tracking and debugging solution for Flutter. Monitor, store, and analyze all HTTP requests and responses with SQLite storage and beautiful UI.
import 'package:flutter/material.dart';
import 'package:http_monitor/http_monitor.dart';
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await HttpMonitor.init(
config: const HttpMonitorConfig(
enabled: true,
maxLogCount: 500,
autoCleanupDuration: Duration(days: 3),
maxResponseBodySize: 1024 * 512,
enableInReleaseMode: false,
),
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HTTP Monitor Example',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late Dio _dio;
late http.Client _httpClient;
bool _isLoading = false;
bool _isConcurrentLoading = false;
@override
void initState() {
super.initState();
_initializeClients();
}
void _initializeClients() {
_dio = Dio();
_dio.interceptors.add(
HttpMonitorDioInterceptor(logger: HttpMonitor.instance.logger),
);
_httpClient = HttpMonitorClient(
client: http.Client(),
logger: HttpMonitor.instance.logger,
);
}
@override
void dispose() {
_httpClient.close();
super.dispose();
}
void _openMonitor() {
Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => const CombinedMonitorWidget()));
}
void _openHttpMonitor() {
Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => const HttpMonitorWidget()));
}
void _openAppLogMonitor() {
Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => const AppLogMonitorWidget()));
}
Future<void> _makeGetRequest() async {
setState(() => _isLoading = true);
// Log app event
AppLogger.instance.log(
'Starting GET request to fetch post',
name: 'HomePage',
stackTrace: StackTrace.current,
);
try {
await _dio.get('https://jsonplaceholder.typicode.com/posts/1');
AppLogger.instance.log(
'GET request completed successfully',
name: 'HomePage',
);
_showSnack('GET request completed');
} catch (e) {
AppLogger.instance.log(
'GET request failed: $e',
name: 'HomePage',
stackTrace: StackTrace.current,
);
_showSnack('Error: $e');
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _makeConcurrentRequests() async {
setState(() => _isConcurrentLoading = true);
AppLogger.instance.log(
'Starting concurrent requests test (8 requests)',
name: 'ConcurrencyTest',
);
try {
final stopwatch = Stopwatch()..start();
// Test concurrent requests with Future.wait
await Future.wait([
_dio.get('https://jsonplaceholder.typicode.com/posts/1'),
_dio.get('https://jsonplaceholder.typicode.com/posts/2'),
_dio.get('https://jsonplaceholder.typicode.com/posts/3'),
_dio.get('https://jsonplaceholder.typicode.com/posts/4'),
_dio.get('https://jsonplaceholder.typicode.com/posts/5'),
_dio.post('https://jsonplaceholder.typicode.com/posts', data: {
'title': 'Test Post',
'body': 'Test body',
'userId': 1,
}),
_httpClient
.get(Uri.parse('https://jsonplaceholder.typicode.com/users/1')),
_httpClient
.get(Uri.parse('https://jsonplaceholder.typicode.com/users/2')),
]);
stopwatch.stop();
AppLogger.instance.log(
'Concurrent requests completed in ${stopwatch.elapsedMilliseconds}ms',
name: 'ConcurrencyTest',
);
_showSnack(
'Concurrent requests completed in ${stopwatch.elapsedMilliseconds}ms');
} catch (e) {
AppLogger.instance.log(
'Concurrent requests failed: $e',
name: 'ConcurrencyTest',
stackTrace: StackTrace.current,
);
_showSnack('Error in concurrent requests: $e');
} finally {
setState(() => _isConcurrentLoading = false);
}
}
Future<void> _makeHighConcurrencyTest() async {
setState(() => _isConcurrentLoading = true);
AppLogger.instance.log(
'Starting high concurrency test (50 requests)',
name: 'HighConcurrencyTest',
);
try {
final stopwatch = Stopwatch()..start();
// High concurrency test with 50 requests
final futures = <Future>[];
for (int i = 1; i <= 50; i++) {
futures.add(_dio.get('https://jsonplaceholder.typicode.com/posts/$i'));
}
await Future.wait(futures);
stopwatch.stop();
AppLogger.instance.log(
'50 concurrent requests completed in ${stopwatch.elapsedMilliseconds}ms',
name: 'HighConcurrencyTest',
);
_showSnack(
'50 concurrent requests completed in ${stopwatch.elapsedMilliseconds}ms');
} catch (e) {
AppLogger.instance.log(
'High concurrency test failed: $e',
name: 'HighConcurrencyTest',
stackTrace: StackTrace.current,
);
_showSnack('Error in high concurrency test: $e');
} finally {
setState(() => _isConcurrentLoading = false);
}
}
void _testAppLogger() {
// Test various app log scenarios
AppLogger.instance.log(
'This is a simple log message',
name: 'TestLogger',
);
AppLogger.instance.log(
'Checking native intro ad availability: placement enabled = true',
name: 'OnboardingAdsMixin',
stackTrace: StackTrace.current,
);
AppLogger.instance.log(
'User authentication state changed: logged_in',
name: 'AuthService',
);
AppLogger.instance.logWithTrace(
'This log includes automatic stack trace',
name: 'AutoTraceTest',
);
// Log without name
AppLogger.instance.log('Log without name');
// Log with only stack trace
AppLogger.instance.log(
null,
stackTrace: StackTrace.current,
);
_showSnack('Added 6 test app logs');
}
Future<void> _showQueueStatus() async {
final queueLength = HttpMonitor.instance.logger.queueLength;
final pendingCount = await HttpMonitor.instance.logger.pendingRequestCount;
final appLogCount = await AppLogger.instance.getLogCount();
_showSnack('Queue: $queueLength, Pending: $pendingCount, App Logs: $appLogCount');
}
void _showSnack(String msg) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('HTTP Monitor Example'),
actions: [
IconButton(
onPressed: _showQueueStatus,
icon: const Icon(Icons.info_outline),
tooltip: 'Show Queue Status',
),
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case 'combined':
_openMonitor();
break;
case 'http':
_openHttpMonitor();
break;
case 'app':
_openAppLogMonitor();
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'combined',
child: ListTile(
leading: Icon(Icons.dashboard),
title: Text('Combined Monitor'),
contentPadding: EdgeInsets.zero,
),
),
const PopupMenuItem(
value: 'http',
child: ListTile(
leading: Icon(Icons.http),
title: Text('HTTP Monitor'),
contentPadding: EdgeInsets.zero,
),
),
const PopupMenuItem(
value: 'app',
child: ListTile(
leading: Icon(Icons.article),
title: Text('App Log Monitor'),
contentPadding: EdgeInsets.zero,
),
),
],
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// HTTP Requests Section
const Text(
'HTTP Requests',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
// Single request button
ElevatedButton.icon(
onPressed: _isLoading ? null : _makeGetRequest,
icon: _isLoading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.download),
label:
Text(_isLoading ? 'Loading...' : 'Make GET Request (Dio)'),
),
const SizedBox(height: 12),
// Concurrent requests button
ElevatedButton.icon(
onPressed:
_isConcurrentLoading ? null : _makeConcurrentRequests,
icon: _isConcurrentLoading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.sync),
label: Text(_isConcurrentLoading
? 'Loading...'
: 'Test Concurrent Requests (8 requests)'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 12),
// High concurrency test button
ElevatedButton.icon(
onPressed:
_isConcurrentLoading ? null : _makeHighConcurrencyTest,
icon: _isConcurrentLoading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.flash_on),
label: Text(_isConcurrentLoading
? 'Loading...'
: 'High Concurrency Test (50 requests)'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 32),
// App Logger Section
const Text(
'App Logger',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: _testAppLogger,
icon: const Icon(Icons.article),
label: const Text('Add Test App Logs'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 32),
const Text(
'Test HTTP monitoring and App logging\nwith HTTP Monitor',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
),
),
),
floatingActionButton: FloatingMonitorButton(
onPressed: _openMonitor,
childBuilder: (size) => Container(
width: size,
height: size,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.amber,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 6,
offset: const Offset(2, 3),
),
],
),
child: const Icon(Icons.monitor, color: Colors.white),
),
),
);
}
}