gap2
A small but powerful Flutter layout primitive for intentional spacing.
gap2 provides axis-aware spacing widgets that work consistently across
Flex layouts, Scrollables, and Slivers, with a clean, predictable API and
native sliver support.
Overview
Spacing is one of the most common layout needs in Flutter, yet it is often
handled inconsistently using SizedBox, Spacer, or ad-hoc solutions.
gap2 solves this by providing dedicated spacing primitives that:
- Take space only along the parent’s main axis
- Work correctly in
RowandColumn - Work inside
Scrollablewidgets likeListView - Provide native sliver spacing without adapters
- Fail loudly and predictably when used incorrectly
Features
Gap— fixed spacing along the parent axisMaxGap— flexible spacing with an upper boundSliverGap— native sliver spacing forCustomScrollView- Axis-aware behavior with no configuration
- Clean error messages for invalid usage
- Shared internal layout model for correctness
Installation
Add gap2 to your pubspec.yaml:
dependencies:
gap2: ^1.0.0
Then run:
flutter pub get
Usage
Gap
Gap inserts a fixed amount of space along the layout’s main axis.
Row(
children: const [
Text('Left'),
Gap(16),
Text('Right'),
],
);
In a vertical layout:
Column(
children: const [
Text('Above'),
Gap(24),
Text('Below'),
],
);
Inside a scrollable:
ListView(
children: const [
Text('Item A'),
Gap(16),
Text('Item B'),
],
);
MaxGap
MaxGap behaves like a Spacer with a maximum extent.
Row(
children: const [
Text('A'),
MaxGap(40),
Text('B'),
],
);
The gap will expand if space is available, but never exceed the given value.
SliverGap
For sliver layouts, use SliverGap inside a CustomScrollView.
CustomScrollView(
slivers: const [
SliverToBoxAdapter(child: Text('Item A')),
SliverGap(24),
SliverToBoxAdapter(child: Text('Item B')),
],
);
SliverGap contributes directly to sliver layout and scroll extent without
wrapping box widgets in adapters.
How axis resolution works
gap2 determines its axis automatically:
- Inside a
Column→ vertical spacing - Inside a
Row→ horizontal spacing - Inside a
Scrollable→ based on scroll direction
If Gap is used outside of these contexts, it throws a clear runtime error.
This prevents silent layout bugs and makes intent explicit.
📖 Example
A minimal example using all gap types:
import 'package:flutter/material.dart';
import 'package:gap2/gap2.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: const [
Text('Top'),
Gap(20),
Text('Middle'),
MaxGap(30),
Text('Bottom'),
],
),
),
);
}
}
🤔 Why gap2?
Compared to ad-hoc spacing solutions, gap2:
- Avoids mixing layout intent with visual widgets
- Works identically in box and sliver worlds
- Makes spacing explicit and self-documenting
- Scales from simple UIs to complex scroll views
- Is actively maintained
It is designed as a layout primitive, not just a convenience widget.
📜 License
MIT License.
💬 Contributing
Issues, suggestions, and pull requests are welcome.