esentis / multiple_search_selection

A highly customizable multiple selection widget with fuzzy search functionality.
https://pub.dev/packages/multiple_search_selection
BSD 3-Clause "New" or "Revised" License
11 stars 13 forks source link

Select all, Clear all #52

Open Mohdx opened 2 weeks ago

Mohdx commented 2 weeks ago

Hello, thank you for this cool package, really appreciate it

Current Behavior

When selecting all items then clear all. the items doesn't return back to builder list

final MultipleSearchController<GroupModel> ctrl =
      MultipleSearchController<GroupModel>();
      ...

  void _select() {
    final List<GroupModel> x = ctrl.getPickedItems();
    final List<String> labels = List.generate(x.length, (i) => x[i].name ?? '');
    widget.onSelect(('$labels').replaceAll(RegExp(r'[\[\]]'), ''), x);
  }
  ...

return MultipleSearchSelection<GroupModel>(
 controller: ctrl,
 onItemRemoved: (_) => _select(),
 onItemAdded: (_) => _select(),
 onTapClearAll: () => _select(),
 onTapSelectAll: () => _select(),
 itemBuilder: (GroupModel group, int _) {
        return Container(
          margin: const EdgeInsets.all(6.0),
          padding: const EdgeInsets.fromLTRB(12, 14, 12, 14),
          decoration: Decorations.flat(context),
          child: Text(group.name ?? ''),
        );
      },

Expected behavior/code

When selecting clear all. the items returns back to builder list

SVID_20240618_122211_1-ezgif com-video-to-gif-converter

Full code

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:multiple_search_selection/multiple_search_selection.dart';

import '/app/theme/colors/main_colors.dart';
import '/app/theme/decoration/decorations.dart';
import '/models/ModelProvider.dart';

class CompanyGroupField extends StatefulWidget {
  const CompanyGroupField({
    super.key,
    required this.groups,
    required this.onSelect,
    this.selectedGroups = const <GroupModel>[],
  });
  final List<GroupModel> groups;
  final List<GroupModel> selectedGroups;
  final void Function(String, List<GroupModel>) onSelect;

  @override
  State<CompanyGroupField> createState() => _CompanyGroupFieldState();
}

class _CompanyGroupFieldState extends State<CompanyGroupField> {
  late AppLocalizations arb;

  final MultipleSearchController<GroupModel> ctrl =
      MultipleSearchController<GroupModel>();

  void _select() {
    final List<GroupModel> x = ctrl.getPickedItems();
    final List<String> labels = List.generate(x.length, (i) => x[i].name ?? '');
    widget.onSelect(('$labels').replaceAll(RegExp(r'[\[\]]'), ''), x);
  }

  @override
  void didChangeDependencies() {
    arb = AppLocalizations.of(context)!;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return MultipleSearchSelection<GroupModel>(
      controller: ctrl,
      initialPickedItems: widget.selectedGroups,
      items: widget.groups,
      onItemRemoved: (_) => _select(),
      onItemAdded: (_) => _select(),
      onTapClearAll: () => _select(),
      onTapSelectAll: () => _select(),
      clearSearchFieldOnSelect: true,

      searchField: TextField(
        decoration: InputDecoration(
          hintText: 'Search groups',
          contentPadding: const EdgeInsets.fromLTRB(18, 14, 18, 14),
          focusedBorder: Decorations.formBorderTop(context),
          enabledBorder: Decorations.formBorderTop(context),
          border: Decorations.formBorderTop(context),
        ),
      ),

      pickedItemBuilder: (group) {
        return Container(
          padding: const EdgeInsets.all(8),
          decoration: Decorations.border(context),
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(group.name ?? ''),
              const SizedBox(width: 4),
              const Icon(Icons.cancel, color: MainColors.red, size: 20),
            ],
          ),
        );
      },

      itemBuilder: (GroupModel group, int _) {
        return Container(
          margin: const EdgeInsets.all(6.0),
          padding: const EdgeInsets.fromLTRB(12, 14, 12, 14),
          decoration: Decorations.flat(context),
          child: Text(group.name ?? ''),
        );
      },

      fieldToCheck: (group) => group.name ?? '',
      noResultsWidget: const SizedBox.shrink(),
      pickedItemsBoxDecoration: Decorations.border(context),
      showedItemsBoxDecoration: Decorations.borderBottom(context),
    );
  }
}
esentis commented 1 week ago

