Skip to main content

Flutter: DraggableScrollableSheet widget Tutorial

Introduction

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 
Final App


DraggableScrollableSheet

It is a widget that scrolls up and down of the viewport or screen according to the user's screen gestures. This widget becomes handy when you want to give some extra details which are lengthy content but don't want to navigate to a new screen. You might have come across many apps that implement the same thing. So let us learn to implement DraggableScrollableSheet in our own app.

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 name of the country in a Text widget and also 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.
App with ListView.builder



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. 
Final 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.


Comments

Popular posts from this blog

Flutter | Material Banner Tutorial

In this tutorial, we will create and display Material Banner in Flutter . Material Banners are displayed at the top of the screen . User interaction is required to dismiss the banner . Material Banner Material Banner alerts the user about action and p rovides some actions for the user to take . In brief, it alerts the user about an issue and the user address the issue . The user should dismiss the banner to remove it from the screen else it will be active. Syntax  ScaffoldMessenger.of(context).showMaterialBanner(MaterialBanner( content: content, actions: actions)) Code Here is our starting class. It is a stateless class and we will display a banner when the Button is pressed. class SnackBarTutorial extends StatelessWidget { const SnackBarTutorial({Key ? key}) : super ( key: key); @ override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text( "Material Banner"

How to create Snackbars in Flutter | Snackbar Tutorial

In this article, we will create and display different types of SnackBars in Flutter.  SnackBars in Flutter SnackBars are used to briefly display some information or inform the user about an action. For instance, when a user deletes a mail or message you may want to inform the user about the action. Also, the user can undo the action he performed by the undo button in the Snackbar . Syntax For creating a SnackBar, we use the following code. ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: content)); Inside the content field of SnackBar, we will pass the content. Any content can be passed inside it, but in practice, small messages with or without a button. Example Simple SnackBar class SnackBarTutorial extends StatelessWidget {   const SnackBarTutorial({Key? key}) : super (key: key);   @override   Widget build(BuildContext context) {     return

Flutter: DatePicker Tutorial both with Material and Cupertino Style

Introduction DatePicker is very important when you want the user to pick his / her date of birth or something else. In Flutter, implementing DatePicker is very easy and we will implement DatePicker in both Android or Material style and Cupertino or IOS style.  Table of contents Approach Project Setup Material Style DatePicker Cupertino Style DatePicker Conclusion Approach Flutter has widgets for everything and even for DatePicker. DatePicker widget is loaded with all needed animation and colours. So implementation is very easy.  Project Setup No extra plugins are required for this project and you can continue with your existing project. Here is the starting code. class DatePickerTutorial extends StatefulWidget { const DatePickerTutorial({Key? key}) : super (key: key); @ override _DatePickerTutorialState createState() => _DatePickerTutorialState(); } class _DatePickerTutorialState extends State<DatePick