How to use Stack in Flutter

To use Stack widget in Flutter, firstly, we need to know how it works. Secondly, we’ll learn how we can use Stack widget to build and design an adaptive and responsive Flutter application that runs on web and mobile platform at the same time.

Stack widget is one of the main layout widgets that we’ll need to use very often to build any kind of Flutter application.

Stack contains a list of widgets and positions them on top of the other. That means, the Stack allows us to overlap multiple widgets on a single screen. In other words, the first child of Stack is the bottom-most widget. And the last child is the top-most widget.

The first question that comes to our mind is, according to the Stack mechanism, the top-most widget should only be visible, as it sits on top of the other widgets and completely overlaps others.

Then, how we would use Stack, so every child widget will not only be visible, but also be positioned to build a nice-looking layout.

Therefore, the Stack either wrap its children widgets in a Positioned widget, or may place them in a Non-Positioned way.

We’ll see how we can use Stack to build the following design.

Stack widget use positioned widgets
Stack widget use positioned widgets

We’ve built the above design using Stack widget that again positions its children widget in a way, so that the image and text are placed properly.

Not only that the same design remains adaptive and responsive when we run the same flutter application in web platform.

Stack widget maintains adaptive and responsive nature
Stack widget maintains adaptive and responsive nature

Let’s see the first part of code.

import 'package:flutter/material.dart';

//import 'view/my_app.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String title = 'Basic Layout';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: title,
      home: MyAppHome(),
    );
  }
}

Now, we’re going to design the above MyAppHome() widget that shows the profile page as its home page.

To do that, let’s first design the AppBar first.

class MyAppHome extends StatelessWidget {
  const MyAppHome({Key? key}) : super(key: key);

