Flutter SWIPE List Items (Like Whatsapp Message reply do)

Flutter Swipe List Item Tutorial

We all have seen the WhatsApp swipe to reply feature, it looks very cool when swipe and a reply text field appears. We can also make the same animation, same left, right swipe (WhatsApp has only right swipe though) events in our flutter project. Here is a very nice library that does this work for us and we can make flutter swipe list items very easy.

Flutter swipe list items is useful when we want to perform any action on a particular list item, of course, there are other ways too. but it looks nice when you have some cool animated stuff in your layout. Flutter is very good in customization of widgets and this is my most favorite task in flutter development to play with custom widgets. To make things useful with less code and effort and share with other developers to make their life easy is such a wonderful feeling.

In this tutorial, we have a simple example to demonstrate the working of the “swipeable” library. I tried to produce the same WhatsApp-like feeling in this tutorial. On swipe, we will have the same result as WhatsApp gave.

Flutter is an awesome mobile application development platform that provides a lot of good stuff with few lines of code

Expected output

flutter-swipeable-flutter-fumes

Required Dependencies

swipeable: 
fluttertoast: 

Installing it

flutter pub get

We are using the flutter swipeable package to make a swipeable list item and the second one is fluttertoast to show the toast on an event.

Let’s make whats app-like appbar

There is no special thing is done in this section, we have put a profile image in using CircleAvatar. Then added a title which is our contact name. Appbar provides us the power to add an action button so we have an array of activities that are represented by icons.

AppBar(
        leading: Row(
          children: [
            Padding(
              padding: const EdgeInsets.only(left: 10),
              child: CircleAvatar(
                backgroundImage:
                    NetworkImage('https://picsum.photos/id/237/200/300'),
              ),
            ),
          ],
        ),
        title: Material(
          color: Colors.white.withOpacity(0.0),
          child: InkWell(
            onTap: () {},
            child: Row(
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                Column(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 2.0),
                      child: Text(
                        'Flutter Fumes',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 18.0,
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
        actions: <Widget>[
          Builder(
            builder: (BuildContext context) {
              return IconButton(
                icon: Icon(Icons.videocam),
                onPressed: () {
               
                },
              );
            },
          ),
          Builder(
            builder: (BuildContext context) {
              return IconButton(
                icon: Icon(Icons.call),
                onPressed: () {
                
                },
              );
            },
          ),
        ],
      )

List of dummy messages

We created a dummy list of messages to make a list of items.

Flexible(
            flex: 1,
            child: ListView.builder(
                itemCount: dummyMessages.length,
                itemBuilder: (context, index) {
                  return Container(
                    child: Swipeable(
                      threshold: 100.0,
                      onSwipeLeft: () {
                        setState(() {
                          rightSelected = true;
                          leftSelected = false;
                        });
                      },
                      onSwipeRight: () {
                        setState(() {
                          rightSelected = false;
                          leftSelected = true;
                        });
                      },
                      child: Card(
                        key: UniqueKey(),
                        child: Padding(
                          padding: EdgeInsets.all(20),
                          child: Text(dummyMessages[index]),
                        ),
                      ),
                      background: Icon(
                        Icons.reply,
                        color: Colors.black26,
                      ),
                    ),
                  );
                }),
          ),

Message text field with attachment, smeily, and send button

Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 10, 10),
            child: Column(
              children: [
                leftSelected
                    ? _replyWidget()
                    : AnimatedContainer(
                        duration: Duration(seconds:1),
                      ),
                Row(
                  mainAxisSize: MainAxisSize.max,
                  children: <Widget>[
                    Flexible(
                      flex: 1,
                      child: Container(
                        decoration: BoxDecoration(
                          borderRadius:
                              BorderRadius.all(const Radius.circular(30.0)),
                          color: Colors.white,
                        ),
                        child: Row(
                          children: <Widget>[
                            IconButton(
                              padding: const EdgeInsets.all(0.0),
                              disabledColor: Colors.grey,
                              color: Colors.redAccent,
                              icon: Icon(Icons.insert_emoticon),
                              onPressed: () {},
                            ),
                            Flexible(
                              child: TextField(
                                decoration: InputDecoration(
                                  border: InputBorder.none,
                                  contentPadding: const EdgeInsets.all(0.0),
                                  hintText: 'Type a message',
                                  hintStyle: TextStyle(
                                    fontSize: 16.0,
                                  ),
                                  counterText: '',
                                ),
                                onSubmitted: (String text) {},
                                keyboardType: TextInputType.multiline,
                                maxLines: null,
                                maxLength: 100,
                              ),
                            ),
                            IconButton(
                              color: Colors.redAccent,
                              icon: Icon(Icons.attach_file),
                              onPressed: () {},
                            ),
                            IconButton(
                              color: Colors.redAccent,
                              icon: Icon(Icons.camera_alt),
                              onPressed: () {},
                            )
                          ],
                        ),
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(left: 4.0),
                      child: FloatingActionButton(
                          elevation: 2.0,
                          foregroundColor: Colors.white,
                          child: Icon(Icons.send),
                          onPressed: () {
                            Fluttertoast.showToast(msg: 'Sending Message');
                          }),
                    )
                  ],
                ),
              ],
            ),
          )

Reply widget design

Widget _replyWidget() {
    return AnimatedContainer(
      alignment: Alignment.centerLeft,
      duration: Duration(seconds: 1),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(10.0)),
        color: Colors.white,
      ),
      child: Padding(
        padding: EdgeInsets.all(5),
        child: Stack(
          children: [
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.grey.shade100,
                border: Border(
                  left: BorderSide(color: Colors.purple, width: 3),
                ),
              ),
              child: Column(
                children: [
                  Text('Reply',
                      style: TextStyle(
                          color: Colors.purple,
                          fontSize: 15,
                          fontWeight: FontWeight.w500)),
                  SizedBox(height: 3),
                  Text('This is text message sample',
                      style: TextStyle(
                        color: Colors.black54,
                        fontSize: 12,
                      )),
                ],
              ),
            ),
            Positioned(
                right: 10,
                top: 10,
                child: GestureDetector(
                  onTap: () {
                    setState(() {
                      leftSelected = false;
                      rightSelected = false;
                    });
                  },
                  child: Icon(
                    Icons.clear,
                    color: Colors.black87,
                    size: 18,
                  ),
                )),
          ],
        ),
      ),
    );
  }