Hell hello !!

Thanks for taking the time to fill the issue. I will investigate it as soon as possible, and will get back to you ! Cheers.

esentis commented 1 week ago

@Mohdx is there a reason you use the same method for those callbacks ?

     onTapClearAll: () => _select(),
     onTapSelectAll: () => _select(),
Mohdx commented 1 week ago

@Mohdx is there a reason you use the same method for those callbacks ?


     onTapClearAll: () => _select(),

     onTapSelectAll: () => _select(),

Sorry I couldn't find the best use practice in the documentation.

Just wanted to keep listening to controller on click, what is selected items

esentis commented 1 week ago

There is a callback onPickedChange which fires everytime the picked / selected items change. Your example could lead to inconsistencies since you are manipulating the list on clear method. The list manipulation happens internally, there is no need to do it on your own.

onTapClearAll & onTapSelectAll are just callbacks when the events occures.

Mohdx commented 1 week ago

There is a callback onPickedChange which fires everytime the picked / selected items change. Your example could lead to inconsistencies since you are manipulating the list on clear method. The list manipulation happens internally, there is no need to do it on your own.

onTapClearAll & onTapSelectAll are just callbacks when the events occures.

Gotcha, thanks for the explanation. Meaning for my use, no need for a controller to get selected items on click, so my code now looks like this:

class _MultiSelectFieldState<T> extends State<MultiSelectField<T>> {
  late AppLocalizations arb;

  // final MultipleSearchController<T> ctrl = MultipleSearchController<T>();

  void _select(List<T> list) {
    final List<String> labels =
        List.generate(list.length, (i) => widget.labelExtractor(list[i]));
    widget.onSelect(('$labels').replaceAll(RegExp(r'[\[\]]'), ''), list);
  }

  @override
  void didChangeDependencies() {
    arb = AppLocalizations.of(context)!;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return MultipleSearchSelection<T>(
      // controller: ctrl,
      initialPickedItems: widget.selectedModels,
      items: widget.models,
      onPickedChange: (list) => _select(list),
      clearSearchFieldOnSelect: true,
      ...

However, I'm still facing the issue :( Also I faced another issue where when you set itemsVisibility: to ShowedItemsVisibility.toggle, The items is not removed from the pop-up list when selecting ezgif-5-5b68142990

esentis commented 1 week ago

Hmm, try to not manipulate the original list on your onPickedChange method (_select). If you want to manipulate your original list, try creating a new local list for your MultipleSearchSelection widget.

e.g

late final _localList = [...widget.groups]

As I said the adding/removing from lists is done internally and there is no need for you to do this manually. onPickedChange ideally, should just update a variable with your currently picked items, rather than changing the original feeding list.

Mohdx commented 6 days ago

Hmm, try to not manipulate the original list on your onPickedChange method (_select). If you want to manipulate your original list, try creating a new local list for your MultipleSearchSelection widget.

e.g

late final _localList = [...widget.groups]

As I said the adding/removing from lists is done internally and there is no need for you to do this manually. onPickedChange ideally, should just update a variable with your currently picked items, rather than changing the original feeding list.

Sorry I'm confused. I comment out onPickedChange but still has the same issues.

Nevermind about my code. this is the library example: Why "clear all" doesn't return back items: (to select list)? how I'm going to do it manually? Also you can select the same item more than once in the pop-up list, unlike the example GIF you cannot. ezgif-6-49d197c5e5

image

Mohdx commented 6 days ago

Try to search for a country after "clear all" you won't be able to see any