flutter_logan_pro 1.0.4
flutter_logan_pro: ^1.0.4 copied to clipboard
A high-performance, robust log plugin for Flutter based on Meituan Logan. Fixed truncation and decryption issues.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_logan_pro/flutter_logan_pro.dart';
import 'config.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// Used to show SnackBars without needing a context *below* the MaterialApp
// (State.context sits above it, so ScaffoldMessenger.of(context) would fail).
final GlobalKey<ScaffoldMessengerState> _messengerKey =
GlobalKey<ScaffoldMessengerState>();
bool _initialized = false;
StreamSubscription<LoganSendResult>? _sendSub;
@override
void initState() {
super.initState();
// Observe every completed upload, regardless of who triggered it.
_sendSub = FlutterLoganPro.onLoganSend.listen((result) {
_showSnackBar('onLoganSend: status=${result.statusCode}');
});
}
@override
void dispose() {
_sendSub?.cancel();
super.dispose();
}
Future<void> _initLogan() async {
try {
await FlutterLoganPro.init(
secretKey: LoganConfig.secretKey,
secretIV: LoganConfig.secretIV,
maxFileLen: 1024 * 1024 * 10, // 10MB per day, in bytes
isDebug: true,
maxReversedDate: 7,
// Android only: stop writing when free space drops below 50MB.
// Ignored on iOS (hardcoded 5MB floor).
minSDCardBytes: 50 * 1024 * 1024,
);
setState(() => _initialized = true);
_showSnackBar('Logan init success');
} on ArgumentError catch (e) {
_showSnackBar('Logan init rejected: ${e.message}');
} on PlatformException catch (e) {
_showSnackBar('Logan init failed: ${e.message}');
}
}
Future<void> _addLog() async {
try {
await FlutterLoganPro.log(
'This is a test log from flutter_logan_pro at ${DateTime.now()}');
_showSnackBar('Log added');
} on PlatformException catch (e) {
_showSnackBar('Add log failed: ${e.message}');
}
}
Future<void> _flush() async {
await FlutterLoganPro.flush();
_showSnackBar('Flushed to disk');
}
Future<void> _sendLog() async {
try {
final res = await FlutterLoganPro.send(
url: LoganConfig.uploadUrl,
appId: '123131',
);
_showSnackBar('Send status: ${res.statusCode}, ok=${res.isSuccess()}');
} on StateError catch (e) {
_showSnackBar('Send already in progress: $e');
} catch (e) {
_showSnackBar('Send failed: $e');
}
}
Future<void> _showFilesInfo() async {
final files = await FlutterLoganPro.getAllFilesInfo();
_showSnackBar('Files: ${files ?? '{}'}');
}
/// Resolve today's log file path — the file you'd attach to a bug report.
Future<void> _showUploadPath() async {
final today = await FlutterLoganPro.getTodaysDate();
final path = await FlutterLoganPro.getUploadPath(today);
if (path == null) {
_showSnackBar('No log file for $today yet — add a log first.');
} else {
// In a real app you'd hand `path` to share_plus / an email composer.
_showSnackBar('Log file: $path');
}
}
Future<void> _clearLogs() async {
await FlutterLoganPro.clearAllLogs();
_showSnackBar('All logs cleared');
}
void _showSnackBar(String message) {
_messengerKey.currentState
?..clearSnackBars()
..showSnackBar(SnackBar(content: Text(message)));
}
@override
Widget build(BuildContext context) {
// Everything except Init is disabled until init() succeeds, so the demo
// can't accidentally call the API before it's ready.
return MaterialApp(
scaffoldMessengerKey: _messengerKey,
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Logan Pro'),
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _initialized ? null : _initLogan,
child: Text(_initialized ? 'Initialized ✓' : 'Init Logan'),
),
const Divider(height: 32),
_buttons([
('Add Log', _addLog),
('Flush', _flush),
('Send Log', _sendLog),
('All Files Info', _showFilesInfo),
('Get Upload Path', _showUploadPath),
('Clear All Logs', _clearLogs),
]),
],
),
),
),
),
);
}
Widget _buttons(List<(String, Future<void> Function())> items) {
return Column(
children: [
for (final (label, action) in items) ...[
ElevatedButton(
onPressed: _initialized ? action : null,
child: Text(label),
),
const SizedBox(height: 12),
],
],
);
}
}