flutter_simple_multiselect 0.0.2 copy "flutter_simple_multiselect: ^0.0.2" to clipboard
flutter_simple_multiselect: ^0.0.2 copied to clipboard

Flutter simple multiselect project.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_simple_multiselect/flutter_simple_multiselect.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Simple Multiselect Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Simple Multiselect Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late Color lineColor = const Color.fromRGBO(36, 37, 51, 0.04);
  List selectedItems = [];
  List selectedItemsAsync = [];
  Map? singleItem;
  bool isLoading = false;

  List<Map<String, dynamic>> testData = [
    {"uuid": 1, "name": "Alfred Johanson"},
    {"uuid": 2, "name": "Goran Borovic"},
    {"uuid": 3, "name": "Ivan Horvat"},
    {"uuid": 4, "name": "Bjorn Sigurdson"}
  ];

  Future<List<Map<String, dynamic>>> searchFunction(query) async {
    return testData.where((element) {
      return element["name"].toLowerCase().contains(query.toLowerCase());
    }).toList();
  }

  Future<List<Map<String, dynamic>>> searchFunctionAsync(query) async {
    return Future.delayed(const Duration(seconds: 1), () {
      return testData.where((element) {
        return element["name"].toLowerCase().contains(query.toLowerCase());
      }).toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          // Here we take the value from the MyHomePage object that was created by
          // the App.build method, and use it to set our appbar title.
          title: Text(widget.title),
        ),
        body: Padding(
          padding: const EdgeInsets.all(20),
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () {
              FocusScope.of(context).unfocus();
            },
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Padding(padding: EdgeInsets.only(bottom: 10), child: Text("Static data multiselect")),
                _staticData(),
                const Padding(padding: EdgeInsets.only(bottom: 10, top: 20), child: Text("Async data multiselect")),
                _asyncData(),
                const Padding(padding: EdgeInsets.only(bottom: 10, top: 20), child: Text("Data single select")),
                _staticSingleData(),
              ],
            ),
          ),
        ));
  }

  Widget _staticSingleData() {
    return FlutterMultiselect(
        multiselect: false,
        autofocus: false,
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
        enableBorderColor: lineColor,
        focusedBorderColor: lineColor,
        borderRadius: 5,
        borderSize: 1,
        resetTextOnSubmitted: true,
        minTextFieldWidth: 300,
        suggestionsBoxMaxHeight: 300,
        length: 1,
        tagBuilder: (context, index) => SelectTag(
              index: index,
              label: selectedItems[index]["name"],
              onDeleted: (value) {
                selectedItems.removeAt(index);
                setState(() {});
              },
            ),
        suggestionBuilder: (context, state, data) {
          var existing = singleItem == data;
          return Material(
            child: ListTile(
                selected: existing,
                trailing: existing ? const Icon(Icons.check) : null,
                selectedColor: Colors.white,
                selectedTileColor: Colors.green,
                dense: true,
                title: Text(data["name"].toString()),
                onTap: () {
                  singleItem = existing ? null : data;

                  state.selectAndClose(data, singleItem != null ? singleItem!["name"].toString() : "");
                  setState(() {});
                }),
          );
        },
        suggestionsBoxElevation: 10,
        findSuggestions: (String query) async {
          setState(() {
            isLoading = true;
          });
          var data = await searchFunction(query);
          setState(() {
            isLoading = false;
          });
          return data;
        });
  }

  Widget _staticData() {
    return FlutterMultiselect(
        autofocus: false,
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
        enableBorderColor: lineColor,
        focusedBorderColor: lineColor,
        borderRadius: 5,
        borderSize: 1,
        resetTextOnSubmitted: true,
        minTextFieldWidth: 300,
        suggestionsBoxMaxHeight: 300,
        length: selectedItems.length,
        tagBuilder: (context, index) => SelectTag(
              index: index,
              label: selectedItems[index]["name"],
              onDeleted: (value) {
                selectedItems.removeAt(index);
                setState(() {});
              },
            ),
        suggestionBuilder: (context, state, data) {
          var existingIndex = selectedItems.indexWhere((element) => element["uuid"] == data["uuid"]);
          var selectedData = data;
          return Material(
            child: ListTile(
                dense: true,
                selected: existingIndex >= 0,
                trailing: existingIndex >= 0 ? const Icon(Icons.check) : null,
                selectedColor: Colors.white,
                selectedTileColor: Colors.green,
                title: Text(selectedData["name"].toString()),
                onTap: () {
                  if (existingIndex >= 0) {
                    selectedItems.removeAt(existingIndex);
                  } else {
                    selectedItems.add(data);
                  }

                  state.selectAndClose(data);
                  setState(() {});
                }),
          );
        },
        suggestionsBoxElevation: 10,
        findSuggestions: (String query) async {
          setState(() {
            isLoading = true;
          });
          var data = await searchFunction(query);
          setState(() {
            isLoading = false;
          });
          return data;
        });
  }

  Widget _asyncData() {
    return FlutterMultiselect(
        autofocus: false,
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
        enableBorderColor: lineColor,
        focusedBorderColor: lineColor,
        borderRadius: 5,
        borderSize: 1,
        resetTextOnSubmitted: true,
        minTextFieldWidth: 300,
        suggestionsBoxMaxHeight: 300,
        length: selectedItemsAsync.length,
        isLoading: isLoading,
        tagBuilder: (context, index) => SelectTag(
              index: index,
              label: selectedItemsAsync[index]["name"],
              onDeleted: (value) {
                selectedItemsAsync.removeAt(index);
                setState(() {});
              },
            ),
        suggestionBuilder: (context, state, data) {
          var existingIndex = selectedItemsAsync.indexWhere((element) => element["uuid"] == data["uuid"]);
          var selectedData = data;
          return Material(
              child: ListTile(
                  selected: existingIndex >= 0,
                  trailing: existingIndex >= 0 ? const Icon(Icons.check) : null,
                  selectedColor: Colors.white,
                  selectedTileColor: Colors.green,
                  title: Text(selectedData["name"].toString()),
                  onTap: () {
                    var existingIndex = selectedItemsAsync.indexWhere((element) => element["uuid"] == data["uuid"]);
                    if (existingIndex >= 0) {
                      selectedItemsAsync.removeAt(existingIndex);
                    } else {
                      selectedItemsAsync.add(data);
                    }

                    state.selectAndClose(data);
                    setState(() {});
                  }));
        },
        suggestionsBoxElevation: 10,
        findSuggestions: (String query) async {
          setState(() {
            isLoading = true;
          });
          var data = await searchFunctionAsync(query);
          setState(() {
            isLoading = false;
          });
          return data;
        });
  }
}

class SelectTag extends StatelessWidget {
  const SelectTag({
    super.key,
    required this.label,
    required this.onDeleted,
    required this.index,
  });

  final String label;
  final ValueChanged<int> onDeleted;
  final int index;
  final Color darkAlias6 = const Color.fromRGBO(36, 37, 51, 0.06);

  @override
  Widget build(BuildContext context) {
    return Chip(
      backgroundColor: darkAlias6,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      labelPadding: const EdgeInsets.only(left: 8.0),
      label: Text(label),
      deleteIcon: const Icon(
        Icons.close,
        size: 18,
      ),
      onDeleted: () {
        onDeleted(index);
      },
    );
  }
}