Mrye
Mrye

Reputation: 727

Flutter ListTile enable multi-select onLongPress

I would like to achieve similar things in Google Keep.

Enable multi-select onLongPress

How can I enable multiple selections on long press and change the header buttons, so that I can delete those selected items later?

My current Dart code :

@override
  Widget build(BuildContext context) {
    return new Card(
      child: new Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
        new ListTile(
          leading: const Icon(Icons.info),
          title: new Text(item.name),
          subtitle: new Text(item.description),
          trailing: new Text(item.dateTime.month.toString()),
          onTap: () => _openEditDialog(context, item),
          onLongPress: // what should I put here,
        )
      ]),
    );
  }

Upvotes: 17

Views: 20788

Answers (2)

thebigbrownBox
thebigbrownBox

Reputation: 51

I made a similar implementation a couple of days ago. Long-press on any item in the list to activate the selection, then single-tap to select afterwards. Once the desired items have been selected, tap the delete button to remove all selected items from the list.

CODE:

import 'package:flutter/material.dart';

void main() => runApp(const MultiSelect());

class MultiSelect extends StatefulWidget {
  const MultiSelect({super.key});

  @override
  State<MultiSelect> createState() => _MultiSelectState();
}

class _MultiSelectState extends State<MultiSelect> {
  existsInTrashCan(Course course) => trashCan.contains(course);
  List<Course> trashCan = [];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        brightness: Brightness.dark,
        colorSchemeSeed: Colors.cyan.shade900,
        useMaterial3: true,
      ),
      home: Scaffold(
        appBar: trashCan.isEmpty
            ? AppBar(
                title: const Text('Multi-Delete'),
                centerTitle: true,
                backgroundColor: Colors.grey.shade900,
              )
            : AppBar(
                backgroundColor: Colors.grey.shade800,
                leading: IconButton(
                  icon: const Icon(Icons.clear),
                  onPressed: () {
                    setState(() {
                      trashCan.clear();
                    });
                  },
                ),
                title: Text(trashCan.length.toString()),
                actions: [
                  IconButton(
                      onPressed: () {
                        coursesData
                            .removeWhere((item) => trashCan.contains(item));
                        trashCan.clear();
                        setState(() {});
                      },
                      icon: const Icon(Icons.delete))
                ],
              ),
        body: ListView(
          children: [
            const SizedBox(height: 30),
            ListView.separated(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              itemCount: coursesData.length,
              separatorBuilder: (context, index) => const SizedBox(height: 5),
              itemBuilder: (BuildContext context, int index) {
                return PrettyCard(
                  name: coursesData[index].name,
                  isSelected: existsInTrashCan(coursesData[index]),
                  trashCan: trashCan,
                  onTap: () {
                    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
                      content: Text('Single Tap!'),
                      duration: Duration(seconds: 1),
                    ));
                  },
                  onDelete: () {
                    if (trashCan.contains(coursesData[index])) {
                      trashCan.remove(coursesData[index]);
                      setState(() {});
                    } else {
                      trashCan.add(coursesData[index]);
                      setState(() {});
                    }
                  },
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class PrettyCard extends StatelessWidget {
  final String name;
  final bool isSelected;
  final void Function()? onDelete;
  final void Function()? onTap;
  final List trashCan;

  const PrettyCard(
      {Key? key,
      required this.name,
      required this.isSelected,
      this.onDelete,
      this.onTap,
      required this.trashCan})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
      child: Card(
        surfaceTintColor: isSelected ? Colors.black : null,
        color: Colors.cyan.shade900,
        child: ListTile(
          dense: true,
          selected: isSelected,
          onTap: trashCan.isNotEmpty ? onDelete : onTap,
          onLongPress: onDelete,
          tileColor: Colors.cyan.shade900,
          selectedColor: Colors.white,
          selectedTileColor: Colors.cyan.shade900,
          title: Text(name),
          shape: RoundedRectangleBorder(
              side: BorderSide(
                  color: Colors.cyan.shade50,
                  width: 3,
                  style: isSelected ? BorderStyle.solid : BorderStyle.none)),
        ),
      ),
    );
  }
}

class Course {
  final String id;
  final String name;
  Course({
    required this.id,
    required this.name,
  });
}

List<Course> coursesData = [
  Course(id: '1', name: 'MTH'),
  Course(id: '2', name: 'STS'),
  Course(id: '3', name: 'ACC'),
  Course(id: '4', name: 'ETH'),
  Course(id: '5', name: 'PHY'),
  Course(id: '6', name: 'CSC'),
];

Refer to this repo: https://github.com/acromondx/FlutterVortex/tree/main/multi_select

demo

Upvotes: 0

Raouf Rahiche
Raouf Rahiche

Reputation: 31406

when the user does a long press the ListTile must change the selected property to true and vice versa and the Card Color must be Changed to Something like Grey[300]

class cardy extends StatefulWidget {
  @override
  _cardyState createState() => new _cardyState();
}

class _cardyState extends State<cardy> {
  var isSelected = false;
  var mycolor=Colors.white;

  @override
  Widget build(BuildContext context) {
    return new Card(
      color: mycolor,
      child: new Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
        new ListTile(
            selected: isSelected,
            leading: const Icon(Icons.info),
            title: new Text("Test"),
            subtitle: new Text("Test Desc"),
            trailing: new Text("3"),
            onLongPress: toggleSelection // what should I put here,
            )
      ]),
    );
  }

  void toggleSelection() {
    setState(() {
      if (isSelected) {
        mycolor=Colors.white;
        isSelected = false;
      } else {
        mycolor=Colors.grey[300];
        isSelected = true;
      }
    });
  }
}

enter image description here

EDIT: if you want to get the Border coloring effect make the column Inside the Container and set the decoration property to a variable and call it border and Edit the select method

void toggleSelection() {
    setState(() {
      if (isSelected) {
        border=new BoxDecoration(border: new Border.all(color: Colors.white));
        mycolor = Colors.white;
        isSelected = false;
      } else {
        border=new BoxDecoration(border: new Border.all(color: Colors.grey));
        mycolor = Colors.grey[300];
        isSelected = true;
      }
    });
  }

Upvotes: 17

Related Questions