binnacle 0.1.0
binnacle: ^0.1.0 copied to clipboard
Pull-based flight recorder for Flutter apps: sessions, navigation and HTTP captured locally, uploaded in batches on demand.
example/lib/main.dart
import 'package:binnacle/binnacle.dart';
import 'package:flutter/material.dart';
import 'inspector/inspector_overlay.dart';
import 'pokeapi.dart';
import 'screens/pokemon_detail_screen.dart';
import 'screens/pokemon_list_screen.dart';
import 'screens/recording_screen.dart';
/// The recorder this app records into.
final Binnacle recorder = Binnacle.instance;
/// Held so the button floating above the app can open a screen from outside
/// the navigator it pushes onto.
final navigatorKey = GlobalKey<NavigatorState>();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await recorder.init(
const BinnacleConfig(
apiBaseUrl: 'https://pokeapi.co',
environment: 'example',
),
);
runApp(ExampleApp(api: PokeApi(recorder)));
}
class ExampleApp extends StatefulWidget {
const ExampleApp({super.key, required this.api});
final PokeApi api;
@override
State<ExampleApp> createState() => _ExampleAppState();
}
class _ExampleAppState extends State<ExampleApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
/// Leaving the app for long enough ends a session, so the recorder needs to
/// hear about every trip to the background.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
recorder.notifyLifecycle(state);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'binnacle example',
debugShowCheckedModeBanner: false,
navigatorKey: navigatorKey,
builder: (context, child) => InspectorOverlay(
recorder: recorder,
navigatorKey: navigatorKey,
child: child!,
),
theme: ThemeData(
colorSchemeSeed: const Color(0xFFFF5A1F),
useMaterial3: true,
),
navigatorObservers: [recorder.newNavigatorObserver()],
initialRoute: PokemonListScreen.routeName,
routes: {
PokemonListScreen.routeName: (_) =>
PokemonListScreen(api: widget.api, recorder: recorder),
RecordingScreen.routeName: (_) => RecordingScreen(recorder: recorder),
},
// Named so a recording reads as the screens a person visited, rather
// than a list of widget classes.
onGenerateRoute: (settings) {
if (settings.name != PokemonDetailScreen.routeName) return null;
return MaterialPageRoute<void>(
settings: settings,
builder: (_) => PokemonDetailScreen(
api: widget.api,
name: settings.arguments! as String,
),
);
},
);
}
}