Flutter: DraggableScrollableSheet widget Tutorial

In this tutorial, we are going to learn about the DraggableScrollableSheet widget. It is a widget that responds to user gestures. When the user drags from the bottom of the screen in the upward direction, the widget opens up and when scrolls down the widget close.

The final app will look as follows

Approach

The approach of our project is very simple. In our app, we are going to display some names of countries

with the help of the ListView.builder and ListTile widgets. The data will be initialized when we will set up our project.

Now when the user taps on any of the ListTile, the DraggableScrollableSheet widget will show the details of the selected tile. By default, it will show the details of the first item index.

Table of Contents

  1. DraggableScrollableSheet

  2. Project Setup

  3. Create our ListTile

  4. Implement DraggableScrollableSheet widget

  5. Some more features

  6. Conclusion

Project Setup

DraggableScrollableSheet widget comes with the Flutter framework only. Hence we don't need to install any other package.

Create a new project and let us start our tutorial.

In the main.dart file, the code should be as follows.

import 'dart:html';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'All About Flutter',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: MaterialColor(
          Color.fromRGBO(255, 72, 0, 1).value,
          {
            50: Color.fromRGBO(255, 72, 0, 1),
            100: Color.fromRGBO(255, 72, 0, 1),
            200: Color.fromRGBO(255, 72, 0, 1),
            300: Color.fromRGBO(255, 72, 0, 1),
            400: Color.fromRGBO(255, 72, 0, 1),
            500: Color.fromRGBO(255, 72, 0, 1),
            600: Color.fromRGBO(255, 72, 0, 1),
            700: Color.fromRGBO(255, 72, 0, 1),
            800: Color.fromRGBO(255, 72, 0, 1),
            900: Color.fromRGBO(255, 72, 0, 1),
          },
        ),
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<String> countryName = ['India', 'UK', 'USA'];
  List<String> countryNationalGame = ['Hockey', 'Cricket', 'Baseball'];
  List<String> countryGDP = ['2.87', '2.83', '21.43'];
  List<String> countryNationalAnimal = ['Tiger', 'Lion', 'American bison'];
  int selectedTile = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.amberAccent,
        appBar: AppBar(
          title: Text("Draggable Scrollable Sheet"),
      ),
      body: ListView.builder(
        itemCount: 3,
        itemBuilder: (context, index) {
          return ListTile();
        },
      ),
    );
  }
}

Project Contents:-

  • Some data about three countries which we will display in our app.

  • A variable selectedTile which will be updated when the user selects any ListTile

  • An AppBar

  • A ListView.builder

So we have set up our starting project.

Create our ListTile

Currently, our ListTile is empty. We will add the country's name in a Text widget and set up our onTap function.

Here is our most basic setup with a little style.

itemBuilder: (context, index) {
  return Padding(
    padding: EdgeInsets.all(8.0),
    child: ListTile(
      onTap: () {
        setState(() => selectedTile = index);
        print(selectedTile);
      },
      title: Text(countryName[index]),
      tileColor: Colors.orange[100],
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(5.0),
      ),
    ),
  );
},

Most things are simple. When the user taps on any of the tiles, we call the setState function which updates the index of the selected tile.

Run the app and the result will be as follows.

Now we will create our DraggableScrollableSheet widget. But before that, we have to wrap our ListView.builder inside the Stack widget. This is because the DraggableScrollableSheet widget will be displayed above over ListView and not below our ListView.

So wrap it with the Stack widget.

Stack(
  children: [
    ListView.builder(
      itemCount: 3,
      itemBuilder: (context, index) {
        return Padding(
          padding: EdgeInsets.all(8.0),
          child: ListTile(
            onTap: () {
              setState(() => selectedTile = index);
              print(selectedTile);
            },
            title: Text(countryName[index]),
            tileColor: Colors.orange[100],
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(5.0),
            ),
          ),
        );
      },
    ),
  ],
),

In the next section, we are going to implement the DraggableScrollableSheet widget.

Implement DraggableScrollableSheet

We will show our DraggableScrollableSheet widget above our ListView.builder widget.

Inside the Stack widget, children field, we will create our DraggableScrollableSheet widget.

The index of DraggableScollableSheet index inside the Stack widget should be next to ListView.builder.

Let us learn how it works.

DraggableScollableSheet widget contains on required field and that is the builder function.

builder: (context, scrollController) {}

Here we return the widget that we want to display. We will display the details of the country that is selected using the selectedTile variable. The builder function also has a scollController which we will provide to our ListView to give it an animation effect of scrolling.

