overflow_guard 0.2.1
overflow_guard: ^0.2.1 copied to clipboard
Catch RenderFlex overflow across many device sizes and text scales at once, in widget tests / CI or live at runtime. Reports exactly where each screen overflows.
example/lib/main.dart
// Runnable example for overflow_guard's LIVE runtime detection.
//
// Run with: cd example && flutter run
//
// The `OverflowRecorder` intercepts Flutter's overflow errors as they happen,
// so instead of only red console output you can surface them in the UI. Drag
// the width slider until the toolbar overflows and watch the banner update.
//
// (For the test-time / CI API -- `checkLayout` across many device sizes -- see
// the snippet in example/README.md and the package README.)
import 'package:flutter/material.dart';
import 'package:overflow_guard/overflow_guard.dart';
/// App-wide recorder. `alsoReport: true` keeps overflow errors printing to the
/// console as well, so nothing is silently hidden.
final OverflowRecorder recorder = OverflowRecorder();
void main() {
recorder.start(alsoReport: true);
runApp(const ExampleApp());
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'overflow_guard example',
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _width = 320;
List<OverflowIssue> _issues = const [];
/// Clear the recorder, lay the toolbar out fresh (new key), then read back
/// whatever overflow the package captured on the next frame.
void _recheck() {
recorder.clear();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
setState(() => _issues = recorder.issues.toList());
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('overflow_guard example')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Frame width: ${_width.round()} px',
style: Theme.of(context).textTheme.titleMedium),
Slider(
min: 240,
max: 560,
value: _width,
onChanged: (v) {
setState(() => _width = v);
_recheck();
},
),
const SizedBox(height: 8),
Center(
child: Container(
width: _width,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: KeyedSubtree(
key: ValueKey(_width.round()),
child: const _FragileToolbar(),
),
),
),
const SizedBox(height: 24),
if (_issues.isEmpty)
const Text('No overflow captured at this width.')
else
for (final issue in _issues)
Text('Overflowed by ${issue.summary}',
style: const TextStyle(color: Colors.red)),
],
),
),
);
}
}
/// A Row with no flexible children, so it overflows on narrow widths.
class _FragileToolbar extends StatelessWidget {
const _FragileToolbar();
@override
Widget build(BuildContext context) {
return Container(
color: Colors.indigo.shade50,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: const Row(
children: [
Icon(Icons.dashboard),
SizedBox(width: 8),
Text('Quarterly Sales Dashboard'),
SizedBox(width: 12),
Chip(label: Text('Export')),
SizedBox(width: 8),
Chip(label: Text('Share')),
],
),
);
}
}