flutter_widget_inspector 0.1.0
flutter_widget_inspector: ^0.1.0 copied to clipboard
A runtime Flutter widget inspector — long-press to activate, tap any widget to see its render type, size, position, constraints, padding, text style, and decoration. Works without modifying your widget tree.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_inspector/flutter_widget_inspector.dart';
void main() => runApp(const InspectorExampleApp());
/// Root application for the flutter_widget_inspector example.
///
/// [WidgetInspectorWrapper] is placed around the home page so that any widget
/// on that page can be inspected at runtime.
///
/// **How to use:**
/// 1. Run on a device or simulator — `flutter run`
/// 2. Long-press anywhere to activate inspect mode (blue badge appears)
/// 3. Tap any widget to see its layout info
/// 4. Drag the info panel to reposition it
/// 5. Tap ✕ to deselect; long-press again to exit inspect mode
class InspectorExampleApp extends StatelessWidget {
const InspectorExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Widget Inspector Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)), useMaterial3: true),
home: const WidgetInspectorWrapper(
// kDebugMode ensures zero overhead in release builds.
enabled: kDebugMode,
child: _DemoPage(),
),
);
}
}
// ---------------------------------------------------------------------------
// _DemoPage
// ---------------------------------------------------------------------------
class _DemoPage extends StatefulWidget {
const _DemoPage();
@override
State<_DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<_DemoPage> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F7),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: const Text('Widget Inspector Demo', style: TextStyle(fontWeight: FontWeight.w600)),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(height: 1, color: Colors.black12),
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ── Instructions ──────────────────────────────────────────────────
const _InstructionBanner(),
const SizedBox(height: 24),
// ── Coloured cards ────────────────────────────────────────────────
const _SectionTitle('Cards & Containers'),
const SizedBox(height: 12),
const Row(
children: [
Expanded(
child: _ColorCard(color: Color(0xFF6750A4), label: 'Purple'),
),
SizedBox(width: 12),
Expanded(
child: _ColorCard(color: Color(0xFF006874), label: 'Teal'),
),
SizedBox(width: 12),
Expanded(
child: _ColorCard(color: Color(0xFFB3261E), label: 'Red'),
),
],
),
const SizedBox(height: 24),
// ── Buttons ───────────────────────────────────────────────────────
const _SectionTitle('Buttons'),
const SizedBox(height: 12),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
ElevatedButton(onPressed: () {}, child: const Text('Elevated')),
OutlinedButton(onPressed: () {}, child: const Text('Outlined')),
TextButton(onPressed: () {}, child: const Text('Text')),
FilledButton(onPressed: () {}, child: const Text('Filled')),
FilledButton.tonal(onPressed: () {}, child: const Text('Tonal')),
],
),
const SizedBox(height: 24),
// ── Counter ───────────────────────────────────────────────────────
const _SectionTitle('Stateful Counter'),
const SizedBox(height: 12),
_CounterCard(
count: _counter,
onIncrement: () => setState(() => _counter++),
onDecrement: () => setState(() => _counter--),
),
const SizedBox(height: 24),
// ── Typography ────────────────────────────────────────────────────
const _SectionTitle('Typography'),
const SizedBox(height: 12),
const Card(
margin: EdgeInsets.zero,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Display Large', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('Headline Medium', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
SizedBox(height: 8),
Text('Body text — long-press anywhere to start inspecting.'),
SizedBox(height: 8),
Text('Caption text', style: TextStyle(fontSize: 11, color: Colors.grey)),
],
),
),
),
const SizedBox(height: 24),
// ── Icons ─────────────────────────────────────────────────────────
const _SectionTitle('Icons & Avatars'),
const SizedBox(height: 12),
Wrap(
spacing: 12,
runSpacing: 12,
children: [
for (final entry in {
Icons.favorite: Colors.red,
Icons.star: Colors.amber,
Icons.bolt: Colors.orange,
Icons.eco: Colors.green,
Icons.water_drop: Colors.blue,
Icons.local_fire_department: Colors.deepOrange,
}.entries)
CircleAvatar(
backgroundColor: entry.value.withValues(alpha: 0.12),
child: Icon(entry.key, color: entry.value),
),
],
),
const SizedBox(height: 24),
// ── Nested containers ─────────────────────────────────────────────
const _SectionTitle('Nested Containers'),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.indigo.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.indigo.shade200),
),
child: Column(
children: [
Container(
height: 40,
color: Colors.indigo.shade100,
alignment: Alignment.center,
child: const Text('Inner box 1', style: TextStyle(fontSize: 12)),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: Container(
height: 40,
color: Colors.indigo.shade200,
alignment: Alignment.center,
child: const Text('2a', style: TextStyle(fontSize: 12)),
),
),
const SizedBox(width: 8),
Expanded(
flex: 2,
child: Container(
height: 40,
color: Colors.indigo.shade300,
alignment: Alignment.center,
child: const Text('2b (flex 2)', style: TextStyle(fontSize: 12)),
),
),
],
),
],
),
),
const SizedBox(height: 80),
],
),
),
);
}
}
// ---------------------------------------------------------------------------
// Small reusable demo components
// ---------------------------------------------------------------------------
class _InstructionBanner extends StatelessWidget {
const _InstructionBanner();
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [Color(0xFF6750A4), Color(0xFF9C27B0)]),
borderRadius: BorderRadius.circular(12),
),
child: const Row(
children: [
Icon(Icons.touch_app, color: Colors.white, size: 28),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🔍 How to use',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13),
),
SizedBox(height: 4),
Text(
'Long-press anywhere to activate inspector mode, '
'then tap any widget.',
style: TextStyle(color: Colors.white70, fontSize: 11),
),
],
),
),
],
),
);
}
}
class _SectionTitle extends StatelessWidget {
final String title;
const _SectionTitle(this.title);
@override
Widget build(BuildContext context) {
return Text(
title,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black54),
);
}
}
class _ColorCard extends StatelessWidget {
final Color color;
final String label;
const _ColorCard({required this.color, required this.label});
@override
Widget build(BuildContext context) {
return Container(
height: 80,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(12)),
alignment: Alignment.center,
child: Text(
label,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600, fontSize: 12),
),
);
}
}
class _CounterCard extends StatelessWidget {
final int count;
final VoidCallback onIncrement;
final VoidCallback onDecrement;
const _CounterCard({required this.count, required this.onIncrement, required this.onDecrement});
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: onDecrement,
icon: const Icon(Icons.remove_circle_outline),
color: Theme.of(context).colorScheme.error,
),
Column(
children: [
Text('$count', style: const TextStyle(fontSize: 36, fontWeight: FontWeight.bold)),
const Text('counter', style: TextStyle(fontSize: 11, color: Colors.grey)),
],
),
IconButton(
onPressed: onIncrement,
icon: const Icon(Icons.add_circle_outline),
color: Theme.of(context).colorScheme.primary,
),
],
),
),
);
}
}