endpoint_lens 0.2.1
endpoint_lens: ^0.2.1 copied to clipboard
Inspect recent API calls in debug builds, and in internal QA releases when you opt in. Attach Dio once, then long-press anywhere.
example/lib/main.dart
import 'package:dio/dio.dart';
import 'package:endpoint_lens/endpoint_lens.dart';
import 'package:flutter/material.dart';
import 'example_api.dart';
/// The same Dio client the example screens call.
///
/// The bearer value is fake so a debug long press can show that API Lens
/// redacts it.
final Dio exampleDio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com/v1',
headers: const <String, dynamic>{
'Authorization': 'Bearer demo-secret-token',
},
),
);
/// Example client created after [ApiLens.init] attaches [exampleDio].
late ExampleApi exampleApi;
void main() {
ApiLens.init(dio: exampleDio);
exampleApi = ExampleApi(exampleDio);
runApp(const ExampleApp());
}
/// Three screens plus one advanced exact-mapping page.
class ExampleApp extends StatelessWidget {
/// Creates the example app.
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
const seed = Color(0xFF0B6E4F);
return MaterialApp(
title: 'API Lens example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: seed),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: seed,
brightness: Brightness.dark,
),
useMaterial3: true,
),
navigatorObservers: [ApiLensNavigatorObserver()],
initialRoute: 'Home',
routes: {
'Home': (_) => const HomeScreen(),
'Products': (_) => const ProductsScreen(),
'Profile': (_) => const ProfileScreen(),
'Advanced': (_) => const AdvancedScreen(),
},
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
exampleApi.loadHome();
}
@override
Widget build(BuildContext context) {
return _Page(
index: 0,
title: 'Home',
child: const Text('Long press anywhere to inspect recent API activity.'),
);
}
}
class ProductsScreen extends StatefulWidget {
const ProductsScreen({super.key});
@override
State<ProductsScreen> createState() => _ProductsScreenState();
}
class _ProductsScreenState extends State<ProductsScreen> {
late final Future<List<DemoProduct>> _products = exampleApi.loadProducts();
@override
Widget build(BuildContext context) {
return _Page(
index: 1,
title: 'Products',
child: FutureBuilder<List<DemoProduct>>(
future: _products,
builder: (context, snapshot) {
final products = snapshot.data ?? const <DemoProduct>[];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final product in products)
Text('${product.name} · \$${product.price}'),
],
);
},
),
);
}
}
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
late final Future<DemoProfile> _profile = exampleApi.loadProfile();
@override
Widget build(BuildContext context) {
return _Page(
index: 2,
title: 'Profile',
child: FutureBuilder<DemoProfile>(
future: _profile,
builder: (context, snapshot) {
final profile = snapshot.data;
if (profile == null) return const Text('Loading profile');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(profile.name),
Text(profile.email),
for (final post in profile.posts) Text(post.title),
TextButton(
onPressed: () => Navigator.pushNamed(context, 'Advanced'),
child: const Text('Advanced exact mapping'),
),
],
);
},
),
);
}
}
class AdvancedScreen extends StatelessWidget {
const AdvancedScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Advanced')),
body: Center(
child: ApiLensWidget(
feature: 'avatar',
child: Padding(
padding: const EdgeInsets.all(24),
child: FilledButton(
onPressed: exampleApi.loadAvatar,
child: const Text('Load avatar'),
),
),
),
),
);
}
}
class _Page extends StatelessWidget {
const _Page({required this.index, required this.title, required this.child});
final int index;
final String title;
final Widget child;
static const _routes = <String>['Home', 'Products', 'Profile'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Padding(padding: const EdgeInsets.all(24), child: child),
bottomNavigationBar: NavigationBar(
selectedIndex: index,
onDestinationSelected: (next) {
if (next == index) return;
Navigator.pushReplacementNamed(context, _routes[next]);
},
destinations: const [
NavigationDestination(icon: Icon(Icons.home_outlined), label: 'Home'),
NavigationDestination(
icon: Icon(Icons.shopping_bag_outlined),
label: 'Products',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
label: 'Profile',
),
],
),
);
}
}