Complete Source Code

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:swipeable/swipeable.dart';

class ChatDetailScreen extends StatefulWidget {
  _ChatDetailScreen createState() => _ChatDetailScreen();
}

class _ChatDetailScreen extends State<ChatDetailScreen> {
  final dummyMessages = List<String>.generate(1000, (i) => 'Message $i');
  late bool leftSelected;
  late bool rightSelected;
  final Color scaffoldBgColor = const Color(0xfff0eee4);

  @override
  void initState() {
    leftSelected = false;
    rightSelected = false;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: scaffoldBgColor,
      appBar: AppBar(
        leading: Row(
          children: [
            Padding(
              padding: const EdgeInsets.only(left: 10),
              child: CircleAvatar(
                backgroundImage:
                    NetworkImage('https://picsum.photos/id/237/200/300'),
              ),
            ),
          ],
        ),
        title: Material(
          color: Colors.white.withOpacity(0.0),
          child: InkWell(
            onTap: () {},
            child: Row(
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                Column(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 2.0),
                      child: Text(
                        'Flutter Fumes',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 18.0,
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
        actions: <Widget>[
          Builder(
            builder: (BuildContext context) {
              return IconButton(
                icon: Icon(Icons.videocam),
                onPressed: () {

                },
              );
            },
          ),
          Builder(
            builder: (BuildContext context) {
              return IconButton(
                icon: Icon(Icons.call),
                onPressed: () {

                },
              );
            },
          ),
        ],
      ),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Flexible(
            flex: 1,
            child: ListView.builder(
                itemCount: dummyMessages.length,
                itemBuilder: (context, index) {
                  return Container(
                    child: Swipeable(
                      threshold: 100.0,
                      onSwipeLeft: () {
                        setState(() {
                          rightSelected = true;
                          leftSelected = false;
                        });
                      },
                      onSwipeRight: () {
                        setState(() {
                          rightSelected = false;
                          leftSelected = true;
                        });
                      },
                      child: Card(
                        key: UniqueKey(),
                        child: Padding(
                          padding: EdgeInsets.all(20),
                          child: Text(dummyMessages[index]),
                        ),
                      ),
                      background: Icon(
                        Icons.reply,
                        color: Colors.black26,
                      ),
                    ),
                  );
                }),
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 10, 10),
            child: Column(
              children: [
                leftSelected
                    ? _replyWidget()
                    : AnimatedContainer(
                        duration: Duration(seconds:1),
                      ),
                Row(
                  mainAxisSize: MainAxisSize.max,
                  children: <Widget>[
                    Flexible(
                      flex: 1,
                      child: Container(
                        decoration: BoxDecoration(
                          borderRadius:
                              BorderRadius.all(const Radius.circular(30.0)),
                          color: Colors.white,
                        ),
                        child: Row(
                          children: <Widget>[
                            IconButton(
                              padding: const EdgeInsets.all(0.0),
                              disabledColor: Colors.grey,
                              color: Colors.redAccent,
                              icon: Icon(Icons.insert_emoticon),
                              onPressed: () {},
                            ),
                            Flexible(
                              child: TextField(
                                decoration: InputDecoration(
                                  border: InputBorder.none,
                                  contentPadding: const EdgeInsets.all(0.0),
                                  hintText: 'Type a message',
                                  hintStyle: TextStyle(
                                    fontSize: 16.0,
                                  ),
                                  counterText: '',
                                ),
                                onSubmitted: (String text) {},
                                keyboardType: TextInputType.multiline,
                                maxLines: null,
                                maxLength: 100,
                              ),
                            ),
                            IconButton(
                              color: Colors.redAccent,
                              icon: Icon(Icons.attach_file),
                              onPressed: () {},
                            ),
                            IconButton(
                              color: Colors.redAccent,
                              icon: Icon(Icons.camera_alt),
                              onPressed: () {},
                            )
                          ],
                        ),
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(left: 4.0),
                      child: FloatingActionButton(
                          elevation: 2.0,
                          foregroundColor: Colors.white,
                          child: Icon(Icons.send),
                          onPressed: () {
                            Fluttertoast.showToast(msg: 'Sending Message');
                          }),
                    )
                  ],
                ),
              ],
            ),
          )
        ],
      ),
    );
  }

  Widget _replyWidget() {
    return AnimatedContainer(
      alignment: Alignment.centerLeft,
      duration: Duration(seconds: 1),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(10.0)),
        color: Colors.white,
      ),
      child: Padding(
        padding: EdgeInsets.all(5),
        child: Stack(
          children: [
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.grey.shade100,
                border: Border(
                  left: BorderSide(color: Colors.purple, width: 3),
                ),
              ),
              child: Column(
                children: [
                  Text('Reply',
                      style: TextStyle(
                          color: Colors.purple,
                          fontSize: 15,
                          fontWeight: FontWeight.w500)),
                  SizedBox(height: 3),
                  Text('This is text message sample',
                      style: TextStyle(
                        color: Colors.black54,
                        fontSize: 12,
                      )),
                ],
              ),
            ),
            Positioned(
                right: 10,
                top: 10,
                child: GestureDetector(
                  onTap: () {
                    setState(() {
                      leftSelected = false;
                      rightSelected = false;
                    });
                  },
                  child: Icon(
                    Icons.clear,
                    color: Colors.black87,
                    size: 18,
                  ),
                )),
          ],
        ),
      ),
    );
  }
}

If you have any query feel free to ask. Also, check out the bottom navigation series and custom textfield, social button design, and many others.

Thanks for reading.

Previous Post
Next Post

Leave a Reply

Your email address will not be published. Required fields are marked *