expandable_column 0.0.1
expandable_column: ^0.0.1 copied to clipboard
An expandable Flutter column widget that supports scrolling while allowing Expanded and Spacer widgets. Perfect for responsive layouts that need to fill available space.
Expandable Column #
A scrollable column widget that supports Expanded and Spacer widgets. Combines the flexibility of Column with the scrolling capability of ScrollView for dynamic, space-aware layouts.
Features #
β¨ Use Expanded and Spacer - Unlike ListView, fully supports flex widgets
π± Automatic Scrolling - Scrolls when content overflows the viewport
π― Space Distribution - Children can expand to fill available space
β‘ Performance Optimized - Built on Flutter's efficient sliver system
π¨ Highly Customizable - All standard Column and ScrollView properties supported
Why Use This Package? #
The Problem #
In Flutter, you often face a dilemma:
ColumnsupportsExpandedandSpacer, but doesn't scroll when content overflowsListViewscrolls perfectly, but doesn't supportExpandedorSpacerwidgetsSingleChildScrollViewwithColumnscrolls, butExpandedwidgets throw errors
The Solution #
ExpandableColumn gives you the best of both worlds:
ExpandableColumn(
children: [
Text('Header'),
Expanded(
child: Container(
color: Colors.blue,
child: Center(child: Text('This fills available space')),
),
),
Spacer(),
Text('Footer'),
],
)
Installation #
Add this to your pubspec.yaml:
dependencies:
expandable_column: ^0.0.1
Then run:
flutter pub get
Usage #
Basic Example #
import 'package:flutter/material.dart';
import 'package:flutter_expandable_column/flutter_expandable_column.dart';
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Expandable Column Example')),
body: ExpandableColumn(
children: [
Container(
height: 100,
color: Colors.red,
child: Center(child: Text('Fixed Height Header')),
),
Expanded(
child: Container(
color: Colors.blue,
child: Center(
child: Text('This expands to fill available space'),
),
),
),
Container(
height: 100,
color: Colors.green,
child: Center(child: Text('Fixed Height Footer')),
),
],
),
);
}
}
Form with Dynamic Content #
ExpandableColumn(
padding: EdgeInsets.all(16),
children: [
Text(
'User Registration',
style: Theme.of(context).textTheme.headlineMedium,
),
SizedBox(height: 20),
TextField(decoration: InputDecoration(labelText: 'Name')),
TextField(decoration: InputDecoration(labelText: 'Email')),
TextField(decoration: InputDecoration(labelText: 'Password')),
Spacer(), // Pushes button to bottom
ElevatedButton(
onPressed: () {},
child: Text('Submit'),
),
],
)
Multiple Expanded Widgets #
ExpandableColumn(
children: [
Text('Dashboard'),
Expanded(
flex: 2,
child: Container(
color: Colors.amber,
child: Center(child: Text('Takes 2/3 of space')),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.purple,
child: Center(child: Text('Takes 1/3 of space')),
),
),
Padding(
padding: EdgeInsets.all(16),
child: Text('Footer Text'),
),
],
)
With Custom Scroll Physics #
ExpandableColumn(
physics: BouncingScrollPhysics(), // iOS-style bouncing
children: [
// Your widgets here
],
)
Centered Content #
ExpandableColumn(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.check_circle, size: 100, color: Colors.green),
SizedBox(height: 20),
Text('Success!', style: TextStyle(fontSize: 24)),
SizedBox(height: 10),
Text('Your action was completed successfully.'),
],
)
API Reference #
ExpandableColumn Properties #
| Property | Type | Default | Description |
|---|---|---|---|
children |
List<Widget> |
required | The widgets to display vertically |
mainAxisAlignment |
MainAxisAlignment |
start |
How children are aligned vertically |
mainAxisSize |
MainAxisSize |
max |
How much vertical space to occupy |
crossAxisAlignment |
CrossAxisAlignment |
center |
How children are aligned horizontally |
textDirection |
TextDirection? |
null |
Order for horizontal layout |
verticalDirection |
VerticalDirection |
down |
Order for vertical layout |
textBaseline |
TextBaseline? |
null |
Baseline for alignment |
physics |
ScrollPhysics? |
null |
Scroll physics behavior |
primary |
bool? |
null |
Whether this is the primary scroll view |
reverse |
bool |
false |
Whether to reverse scroll direction |
clipBehavior |
Clip |
hardEdge |
How to clip content |
Common Use Cases #
1. Login Screen with Logo #
ExpandableColumn(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(flex: 2),
Image.asset('assets/logo.png', height: 120),
SizedBox(height: 40),
TextField(decoration: InputDecoration(labelText: 'Username')),
SizedBox(height: 16),
TextField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
),
SizedBox(height: 24),
ElevatedButton(
onPressed: () {},
child: Text('Login'),
),
Spacer(flex: 3),
],
)
2. Chat-like Interface #
ExpandableColumn(
children: [
Expanded(
child: ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) => MessageBubble(messages[index]),
),
),
Divider(height: 1),
MessageInput(),
],
)
3. Settings Page #
ExpandableColumn(
children: [
ListTile(
title: Text('Account'),
leading: Icon(Icons.person),
trailing: Icon(Icons.chevron_right),
),
ListTile(
title: Text('Notifications'),
leading: Icon(Icons.notifications),
trailing: Switch(value: true, onChanged: (_) {}),
),
ListTile(
title: Text('Privacy'),
leading: Icon(Icons.lock),
trailing: Icon(Icons.chevron_right),
),
Spacer(),
Padding(
padding: EdgeInsets.all(16),
child: Text(
'Version 1.0.0',
style: TextStyle(color: Colors.grey),
textAlign: TextAlign.center,
),
),
],
)
Comparison with Alternatives #
| Feature | ExpandableColumn | Column + SingleChildScrollView | ListView |
|---|---|---|---|
Supports Expanded |
β Yes | β No (throws error) | β No |
Supports Spacer |
β Yes | β No (throws error) | β No |
| Scrollable | β Yes | β Yes | β Yes |
| Fills available space | β Yes | β οΈ Limited | β No |
| Performance | β Optimized | β Good | β Optimized |
Migration Guide #
From Column + SingleChildScrollView #
Before:
SingleChildScrollView(
child: Column(
children: [
Widget1(),
// Can't use Expanded here!
Widget2(),
],
),
)
After:
ExpandableColumn(
children: [
Widget1(),
Expanded(child: Widget2()), // Now works!
],
)
From Column #
Before:
Column(
children: [
Widget1(),
Expanded(child: Widget2()),
],
)
After:
ExpandableColumn(
children: [
Widget1(),
Expanded(child: Widget2()), // Now scrollable too!
],
)
Performance Tips #
- Use
constconstructors when possible for better performance - Avoid deeply nested
ExpandableColumnwidgets - use one at the root level - For long lists inside
Expanded, useListView.builderinstead ofColumn - Set
primary: falseif not the primary scroll view in your screen
Troubleshooting #
Content not scrolling? #
Make sure your total content height exceeds the viewport. If content fits, there's nothing to scroll.
Expanded throwing errors? #
Ensure you're using ExpandableColumn directly, not wrapping it in additional widgets that constrain height.
Multiple scroll views conflict? #
Set primary: false on nested scroll views:
ExpandableColumn(
primary: false, // Important!
children: [
Expanded(
child: ListView(...),
),
],
)
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Support #
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
Changelog #
See CHANGELOG.md for a list of changes.
Acknowledgments #
- Built with Flutter's powerful sliver system
- Inspired by the common need to combine
Columnflexibility withScrollViewscrolling
Made with β€οΈ for the Flutter community