dummy_api_overlay 0.1.0
dummy_api_overlay: ^0.1.0 copied to clipboard
A Flutter developer tool that adds a draggable overlay button to your app, letting you mock REST API responses without touching production code. Supports both the http and dio packages with shared_pre [...]
example/lib/main.dart
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:dummy_api_overlay/dummy_api_overlay.dart';
import 'package:flutter/material.dart';
final _navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dummy API Overlay — Example',
debugShowCheckedModeBanner: false,
navigatorKey: _navigatorKey,
theme: ThemeData(
colorSchemeSeed: const Color(0xFF6C63FF),
useMaterial3: true,
),
builder: (context, child) => DummyApiOverlay(
navigatorKey: _navigatorKey,
child: child!,
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _httpClient = DummyHttpClient();
final _dio = Dio()..interceptors.add(DummyDioInterceptor());
String _httpResult = '—';
String _dioResult = '—';
bool _httpLoading = false;
bool _dioLoading = false;
Future<void> _fetchHttp() async {
setState(() => _httpLoading = true);
try {
final res = await _httpClient
.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
final decoded = jsonDecode(res.body);
setState(() => _httpResult =
const JsonEncoder.withIndent(' ').convert(decoded));
} catch (e) {
setState(() => _httpResult = 'Error: $e');
} finally {
setState(() => _httpLoading = false);
}
}
Future<void> _fetchDio() async {
setState(() => _dioLoading = true);
try {
final res =
await _dio.get('https://jsonplaceholder.typicode.com/posts/1');
setState(() => _dioResult =
const JsonEncoder.withIndent(' ').convert(res.data));
} catch (e) {
setState(() => _dioResult = 'Error: $e');
} finally {
setState(() => _dioLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dummy API Overlay'),
centerTitle: false,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text(
'Tap the floating API button to add mock rules.\n'
'Then hit the fetch buttons — if a matching rule is enabled, '
'the dummy response will be returned instead of the real API.',
style: TextStyle(fontSize: 14, color: Colors.black54),
),
const SizedBox(height: 24),
_RequestCard(
title: 'http package',
subtitle: 'GET /todos/1',
result: _httpResult,
loading: _httpLoading,
onFetch: _fetchHttp,
),
const SizedBox(height: 16),
_RequestCard(
title: 'Dio package',
subtitle: 'GET /posts/1',
result: _dioResult,
loading: _dioLoading,
onFetch: _fetchDio,
),
],
),
);
}
}
class _RequestCard extends StatelessWidget {
const _RequestCard({
required this.title,
required this.subtitle,
required this.result,
required this.loading,
required this.onFetch,
});
final String title;
final String subtitle;
final String result;
final bool loading;
final VoidCallback onFetch;
@override
Widget build(BuildContext context) {
return Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: Colors.grey.shade200),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
fontWeight: FontWeight.w700, fontSize: 15)),
Text(subtitle,
style: const TextStyle(
color: Colors.black54, fontSize: 12)),
],
),
),
FilledButton(
onPressed: loading ? null : onFetch,
child: loading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2, color: Colors.white),
)
: const Text('Fetch'),
),
],
),
const SizedBox(height: 12),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade200),
),
child: Text(
result,
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: Colors.black87,
),
),
),
],
),
),
);
}
}