It has many important parameters. They are listed below:-

  1. initialChildSize: Takes double value from 0 - 1.0 with default value of 0.5. It is the percentage of the screen to be occupied when the first widget is rendered.

  2. minChildSize: Takes double value from 0 - 1.0 with default value of 0.25. It is the percentage of the screen to be occupied that the child widget will display even if the user drags down it completely.

  3. maxChildSize: Takes double value from 0 - 1.0 with default value of 1.0. It is the percentage of the screen to be occupied that the child widget will display even if the user drags up it completely.

I will initialize the parameters as

  1. initialChildSize: .25,

  2. minChildSize: .1,

  3. maxChildSize: .8,

Now here is the code with some style.

DraggableScrollableSheet(
  initialChildSize: .25,
  minChildSize: .1,
  maxChildSize: .8,
  builder: (context, scrollController) {
    return ClipRRect(
      borderRadius: BorderRadius.only(
          topLeft: Radius.circular(12.0),
          topRight: Radius.circular(12.0)),
      child: Container(
        decoration: BoxDecoration(
          color: Colors.white,
        ),
        child: ListView(
          controller: scrollController,
          children: [
            Container(
              color: Colors.white,
              child: ListTile(
                title: Text("Country GDP (2019)"),
                subtitle: Text(
                  countryGDP[selectedTile],
                ),
              ),
            ),
            ListTile(
              title: Text("Country National Game"),
              subtitle: Text(
                countryNationalGame[selectedTile],
              ),
            ),
            ListTile(
              title: Text("Country National Animal"),
              subtitle: Text(
                countryNationalAnimal[selectedTile],
              ),
            ),
          ],
        ),
      ),
    );
  },
),

We have rounded the corners of the DraggableScrollableSheet. Also, we have assigned the scrollController to the controller field of ListView we have created in our app.

We have finished our app implementing the DraggableScollableSheet widget. Our widget is also animating. In case you have missed anywhere, here is the full code.

import 'dart:html';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'All About Flutter',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: MaterialColor(
          Color.fromRGBO(255, 72, 0, 1).value,
          {
            50: Color.fromRGBO(255, 72, 0, 1),
            100: Color.fromRGBO(255, 72, 0, 1),
            200: Color.fromRGBO(255, 72, 0, 1),
            300: Color.fromRGBO(255, 72, 0, 1),
            400: Color.fromRGBO(255, 72, 0, 1),
            500: Color.fromRGBO(255, 72, 0, 1),
            600: Color.fromRGBO(255, 72, 0, 1),
            700: Color.fromRGBO(255, 72, 0, 1),
            800: Color.fromRGBO(255, 72, 0, 1),
            900: Color.fromRGBO(255, 72, 0, 1),
          },
        ),
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<String> countryName = ['India', 'UK', 'USA'];
  List<String> countryNationalGame = ['Hockey', 'Cricket', 'Baseball'];
  List<String> countryGDP = ['2.87', '2.83', '21.43'];
  List<String> countryNationalAnimal = ['Tiger', 'Lion', 'American bison'];
  int selectedTile = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.amberAccent,
      appBar: AppBar(
        title: Text("Draggable Scrollable Sheet"),
      ),
      body: Stack(
        children: [
          ListView.builder(
            itemCount: 3,
            itemBuilder: (context, index) {
              return Padding(
                padding: EdgeInsets.all(8.0),
                child: ListTile(
                  onTap: () {
                    setState(() => selectedTile = index);
                    print(selectedTile);
                  },
                  title: Text(countryName[index]),
                  tileColor: Colors.orange[100],
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(5.0),
                  ),
                ),
              );
            },
          ),
          DraggableScrollableSheet(
            initialChildSize: .25,
            minChildSize: .1,
            maxChildSize: .8,
            builder: (context, scrollController) {
              return ClipRRect(
                borderRadius: BorderRadius.only(
                    topLeft: Radius.circular(12.0),
                    topRight: Radius.circular(12.0)),
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.white,
                  ),
                  child: ListView(
                    controller: scrollController,
                    children: [
                      Container(
                        color: Colors.white,
                        child: ListTile(
                          title: Text("Country GDP (2019)"),
                          subtitle: Text(
                            countryGDP[selectedTile],
                          ),
                        ),
                      ),
                      ListTile(
                        title: Text("Country National Game"),
                        subtitle: Text(
                          countryNationalGame[selectedTile],
                        ),
                      ),
                      ListTile(
                        title: Text("Country National Animal"),
                        subtitle: Text(
                          countryNationalAnimal[selectedTile],
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

Conclusion

We have made our DraggableScrollableSheet widget. It has all the modern features of the bottom sheet that we come across many apps. Hope you liked the tutorial. If you have any doubts, please comment below.

Did you find this article valuable?

Support All About Flutter | Flutter and Dart by becoming a sponsor. Any amount is appreciated!