VOOZH about

URL: https://blog.logrocket.com/intro-flutter-stack-widget/

โ‡ฑ Intro to the Flutter Stack widget - LogRocket Blog


2021-08-26
1474
#flutter
Lewis Cianci
64323
๐Ÿ‘ Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

If thereโ€™s one thing Flutter is known for, itโ€™s the incredible amount of widgets it comes with. All these widgets help developers get the exact look theyโ€™re after with as little effort as possible.

๐Ÿ‘ Intro Flutter Stack Widget

In this post, weโ€™ll look at a widget that every Flutter developer should be aware of: the Stack widget.

Through effective use of the Stack widget in an application, we can communicate depth to users and create some fairly complex layouts without a lot of work.

๐Ÿš€ Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

What does a Stack widget look like?

Here, we can see a sample of what kind of layout we can achieve with a Stack widget in our Flutter app.

๐Ÿ‘ An Example Of A Layout Achieved With A Stack, Showing Three Photos With The Most Visible Positioned On Top, A Title and Description Above And Below Images
An example of a layout achieved with a Stack (photo from Sarah Dorweller at Unsplash).

For this app, we see an image in the center, and then two other images on either side. The images to the left and right are sized a little bit smaller and are placed behind the image in the middle.

Essentially, these widgets stack on top of each other, giving the user a clear sense of what we want them to focus on.

How does a Stack widget work?

To demonstrate what a Stack widget does, letโ€™s first look at how a Column lays out its children. In this simple example, we have five containers that are laid out with progressively higher widths and heights:

Widget build(BuildContext context) {
 return Scaffold(
 body: Column(
 children: [
 ...List.generate(
 5,
 (index) => Container(
 width: index * 50 + 50,
 height: index * 50 + 50,
 color: Color.fromRGBO(index * 20, index * 20, index * 40, 1.0),
 ),
 ).reversed
 ],
 ),
 );
}

This code results in the following:

๐Ÿ‘ Column Layout With Child Elements Stacked From Top To Bottom

Now, if we replace the Column widget with a Stack widget, it becomes this instead:

๐Ÿ‘ Replacing The Column Widget With The Stack Widget, Makes Children Layer On Top Of Each Other

Instead of the widgets laid out on the vertical axis, they stack on top of each other. This is beneficial when we want our widgets on top of each other, and not top-to-bottom or left-to-right.

We can also see that widgets render from the bottom up. In our example, the biggest widget renders at the bottom of the stack, and the smaller widget renders on top, and so on and so forth.

Child widgets are aligned to the top left by default, and the Stack resizes to fit all the children, meaning it will be as big as our biggest child widget.

Alignment and fit

Sometimes, if we place a smaller widget inside a bigger widget, itโ€™s more aesthetically pleasing to align all the children to the center.

If we want to align our widgets to the center for visual appeal, we can align our child widgets within the Stack to the center. To accomplish this, itโ€™s as easy as setting the alignment property in our Stack to Alignment.center, like so:

 Widget build(BuildContext context) {
 return Scaffold(
 body: Stack(
 alignment: Alignment.center, // Center children in the Stack
 children: [
 ...List.generate(
 5,
 (index) => Container(
 width: index * 50 + 50,
 height: index * 50 + 50,
 color: Color.fromRGBO(index * 20, index * 20, index * 40, 1.0),
 ),
 ).reversed,
 ],
 ),
 );
 }

This centers all the children in the Stack to the relative center, like so:

๐Ÿ‘ Centering Children In Stack Widget, Shows Children Stacked On Top Of Each Other And Centered

Because we havenโ€™t centered the Stack yet, it remained in the top left corner. Instead, we just centered the widgets that are inside the Stack.

We can also use the fit parameter to define whether our stack should expand to fill the parent widget, or whether it should pass through the fit of child objects directly to the children in the Stack.


Over 200k developers use LogRocket to create better digital experiences

๐Ÿ‘ Image
Learn more โ†’

Broadly speaking, these only apply in more advanced layout scenarios, so we should be fine leaving the fit as StackFit.loose, which is the default.

We can also position widgets within the stack itself by using Positioned. If we add a Container with a blue background, place some text in it, and position it at the bottom center, the widget lays out accordingly within the bounds of the Stack.

Our code then becomes this:

 Widget build(BuildContext context) {
 return Scaffold(
 body: Stack(
 alignment: Alignment.center, // Center children in the Stack
 children: [
 ...List.generate(
 5,
 (index) => Container(
 width: index * 50 + 50,
 height: index * 50 + 50,
 color: Color.fromRGBO(index * 20, index * 20, index * 40, 1.0),
 ),
 ).reversed,
 // The second child positions the container at the very bottom
 // of the parent Stack.
 Positioned(
 left: 0,
 right: 0,
 bottom: 0,
 child: Container(
 color: Colors.blue.withOpacity(0.8),
 child: Text(
 "Yay for LogRocket!",
 textAlign: TextAlign.center,
 style: Theme.of(context).textTheme.headline5!.copyWith(
 color: Colors.white,
 ),
 ),
 ),
 )
 ],
 ),
 );
 }

This gives us the following result, where the children within the Stack are centered, and our Container is aligned to the very bottom, as per the padding we specified in the left, top, and right parameters.

๐Ÿ‘ Aligning Container To The Stack, Shows A Blue Container With The Words "Yay For LogRocket!"

A complete demo of the code used to lay out the above Stack can be found here.

Clipping behavior

We can also use Stack to accomplish some great-looking layouts of our application without using the lower-level drawing functions.

