<!--
@id inspector-preview
@title Flutter Network Inspector Preview
@description Interactive live preview of the Interceptly Flutter Network Inspector DevTool
@order 5
@tags flutter, devtools, network, showcase
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interceptly — Flutter Network Inspector</title>
<style>
:root {
--bg: #1B1E2B;
--card-bg: #292D3E;
--border: #32374D;
--accent: #80CBC4;
--accent-hover: #4db6ac;
--text: #babed8;
--text-white: #ffffff;
--text-muted: #757CA1;
--status-200: #C3E88D;
--status-400: #FFCB6B;
--status-500: #FF5370;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "JetBrains Mono", monospace;
background: var(--bg);
color: var(--text);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
header {
background: var(--card-bg);
border-bottom: 1px solid var(--border);
padding: 10px 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
}
.badge {
background: rgba(128, 203, 196, 0.15);
color: var(--accent);
border: 1px solid var(--accent);
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
}
.controls {
display: flex;
gap: 8px;
}
button {
background: var(--card-bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-family: inherit;
transition: all 0.2s;
}
button:hover {
background: var(--border);
color: var(--text-white);
}
button.primary {
background: var(--accent);
color: #1B1E2B;
border: none;
font-weight: 600;
}
button.primary:hover {
background: var(--accent-hover);
}
.main-container {
display: flex;
flex: 1;
overflow: hidden;
}
.requests-list {
width: 48%;
border-right: 1px solid var(--border);
overflow-y: auto;
}
.request-item {
padding: 10px 14px;
border-bottom: 1px solid var(--border);
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
transition: background 0.15s;
}
.request-item:hover {
background: rgba(255, 255, 255, 0.03);
}
.request-item.active {
background: rgba(128, 203, 196, 0.1);
border-left: 3px solid var(--accent);
}
.method-badge {
font-weight: 700;
font-size: 11px;
width: 45px;
display: inline-block;
}
.method-GET { color: #82AAFF; }
.method-POST { color: #C3E88D; }
.method-PUT { color: #FFCB6B; }
.method-DELETE { color: #FF5370; }
.url-path {
font-size: 12px;
color: var(--text-white);
flex: 1;
margin: 0 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.status-code {
font-size: 11px;
font-weight: 600;
padding: 2px 6px;
border-radius: 3px;
}
.status-200 { color: var(--status-200); background: rgba(195, 232, 141, 0.1); }
.status-400 { color: var(--status-400); background: rgba(255, 203, 107, 0.1); }
.status-500 { color: var(--status-500); background: rgba(255, 83, 112, 0.1); }
.details-panel {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
background: #202434;
}
.tabs {
display: flex;
background: var(--card-bg);
border-bottom: 1px solid var(--border);
}
.tab {
padding: 8px 16px;
font-size: 12px;
cursor: pointer;
border-right: 1px solid var(--border);
color: var(--text-muted);
}
.tab.active {
background: #202434;
color: var(--accent);
font-weight: 600;
border-bottom: 2px solid var(--accent);
}
.detail-body {
padding: 16px;
overflow-y: auto;
flex: 1;
font-size: 12px;
line-height: 1.6;
}
pre {
background: #1B1E2B;
padding: 12px;
border-radius: 6px;
border: 1px solid var(--border);
overflow-x: auto;
color: #89DDFF;
}
</style>
</head>
<body>
<header>
<div class="brand">
<span style="font-weight: bold; color: var(--text-white);">Interceptly</span>
<span class="badge">Flutter DevTool</span>
</div>
<div class="controls">
<button onclick="simulateRequest()">+ Trigger Request</button>
<button onclick="clearRequests()">Clear</button>
<button class="primary" onclick="alert('Interceptly Inspector connected to active Flutter Engine')">Connected: 127.0.0.1:8080</button>
</div>
</header>
<div class="main-container">
<div class="requests-list" id="requestsList"></div>
<div class="details-panel">
<div class="tabs">
<div class="tab active" onclick="switchTab('response')">Response Body</div>
<div class="tab" onclick="switchTab('headers')">Headers</div>
<div class="tab" onclick="switchTab('timing')">Timing</div>
</div>
<div class="detail-body" id="detailBody">
<p style="color: var(--text-muted);">Select a request on the left to view payload details.</p>
</div>
</div>
</div>
<script>
const mockRequests = [
{
id: 1,
method: "GET",
url: "/api/v2/portfolio/profile",
status: 200,
time: "14ms",
size: "1.2 KB",
response: {
name: "Tuan Do",
role: "Mobile & Frontend Lead Engineer",
focus: ["Flutter", "Dart", "Offline-First", "Next.js"],
openSource: "https://pub.dev/packages/interceptly"
}
},
{
id: 2,
method: "POST",
url: "/api/v1/auth/exchange-token",
status: 200,
time: "65ms",
size: "840 B",
response: {
tokenType: "Bearer",
expiresIn: 3600,
authenticated: true
}
},
{
id: 3,
method: "GET",
url: "/api/v2/analytics/live-telemetry",
status: 400,
time: "32ms",
size: "310 B",
response: {
error: "BadRequest",
message: "Missing device_fingerprint query param"
}
}
];
let selectedReq = mockRequests[0];
let currentTab = "response";
function renderList() {
const container = document.getElementById("requestsList");
container.innerHTML = mockRequests.map(req => `
<div class="request-item ${selectedReq && selectedReq.id === req.id ? 'active' : ''}" onclick="selectRequest(${req.id})">
<span class="method-badge method-${req.method}">${req.method}</span>
<span class="url-path">${req.url}</span>
<span class="status-code status-${req.status}">${req.status}</span>
</div>
`).join("");
renderDetail();
}
function selectRequest(id) {
selectedReq = mockRequests.find(r => r.id === id);
renderList();
}
function switchTab(tab) {
currentTab = tab;
document.querySelectorAll(".tab").forEach(t => t.classList.remove("active"));
event.target.classList.add("active");
renderDetail();
}
function renderDetail() {
const body = document.getElementById("detailBody");
if (!selectedReq) {
body.innerHTML = '<p style="color: var(--text-muted);">No request selected.</p>';
return;
}
if (currentTab === "response") {
body.innerHTML = `
<h4 style="margin-bottom: 8px; color: var(--text-white);">HTTP ${selectedReq.status} — ${selectedReq.time} (${selectedReq.size})</h4>
<pre>${JSON.stringify(selectedReq.response, null, 2)}</pre>
`;
} else if (currentTab === "headers") {
body.innerHTML = `
<h4 style="margin-bottom: 8px; color: var(--text-white);">Request & Response Headers</h4>
<pre>content-type: application/json; charset=utf-8\nx-request-id: req-${selectedReq.id}-flutter\ncache-control: no-cache</pre>
`;
} else {
body.innerHTML = `
<h4 style="margin-bottom: 8px; color: var(--text-white);">Network Waterfall</h4>
<p>DNS Lookup: 2ms</p>
<p>TLS Handshake: 8ms</p>
<p>Server Processing: ${selectedReq.time}</p>
`;
}
}
function simulateRequest() {
const newId = mockRequests.length + 1;
mockRequests.unshift({
id: newId,
method: "POST",
url: `/api/v2/orders/checkout-${newId}`,
status: 200,
time: `${Math.floor(Math.random() * 80) + 15}ms`,
size: "620 B",
response: { success: true, orderId: `ord-${Date.now()}` }
});
selectedReq = mockRequests[0];
renderList();
}
function clearRequests() {
mockRequests.length = 0;
selectedReq = null;
renderList();
}
renderList();
</script>
</body>
</html>