Flutter: Detailed Usage of Center Widget
Last updated:
7 min read

Center puts a single child in the middle of the space it is given. Technically it is an Align with alignment locked to Alignment.center, and it follows exactly the same sizing rules. A loading spinner, an empty-list message, a login form, the body of a dialog: whenever you think "center this", Center is the widget you reach for first. It looks trivial, yet the same three questions keep coming back — why doesn't it center vertically inside a Column, why did it swallow the whole screen, and how do I center something on a scrollable page. This post answers all three.
Live Demo: Center Widget Examples
Try the Center widget in different scenarios in the interactive example below:
💡 If the example above doesn't load, click DartPad to run it in a new tab.
Basic Usage
Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
)Center aligns both horizontally and vertically and takes exactly one child. Because the Scaffold body hands it a bounded box, Center grows to fill that body and the spinner lands in the middle of the screen. This is the most common use and it almost never surprises anyone. The surprises start when you put Center inside a Column, a Row, or a scrollable widget.
Why Does Center Fill the Whole Area?
Center sizes itself by the same rule as Align: if the parent gives it a bounded constraint on an axis, it takes the largest size it can on that axis; if the constraint is unbounded, it shrinks to its child. Centering only means something when there is leftover space around the child, so "grow as much as you can" is the sensible default. When you select a Center in the Flutter Inspector and the highlighted box covers the entire parent, that is not a bug.
You can override that behavior with widthFactor and heightFactor:
Center(
widthFactor: 1, // Center's width = child's width
heightFactor: 2, // Center's height = twice the child's height
child: Text('Wrapped tightly'),
)With widthFactor: 1 and heightFactor: 1 the Center hugs its child. The centering effect disappears, but so does the growth — useful when you do not want the Center to inflate a Stack or a parent that sizes itself to its children. A factor like 2 leaves space around the child proportional to the child's own size.
Center Inside a Column
A Column hands its children a bounded width but an unbounded height. That is why a Center inside a Column is full width but only as tall as its child: it centers horizontally only. Vertical centering is the Column's own job:
// Centers horizontally, not vertically
Column(
children: [
Center(child: Text('Horizontally centered only')),
],
)
// Centers all children vertically
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Vertically centered'),
],
)
// Centers the whole Column on both axes
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.inbox, size: 64),
SizedBox(height: 16),
Text('Nothing here yet'),
],
),
)The mainAxisSize: MainAxisSize.min in the third snippet is the critical part: by default a Column stretches to every pixel of height it is offered, which leaves Center no room to center anything. I go through that behavior in detail in the mainAxisSize post. For a single child you can also drop the Center and use crossAxisAlignment: CrossAxisAlignment.center on the Column — the difference is that crossAxisAlignment applies to every child, not just one.
Center Inside a Row
A Row gives unbounded width, so Center shrinks to its child and there is no space left to center within. If you want "center this text" inside a Row, wrap the Center in an Expanded; Expanded hands it a bounded box and the centering starts working:
Row(
children: [
Icon(Icons.arrow_back),
Expanded(
child: Center(child: Text('Title')),
),
Icon(Icons.more_vert),
],
)All the width left between the two icons goes to the Center, and the title sits in the middle of it. Why Expanded behaves this way is covered in the Expanded post.
When to Use It and When Not To
Reach for Center when:
- You want one widget in the middle of a bounded box (a Scaffold body, a Container, a Card, an Expanded).
- You are building a dialog, an empty state, or a loading screen — layouts where there is a single thing in the middle.
- You are centering a child inside a Stack or a SizedBox.
Pick something else when:
- You need any position other than the middle — use
Align. - You are aligning all the children of a Row or Column — use
mainAxisAlignmentandcrossAxisAlignment. - You already have a Container —
Container(alignment: Alignment.center)does the same job without an extra widget in the tree. - You want the lines of a multi-line paragraph centered — use
textAlign: TextAlign.center. Center aligns the text box, not the lines inside it.
Common Mistakes
1. Center does not center vertically inside a SingleChildScrollView
Symptom: a short form sticks to the top of a scrollable page. SingleChildScrollView gives unbounded height, so Center shrinks to its child and has nothing to center against. Fix: either move the Center outside the scrollable (see the scenario below) or give the content at least the screen height:
LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: Center(child: Text('Short content centers, long content scrolls')),
),
),
)2. The Column inside a Center takes up the full height
Symptom: you wrote Center(child: Column(...)) and the content still starts at the top. A Column's mainAxisSize defaults to max, so it eats all the height Center offered it. Add mainAxisSize: MainAxisSize.min, or let the Column center its own children with mainAxisAlignment: MainAxisAlignment.center.
3. Long text does not look centered
Symptom: the text block sits in the middle but the lines are flush left. Center centers the box of the Text widget; once the text wraps, the lines inside that box align left by default. Pass Text('...', textAlign: TextAlign.center).
4. Center inflates the Stack around it
Symptom: you wanted a small centered overlay on top of an icon and the Stack spread across the whole screen. Center grows inside a bounded box, and a Stack sizes itself to its largest non-positioned child, so the Stack grows too. Either give the Stack a size with a SizedBox, or add widthFactor: 1, heightFactor: 1 to the Center. The same problem and the Positioned alternative are discussed in the Align post, and the Stack's sizing rules in the Stack and Positioned post.
Mini Scenario: A Centered, Scrollable Login Screen
Picture a login screen: on a large display the form should sit in the middle and never grow wider than 400 pixels; on a small one the content should scroll when the keyboard comes up.
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const FlutterLogo(size: 72),
const SizedBox(height: 32),
const TextField(decoration: InputDecoration(labelText: 'Email')),
const SizedBox(height: 16),
const TextField(
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
),
const SizedBox(height: 24),
FilledButton(onPressed: () {}, child: const Text('Sign in')),
],
),
),
),
),
),
);
}
}The order is deliberate: Center on the outside, SingleChildScrollView on the inside. Center receives the bounded box from the Scaffold and grows into it; the scroll view sizes itself to its content, and Center centers that. When the content is taller than the screen, the scroll view takes the full height and simply scrolls — nothing looks off-center, because there is no leftover space to distribute. When the keyboard appears the Scaffold shrinks the body and the same mechanism kicks in again. ConstrainedBox(maxWidth: 400) stops the form from stretching edge to edge on a tablet, and mainAxisSize: MainAxisSize.min keeps the Column as tall as its content. I put the padding on SingleChildScrollView.padding on purpose so the margins scroll with the content; the details are in the Padding post.
Frequently Asked Questions
What is the difference between Center and Align?
There isn't one: Center is an Align with alignment: Alignment.center baked in, and it exposes the same widthFactor, heightFactor and sizing rules. If you are only centering, Center reads better; if you need any other position, use Align.
Why is Center bigger than its child?
Because when the parent gives a bounded constraint, Center takes all of it — centering only makes sense if there is space around the child. If you want it to hug its child, set widthFactor: 1 and heightFactor: 1.
Why doesn't Center align vertically inside a Column?
A Column gives unbounded height, so Center finds no height to center within and shrinks to its child. Use mainAxisAlignment: MainAxisAlignment.center on the Column, or wrap the Column in a Center and set mainAxisSize: MainAxisSize.min.
Should I use Center or mainAxisAlignment?
Use Center to put one child in the middle on both axes. Use mainAxisAlignment to distribute several children of a Row or Column along the main axis (center, spaceBetween, spaceEvenly). They combine happily: Center positions the outer box, mainAxisAlignment arranges what is inside it.
Related Posts
Flutter: Fine Alignment Operations with Align Widget
Precise placement with Align: the Alignment(x, y) coordinate system, FractionalOffset, widthFactor and heightFactor, Align vs Positioned in a Stack.
Flutter: ListView Widget Usage
A practical Flutter ListView guide: lazy lists with builder, separated, itemExtent, the real cost of shrinkWrap and fixing the unbounded height error.
Flutter: GridView Widget Usage
A practical Flutter GridView guide: count, extent and builder, the two grid delegates, the childAspectRatio overflow error and the real cost of shrinkWrap.