We can do this by positioning our widgets outside of our Stack by using a Position widget, and then specifying a negative number for the appropriate direction (such as bottom or right).

If we place a container outside of the Stack, we can see that the Stack clips our overflowing widget by default.

๐Ÿ‘ Light Blue Stack Clips Bigger Blue Container

We can also tell our Stack not to clip the overflowing widgets by specifying clipBehaviour: Clip.none, in case we want the widgets to continue rendering outside the bounds of the Stack.

๐Ÿ‘ Stack Does Not Clip Container, Allows Overlapping

Practical Stack usages

Itโ€™s great to see colored boxes on top of each other, but when would we actually use a Stack in your Flutter app?

Stacking widgets on top of each other has a variety of usages, but two main areas where they are used are when specifying a widgetโ€™s position within a container or showing another widget that must be in the foreground.

To demonstrate this, letโ€™s make an app that shows us pictures of cats and gives us the option to add or remove them from our favorites. It will also always show us the total of how many cats we have in our favorites list.

Hereโ€™s what the finished product looks like:

๐Ÿ‘ Cat App With Stacks, Shows A Carousel Of Cat Pictures With A Forward Button And Favorites Container

Our app above has a Stack that contains both a PageView and a Container. The PageView contains five pictures of cats and a styled cover sheet while the Container shows how many favorited cats there are, and gives the user the option of clicking next instead of swiping.

The Container is also nested within a Positioned widget to make it appear down the bottom right of the screen. It also has the appropriate padding, so when the SnackBar shows, it doesnโ€™t overlap the buttons.

As we can see, the two buttons and the total amount of cats that we favorited remain visible even when we interact with the PageView directly underneath:

Stack(
 children: [
 PageView(
 onPageChanged: (page) {
 setState(() {
 showFavouriteButton = page > 0;
 });
 },
 controller: _controller,
 children: [
 Container(
 decoration: BoxDecoration(
 gradient: LinearGradient(
 begin: Alignment.topLeft,
 end: Alignment.bottomCenter,
 colors: [
 Colors.purple,
 Colors.deepPurpleAccent,
 ],
 )),
 child: Center(
 child: Text(
 "Look at these great cats!",
 style: Theme.of(context).textTheme.headline3,
 )),
 ),
 ...catImages.map(
 (e) => Image.network(
 e,
 ),
 )
 ],
 ),
 Positioned(
 bottom: 50,
 right: 0,
 child: Column(
 children: [
 Padding(
 padding: const EdgeInsets.all(16.0),
 child: Container(
 padding: EdgeInsets.all(16),
 decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: Colors.blue),
 child: Column(
 children: [
 Text("Total Favourite Cats"),
 Text(
 favourites.length.toString(),
 ),
 ],
 ),
 ),
 ),
 Row(
 children: [
 Padding(
 padding: const EdgeInsets.all(8.0),
 child: AnimatedOpacity(
 duration: Duration(milliseconds: 500),
 opacity: showFavouriteButton ? 1 : 0,
 child: FloatingActionButton(
 onPressed: () {
 setState(() {
 if (favourites.contains(catImages[_controller.page!.floor() - 1])) {
 favourites.remove(catImages[_controller.page!.floor() - 1]);
 ScaffoldMessenger.of(context).showSnackBar(
 SnackBar(
 content: Text("You removed this cat from your favorites."),
 ),
 );
 } else {
 favourites.add(catImages[_controller.page!.floor() - 1]);
 ScaffoldMessenger.of(context).showSnackBar(
 SnackBar(
 content: Text("You added this cat to your favorites."),
 ),
 );
 }
 });
 },
 child: Icon(Icons.favorite),
 ),
 ),
 ),
 Padding(
 padding: const EdgeInsets.all(8.0),
 child: FloatingActionButton(
 onPressed: () {
 _controller.nextPage(duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
 },
 child: Icon(Icons.navigate_next),
 ),
 )
 ],
 ),
 ],
 ),
 )
 ],
 ),

We also see that the widgets in the foreground, like the buttons and the favorites counter, respond to the tap events and donโ€™t pass them through to the widgets underneath.

Where there are no widgets in the foreground, our touch events pass through to the PageView behind.

You can view the full code for this project here.

Using IndexedStack

A close relative to the Stack widget is the IndexedStack widget. This widget is the same as the Stack widget, but it allows us to specify what item in the Stack we actually want to show.

This makes it a great fit for apps where we want to show one widget at a time, as it maintains the state for each child.

If we have an app that has a home screen, a settings screen, and a favorites screen, we can set the current widget to show in our setState method and easily change between widgets as we need.

Conclusion

The Stack widget is an essential widget in any Flutter developersโ€™ toolkit, and I hope this article has helped you get started with it ๐Ÿ™Œ.

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
๐Ÿ‘ Image
๐Ÿ‘ Image
๐Ÿ‘ Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

๐Ÿ‘ Image
Emmanuel John
Jun 17, 2026 โ‹… 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

๐Ÿ‘ Image
Chizaram Ken
Jun 16, 2026 โ‹… 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension โ€” no new framework required.

๐Ÿ‘ Image
Ikeh Akinyemi
Jun 12, 2026 โ‹… 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo โ€” with email/password login, Google OAuth, session persistence, and protected routes.

๐Ÿ‘ Image
Chinwike Maduabuchi
Jun 9, 2026 โ‹… 13 min read
View all posts

Would you be interested in joining LogRocket's developer community?

Join LogRocketโ€™s Content Advisory Board. Youโ€™ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now