The scroll is displaying content larger than the screen.
You’ve probably run into this while testing in the previous module. You added a long text to a column, or tested your App on a smaller phone, and suddenly… Boom!
The screen breaks, a yellow and black “danger zone” strip appears, and the console yells at you: RenderFlex overflowed by 350 pixels.
This happens because, by default, layouts like Column or Row do not scroll. If the content doesn’t fit, Flutter doesn’t crop or hide it; it warns you that there’s a layout error.
Today we’ll see the simplest solution: the SingleChildScrollView.
What is SingleChildScrollView
It’s the most basic scroll widget. Its functionality is simple: it takes a single child and, if that child is larger than the screen, it allows you to slide it to see the rest.
Think of it as a window. If the landscape (the content) is larger than the window frame, this widget lets you move the landscape to see it all.
Basic Implementation
Suppose we have a Column with a lot of text that causes an error. The solution is as simple as “wrapping” that column.
Scaffold(
body: SingleChildScrollView(
// Only accepts one child. Usually a Column.
child: Column(
children: [
Container(height: 300, color: Colors.red),
Container(height: 300, color: Colors.green),
Container(height: 300, color: Colors.blue),
// Without scroll, this would error because 900px > screen height
],
),
),
)
Note: SingleChildScrollView doesn’t work well if placed inside a parent that doesn’t have a defined height (like another Column without Expanded). It needs bounds to know when to start scrolling.
Scroll Direction
By default, the scroll is vertical. But what if we want a row of cards to slide sideways?
We can change the scrollDirection property.
SingleChildScrollView(
scrollDirection: Axis.horizontal, // Horizontal scroll
child: Row( // Note: Now the child is usually a Row
children: [
ProductCard(),
ProductCard(),
ProductCard(),
// ...
],
),
)
Scroll Physics
A detail that shows quality in an App is how the scroll “feels”. Flutter tries to mimic the native behavior of each platform, but we can override it.
The physics property controls this:
BouncingScrollPhysics: allows visually overshooting the limit and bouncing back.ClampingScrollPhysics: prevents the position from exceeding the content bounds.
You can set specific physics if the design requires it, although it’s usually best to respect the behavior configured for the platform.
SingleChildScrollView(
physics: BouncingScrollPhysics(), // Bounce on Android and iOS
child: Column(...),
)
Controlling Position with ScrollController
Sometimes we need to control the scroll programmatically:
- Scroll to the top when a button is pressed.
- Know if the user has reached the end to load more data.
- Animate the position.
For this, we use a ScrollController.
class MyScrollableScreen extends StatefulWidget {
@override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScrollableScreen> {
// 1. Create the controller
final ScrollController _scrollController = ScrollController();
void _scrollToTop() {
// 3. Use the controller to move
_scrollController.animateTo(
0, // Position 0 (the start)
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
controller: _scrollController, // 2. Assign it to the widget
child: Column(children: List.generate(50, (i) => Text("Item $i"))),
),
floatingActionButton: FloatingActionButton(
onPressed: _scrollToTop,
child: Icon(Icons.arrow_upward),
),
);
}
}
Performance
SingleChildScrollView is great for forms, settings screens, or “About” pages where the content is limited.
But it has a major drawback: It renders all its content at once, even if it’s not visible on screen.
If you put a list of 1,000 photos in a SingleChildScrollView, Flutter will try to paint all 1,000 photos in memory at the same time, even if the user is only seeing the first 2.
Consequences:
- The App will take a long time to open that screen.
- Excessive RAM consumption.
- Possible unexpected closure (Crash) on low-end phones.