  static const String userUrl =
      'https://cdn.pixabay.com/photo/2019/05/04/15/24/art-4178302_960_720.jpg';

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 4,
      child: Scaffold(
          appBar: AppBar(
            centerTitle: true,
            flexibleSpace: Container(
              decoration: const BoxDecoration(
                gradient: LinearGradient(
                  colors: [
                    Colors.pink,
                    Colors.grey,
                  ],
                  begin: Alignment.topRight,
                  end: Alignment.bottomRight,
                ),
              ),
            ),
            titleSpacing: 80,
            leading: const Icon(Icons.menu),
            title: const Text(
              'Let\'s Go!',
              textAlign: TextAlign.center,
            ),
            actions: [
              buildIcons(
                const Icon(Icons.add_a_photo),
              ),
              buildIcons(
                const Icon(
                  Icons.notification_add,
                ),
              ),
              buildIcons(
                const Icon(
                  Icons.settings,
                ),
              ),
              buildIcons(
                const Icon(Icons.search),
              ),
            ],
            bottom: const TabBar(
              isScrollable: true,
              indicatorColor: Colors.red,
              indicatorWeight: 10,
              tabs: [
                Tab(
                  icon: Icon(
                    Icons.home,
                  ),
                  text: 'Home',
                ),
                Tab(
                  icon: Icon(
                    Icons.panorama_fish_eye,
                  ),
                  text: 'Log in',
                ),
                Tab(
                  icon: Icon(
                    Icons.settings,
                  ),
                  text: 'Settings',
                ),
                Tab(
                  icon: Icon(
                    Icons.local_activity,
                  ),
                  text: 'Location',
                ),
              ],
            ),
          ),
...

We’ve wrapped the Scaffold widget with DefaultTabController widget that has a required parameter length which decides how many tabs we should use in TabBar widget. Moreover, according to that length mentioned we need to return the same number of pages in TabBarView widget in the body parameter.

Firstly, any TabBar needs a TabBarView widget to display different pages.

Although before that, when a tab is selected, the TabBar widget always looks up and tries to find the nearest DefaultTabController.

Basically, the TabController is created by the DefaultTabController. Because of that reason, we wrap the Scaffold widget by DefaultTabController.

Let us take a look at the body parameter.

#stack-positioned

...
body: TabBarView(children: [
            ListView(
              children: [
                Stack(
                  clipBehavior: Clip.none,
                  children: [
                    Container(
                      height: 200,
                      decoration: const BoxDecoration(
                        gradient: LinearGradient(
                          colors: [
                            Colors.pink,
                            Colors.grey,
                          ],
                          begin: Alignment.bottomRight,
                          end: Alignment.topRight,
                        ),
                      ),
                    ),
                    Positioned(
                      bottom: -20,
                      left: 0,
                      right: 0,
                      child: Center(
                        child: Container(
                          width: 100,
                          height: 100,
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(50),
                            boxShadow: const [
                              BoxShadow(
                                color: Colors.white,
                                spreadRadius: 4,
                              ),
                            ],
                            image: const DecorationImage(
                              fit: BoxFit.cover,
                              image: NetworkImage(userUrl),
                            ),
                          ),
                        ),
                      ),
                    ),
                    Positioned(
                      bottom: -150,
                      left: 0,
                      right: 0,
                      child: Center(
                        child: Container(
                          margin: const EdgeInsets.all(17),
                          width: 300,
                          height: 100,
                          child: const Text(
                            'Lady Ada Lovelace',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 30,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
/// coming out of Stack
///
                Container(
                  margin: const EdgeInsets.all(10),
                  padding: const EdgeInsets.only(top: 75),
                  child: const Text(
                    'Augusta Ada King, Countess of Lovelace (née Byron; 10 December 1815 - 27 November 1852) '
                    'was an English mathematician and writer, chiefly known for her work on Charles Babbage\'s '
                    'proposed mechanical general-purpose computer, the Analytical Engine. She was the '
                    'first to recognise that the machine had applications beyond pure calculation, and '
                    'to have published the first algorithm intended to be carried out by such a machine. '
                    'As a result, she is often regarded as the first computer programmer.',
                    textAlign: TextAlign.left,
                    style: TextStyle(
                      fontSize: 22,
                    ),
                  ),
                ),
              ],
            ),
            newPage('Log in'),
            newPage('Settings'),
            newPage('Location'),
          ])),
    );
  }

  IconButton buildIcons(Icon icon) {
    return IconButton(
      onPressed: () {},
      icon: icon,
    );
  }

In the body part, we’ve wrapped the whole widget tree with ListView widget.

Because we want to scroll if necessary, here a scrolling widget might help us doing so.

Next, we have used the Stack widget, and used three Container widgets as the children of the Stack and decorate them accordingly.

The first Container is the bottom-most, and the third Container is the top-most.

Since the first Container is the bottom-most, we have made it non-positioned.

However, the second and the third Containers are Positioned.

And to distinguish them with each other, we’ve used a property, bottom; and, after that we’ve provided a negative value, so that it shifts towards the negative side of Y axis.

The Second Container belonging to the Stack shifts 20 pixel down towards the negative side of Y axis.

#Stack-Positioned-bottom

Positioned(
                      bottom: -20,
                      left: 0,
                      right: 0,
                      child: Center(
                        child: Container(
...

And the Third Container belonging to the Stack shifts 150 pixel down towards the negative side of Y axis.

Positioned(
                      bottom: -150,
                      left: 0,
                      right: 0,
                      child: Center(
                        child: Container(
...

As the the child widget in a stack can be either positioned or non-positioned, we need to be careful to use them so that they are placed properly.

We always wrap the positioned items with Positioned widget and the must have a one non-null property.

To get the full code please visit the respective GitHub repository.

#Stack-Alignment

We provide the value and according to which the Stack widget adjusts its size. Moreover, we can also adjust the position of non-positioned child widgets with the help of alignment which by default positions to the top-left corner in left-to-right environments and the top-right corner in right-to-left environments.

We can learn more about the stack layout algorithm, in RenderStack.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter

Comments

Leave a Reply