Home > Mobile >  Why do I have, and how to prevent, duplicate items in DropDownButton items?
Why do I have, and how to prevent, duplicate items in DropDownButton items?

Time:08-16

I'm creating a simple interface for user to select a client from a dropdown. However I end up with duplications of values in the menu items. How do I prevent this? (and why is it happening?)

The list of clients is a singleton that might be populated or empty - and my intention is to add a search field and - and this 'listicle' is shared amongst various routes.

I'm relatively new to Flutter/Dart & OOP.

import 'dart:developer' as developer;

import 'package:agent_01/presentation/state/entity_types/client_listicle.dart';
import 'package:agent_01/presentation/state/entity_types/message_listicle_item.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

class MessageEditPage extends StatefulWidget {
  const MessageEditPage({Key? key}) : super(key: key);
  static const routeName = "/messages/edit";

  @override
  State<MessageEditPage> createState() => _MessageEditPageState();
}

class _MessageEditPageState extends State<MessageEditPage> {
  late MessageLItem? messageLI;
  late bool haveDetail;
  late ClientListicle clientListicle = GetIt.I.get<ClientListicle>();
  final msgEdtFormKey = GlobalKey<FormState>();
  late String dropdownValue = "";
  List<DropdownMenuItem<String>> menuItems = [
    const DropdownMenuItem(value: "", child: Text('Select')),
  ];

  @override
  void initState() {
    super.initState();
    developer.log('initState', name: '_MessagePageState');
    if (dropdownValue.isEmpty) {
      developer.log('set to 1st ', name: '_MessagePageState');
      dropdownValue = dropdownItems.first.value!;
    }
  }

  List<DropdownMenuItem<String>> get dropdownItems {
    developer.log('have  ${clientListicle.items.length} clients',
        name: '_MessagePageState get dropdownItems');
    developer.log('have  ${menuItems.length} menuItems',
        name: '_MessagePageState get dropdownItems');
    if (clientListicle.items.isEmpty) {
      developer.log('go fetch', name: '_MessagePageState get dropdownItems');
      fetchClients();
    }
    developer.log('menuItems ${menuItems.toString()}', name: '_MessagePageState get dropdownItems');
    for (var element in clientListicle.items) {
      developer.log('el ${element.toString()}', name: '_MessagePageState get dropdownItems');
      DropdownMenuItem<String> potentialItem =
          DropdownMenuItem(value: element.id.toString(), child: Text(element.title));
      if (!menuItems.contains(potentialItem)) {
        developer.log('dont have so add', name: '_MessagePageState get dropdownItems');
        menuItems.add(potentialItem);
      } else {
        developer.log('skip', name: '_MessagePageState get dropdownItems');
      }
    }
    return menuItems;
  }

  Future<void> fetchClients() async {
    await clientListicle.fetch(numberToFetch: 5);
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    developer.log('have CL ${clientListicle.items.length}', name: '_MessagePageState build');

    return SafeArea(
        child: Scaffold(
            appBar: AppBar(
                title: Row(children: [
              const SizedBox(
                width: 12,
                height: 1,
              ),
              RichText(
                text: const TextSpan(
                  text: 'Write new message...', //messageLI.title,
                  // style: DefaultTextStyle.of(context).style,
                ),
                maxLines: 3,
              )
            ])),
            body: Center(
                child: Padding(
              padding: const EdgeInsets.all(5),
              child: Form(
                  key: msgEdtFormKey,
                  child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
                    DropdownButton(
                      // Initial Value
                      value: dropdownValue,
                      // Down Arrow Icon
                      icon: const Icon(Icons.keyboard_arrow_down),
                      items: dropdownItems,
                      onChanged: (String? newValue) {
                        developer.log('click $newValue', name: '_MessagePageState');
                        setState(() {
                          dropdownValue = newValue!;
                        });
                      },
                    ),
                    const TextField(
                      keyboardType: TextInputType.multiline,
                      minLines: 1, //Normal textInputField will be displayed
                      maxLines: 5, // when user presses enter it will adapt to it
                    ),
                    ElevatedButton(
                      onPressed: () {
                        developer.log('Send', name: '_MessagePageState');
                      },
                      child: Text('Send'),
                    )
                  ])),
            ))));
  }
}

/*
class ScreenArguments {
  final MessageLItem? message;
  ClientLItem? clientLItem;

  ScreenArguments(this.message, this.clientLItem);
}*/

and log output for this if accessing the screen with 3 items allready in the clients listicle:

[_MessagePageState] initState
[_MessagePageState] set to 1st 
[_MessagePageState get dropdownItems] have  3 clients
[_MessagePageState get dropdownItems] have  1 menuItems
[_MessagePageState get dropdownItems] menuItems [DropdownMenuItem<String>]
[_MessagePageState get dropdownItems] el Instance of 'ClientLItem'
[_MessagePageState get dropdownItems] dont have so add
[_MessagePageState get dropdownItems] el Instance of 'ClientLItem'
[_MessagePageState get dropdownItems] dont have so add
[_MessagePageState get dropdownItems] el Instance of 'ClientLItem'
[_MessagePageState get dropdownItems] dont have so add
[_MessagePageState build] have CL 3
[_MessagePageState get dropdownItems] have  3 clients
[_MessagePageState get dropdownItems] have  4 menuItems
[_MessagePageState get dropdownItems] menuItems [DropdownMenuItem<String>, DropdownMenuItem<String>, DropdownMenuItem<String>, DropdownMenuItem<String>]
[_MessagePageState get dropdownItems] el Instance of 'ClientLItem'
[_MessagePageState get dropdownItems] dont have so add
[_MessagePageState get dropdownItems] el Instance of 'ClientLItem'
[_MessagePageState get dropdownItems] dont have so add
[_MessagePageState get dropdownItems] el Instance of 'ClientLItem'
[_MessagePageState get dropdownItems] dont have so add

Thanks in advance.

Update: I'm not sure about the get dropdownItems - obviously that get's called on rebuild. The main issue though seems to be that if (!menuItems.contains(potentialItem)) doesn't work. So, I've replaced that with below which seems to solve the imediate problem but seems hacky:

 for (var element in clientListicle.items) {
      DropdownMenuItem<String> potentialItem =
          DropdownMenuItem(value: element.id.toString(), child: Text(element.title));
      bool isHave = false;
      for (var exstElement in menuItems) {
        if (exstElement.value == element.id.toString()) {
          isHave = true;
        }
      }
      if (!isHave) {
        menuItems.add(potentialItem);
        setState(() {});
      }
    }
    return menuItems;

Should I be defining a whole new class for the dropDowmMenuItems?

CodePudding user response:

Convert to Set, it will have unique value,

 for (var element in clientListicle.items.toSet().toList()) {
      developer.log('el ${element.toString()}',

CodePudding user response:

https://pub.dev/packages/lodash_flutter

maybe, you can check this package it's helpful for you.

"uniq" method will work for you.

Example,

List myList = [{"title": 'Avengers', "release_date": '10/01/2019'},
    {"title": 'Creed', "release_date": '10/01/2019'},
    {"title": 'Jumanji', "release_date": '30/10/2019'}]
   print(LodashFlutter.uniq(myList));

//Results
[{"title": 'Avengers', "release_date": '10/01/2019'},
   {"title": 'Jumanji', "release_date": '30/10/2019'}]
  • Related