net_logger 1.0.3
net_logger: ^1.0.3 copied to clipboard
A local API request logger client package for Flutter/Dart applications.
example/lib/main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import 'package:net_logger/net_logger.dart';
void main() {
runApp(const NetLoggerExampleApp());
}
class NetLoggerExampleApp extends StatelessWidget {
const NetLoggerExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Net Logger Example',
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(useMaterial3: true).copyWith(
colorScheme: const ColorScheme.dark(
primary: Color(0xFF6366F1), // Indigo
secondary: Color(0xFF10B981), // Emerald green
surface: Color(0xFF0F172A), // Slate
),
scaffoldBackgroundColor: const Color(0xFF020617), // Deep black-slate
),
home: const ExampleHomeScreen(),
);
}
}
class ExampleHomeScreen extends StatefulWidget {
const ExampleHomeScreen({super.key});
@override
State<ExampleHomeScreen> createState() => _ExampleHomeScreenState();
}
class _ExampleHomeScreenState extends State<ExampleHomeScreen> {
late final Dio _dio;
late final http.Client _httpClient;
final List<String> _localConsoleLogs = [];
final ScrollController _scrollController = ScrollController();
static const String serverUrl = 'http://localhost:8080';
@override
void initState() {
super.initState();
// 1. Initialize Dio and add the interceptor
_dio = Dio(BaseOptions(
baseUrl: 'https://jsonplaceholder.typicode.com',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 5),
));
_dio.interceptors.add(NetLoggerInterceptor(
appName: 'Dio App Example',
serverUrl: serverUrl,
enableConsoleLogs: true,
));
// 2. Initialize NetLoggerHttpClient wrapping a standard http Client
_httpClient = NetLoggerHttpClient(
http.Client(),
appName: 'http App Example',
serverUrl: serverUrl,
enableConsoleLogs: true,
);
}
void _addLog(String msg) {
setState(() {
_localConsoleLogs.add('[${DateTime.now().toString().substring(11, 23)}] $msg');
});
// Scroll console to bottom
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
});
}
// --- DIO requests ---
Future<void> _dioGet() async {
_addLog('Dio: Sending GET request to /posts/1...');
try {
final response = await _dio.get('/posts/1');
_addLog('Dio: Received ${response.statusCode} - Title: ${response.data['title']}');
} catch (e) {
_addLog('Dio: Error occurred - $e');
}
}
Future<void> _dioPost() async {
_addLog('Dio: Sending POST request to /posts...');
try {
final response = await _dio.post(
'/posts',
data: {
'title': 'Flutter Net Logger',
'body': 'This is an awesome package to log HTTP requests.',
'userId': 42,
},
);
_addLog('Dio: Received ${response.statusCode} - ID: ${response.data['id']}');
} catch (e) {
_addLog('Dio: Error occurred - $e');
}
}
Future<void> _dioMultipart() async {
_addLog('Dio: Simulating Multipart Upload request...');
try {
// Construct Form Data to see how it logs
final formData = FormData.fromMap({
'name': 'Athul',
'project': 'net_logger',
'file': MultipartFile.fromString('Hello, this is a mock file content.', filename: 'info.txt'),
});
final response = await _dio.post('/posts', data: formData);
_addLog('Dio: Received ${response.statusCode} for multipart upload.');
} catch (e) {
_addLog('Dio: Error occurred - $e');
}
}
Future<void> _dioError() async {
_addLog('Dio: Sending GET to non-existent endpoint to verify error logging...');
try {
await _dio.get('/invalid-endpoint-path');
} catch (e) {
_addLog('Dio: Expected error caught - $e');
}
}
Future<void> _dioTimeout() async {
_addLog('Dio: Sending GET request to non-routable IP (timeout)...');
try {
final dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 2),
));
dio.interceptors.add(NetLoggerInterceptor(
appName: 'Dio App Example',
serverUrl: serverUrl,
enableConsoleLogs: true,
));
await dio.get('http://10.255.255.1');
} catch (e) {
_addLog('Dio: Expected timeout caught - $e');
}
}
// --- http requests ---
Future<void> _httpGet() async {
_addLog('http: Sending GET request to /todos/1...');
try {
final response = await _httpClient.get(
Uri.parse('https://jsonplaceholder.typicode.com/todos/1'),
);
final data = jsonDecode(response.body);
_addLog('http: Received ${response.statusCode} - Title: ${data['title']}');
} catch (e) {
_addLog('http: Error occurred - $e');
}
}
Future<void> _httpPost() async {
_addLog('http: Sending POST request to /todos...');
try {
final response = await _httpClient.post(
Uri.parse('https://jsonplaceholder.typicode.com/todos'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'title': 'Test todo via http package',
'completed': false,
'userId': 99,
}),
);
final data = jsonDecode(response.body);
_addLog('http: Received ${response.statusCode} - ID: ${data['id']}');
} catch (e) {
_addLog('http: Error occurred - $e');
}
}
Future<void> _httpError() async {
_addLog('http: Sending GET request to invalid domain to verify connection failure logging...');
try {
await _httpClient.get(Uri.parse('https://thisdomainwillnotresolve999.com/api'));
} catch (e) {
_addLog('http: Expected error caught - $e');
}
}
Future<void> _httpTimeout() async {
_addLog('http: Sending GET request with 2s timeout...');
try {
final client = NetLoggerHttpClient(
http.Client(),
appName: 'http App Example',
serverUrl: serverUrl,
timeout: const Duration(seconds: 2),
enableConsoleLogs: true,
);
await client.get(Uri.parse('http://10.255.255.1'));
} catch (e) {
_addLog('http: Expected timeout caught - $e');
}
}
@override
void dispose() {
_httpClient.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Net Logger Example App'),
backgroundColor: const Color(0xFF0F172A),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Instructions Card
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF334155)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.info_outline, color: Color(0xFF6366F1)),
SizedBox(width: 8),
Text(
'Viewer Setup Instructions',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
const SizedBox(height: 8),
Text(
'1. Start the visualizer app `net_logger_viewer` first.\n'
'2. Ensure it is listening on $serverUrl.\n'
'3. Tap any of the buttons below to fire client requests.\n'
'4. Watch logs appear live on the net_logger_viewer dashboard!',
style: TextStyle(color: Colors.grey[300], fontSize: 13, height: 1.5),
),
],
),
),
const SizedBox(height: 16),
// Columns of buttons
Expanded(
child: Row(
children: [
// Dio Column
Expanded(
child: Card(
color: const Color(0xFF0F172A),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
const Text(
'Dio Integration',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
const Divider(color: Color(0xFF334155)),
const SizedBox(height: 8),
_buildRequestButton('GET Request', _dioGet, const Color(0xFF6366F1)),
_buildRequestButton('POST JSON', _dioPost, const Color(0xFF6366F1)),
_buildRequestButton('POST Multipart', _dioMultipart, const Color(0xFF6366F1)),
_buildRequestButton('Trigger 404 Error', _dioError, const Color(0xFFEF4444)),
_buildRequestButton('Trigger Timeout', _dioTimeout, const Color(0xFFF97316)),
],
),
),
),
),
const SizedBox(width: 12),
// HTTP Column
Expanded(
child: Card(
color: const Color(0xFF0F172A),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
const Text(
'HTTP Package',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
const Divider(color: Color(0xFF334155)),
const SizedBox(height: 8),
_buildRequestButton('GET Request', _httpGet, const Color(0xFF10B981)),
_buildRequestButton('POST JSON', _httpPost, const Color(0xFF10B981)),
_buildRequestButton('Trigger Connection Err', _httpError, const Color(0xFFEF4444)),
_buildRequestButton('Trigger Timeout', _httpTimeout, const Color(0xFFF97316)),
],
),
),
),
),
],
),
),
const SizedBox(height: 16),
// Console output
Container(
height: 200,
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withAlpha(128),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Local App Logs Console',
style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold, fontSize: 12),
),
GestureDetector(
onTap: () => setState(() => _localConsoleLogs.clear()),
child: const Text(
'Clear Console',
style: TextStyle(color: Color(0xFF6366F1), fontWeight: FontWeight.bold, fontSize: 12),
),
),
],
),
const Divider(color: Color(0xFF1E293B)),
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _localConsoleLogs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Text(
_localConsoleLogs[index],
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: Color(0xFFF1F5F9),
),
),
);
},
),
),
],
),
),
],
),
),
);
}
Widget _buildRequestButton(String label, VoidCallback onPressed, Color color) {
return Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: color.withAlpha(38),
foregroundColor: color,
side: BorderSide(color: color, width: 1.5),
minimumSize: const Size.fromHeight(40),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
onPressed: onPressed,
child: Text(label, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13)),
),
);
}
}