searchfield icon indicating copy to clipboard operation
searchfield copied to clipboard

RangeError onSubmit when selected value is not part of the list

Open Areion0 opened this issue 1 year ago • 1 comments

Describe the bug A RangeError is thrown when the currently selected value is not part of the results list and the field is submitted with the enter key.

The following RangeError was thrown while calling onSubmitted for TextInputAction.done:
RangeError (index): Invalid value: Not in inclusive range 0..3: 6

When the exception was thrown, this was the stack:
#0      List.[] (dart:core-patch/growable_array.dart:264:36)
#1      _SearchFieldState.build.<anonymous closure> (package:searchfield/src/searchfield.dart:806:57)
searchfield.dart:806
#2      EditableTextState._finalizeEditing (package:flutter/src/widgets/editable_text.dart:3079:18)
editable_text.dart:3079
#3      EditableTextState.performAction (package:flutter/src/widgets/editable_text.dart:2910:9)
editable_text.dart:2910
#4      TextInput._handleTextInputInvocation (package:flutter/src/services/text_input.dart:1870:39)
text_input.dart:1870
#5      TextInput._loudlyHandleTextInputInvocation (package:flutter/src/services/text_input.dart:1753:20)
text_input.dart:175

To Reproduce Steps to reproduce the behavior:

  1. Provide a list of suggestions & a valid initialValue.
  2. Type a value in the searchfield that returns suggestions which don't include the selected value.
  3. Press the enter key to submit the field.
  4. The RangeError is thrown.

[X] By clicking this checkbox, I confirm I am using the latest version of the package found on pub.dev/searchfield

Expected behavior onSubmit should be called without throwing an error so that this case can be handled.

Actual behavior A RangeError is thrown and onSubmit is not called.

Proposed solution Allow for the selected item to be deselected when the search text changes. This can be done with an optional flag. e.g. deselectOnTextChanged

Alternatively, there could be a value property that holds the currently selected value like in DropdownButtonFormField for example. In that case we could manipulate the value in whatever way one might need.

Additional context I'm on Flutter 3.13.9

Areion0 avatar May 01 '24 11:05 Areion0

Hi @Areion0, Thanks for filing the issue , I tried to reproduce the issue but I was unable to reproduce it. Can you provide a minimal code sample and provide me with the exact steps to reproduce the error?

I verified it on the latest version of the package v1.0.1

maheshj01 avatar May 02 '24 12:05 maheshj01

I am also facing this issue. Any workaround?

I click on the textfield and start typing, then i use the keyboard arrow keys to navigate the options , when i click enter, i get the below error:

RangeError (index): Index out of range: no indices are valid: 0

When the exception was thrown, this was the stack: dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 297:3 throw dart-sdk/lib/_internal/js_dev_runtime/private/js_array.dart 600:7 _get] packages/searchfield/src/searchfield.dart 546:44 handleSelectKeyPress packages/flutter/src/widgets/actions.dart 597:39 invoke packages/flutter/src/widgets/actions.dart 338:16 [_invoke] packages/flutter/src/widgets/actions.dart 666:27 invokeActionIfEnabled packages/flutter/src/widgets/shortcuts.dart 874:81 handleKeypress packages/flutter/src/widgets/shortcuts.dart 1065:20 [_handleOnKeyEvent] packages/flutter/src/widgets/focus_manager.dart 2002:27 handleKeyMessage packages/flutter/src/services/hardware_keyboard.dart 1103:16 [_dispatchKeyMessage] packages/flutter/src/services/hardware_keyboard.dart 1175:75 handleRawKeyMessage

hitashaRajesh avatar Jun 10 '24 11:06 hitashaRajesh

@hitashaRajesh Are you facing this error on the latest version 1.0.5 ? If so can you please share a minimal code sample to reproduce the issue?

Thanks

maheshj01 avatar Jun 10 '24 17:06 maheshj01

Hello @maheshmnj, I've used the country search example to make a code sample. I've added Albania as the initial value and recorded a short video of the behavior I described.

Package version: 1.0.5 Flutter: 3.22.2

country_search.dart

code sample
import '../country_model.dart';
import 'package:flutter/material.dart';
import 'package:searchfield/searchfield.dart';

class CountrySearch extends StatefulWidget {
  CountrySearch({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _CountrySearchState createState() => _CountrySearchState();
}

class _CountrySearchState extends State<CountrySearch> {
  @override
  void dispose() {
    _searchController.dispose();
    focus.dispose();
    super.dispose();
  }

  final _searchController = TextEditingController();

  @override
  void initState() {
    super.initState();
    countries = data.map((e) => Country.fromMap(e)).toList();
  }

  final _formKey = GlobalKey<FormState>();

  final focus = FocusNode();
  List<Country> countries = [];
  Country _selectedCountry = Country.init();

  bool containsCountry(String text) {
    final Country? result = countries.firstWhere(
        (Country country) => country.name.toLowerCase() == text.toLowerCase(),
        orElse: () => Country.init());

    if (result!.name.isEmpty) {
      return false;
    }
    return true;
  }

  get initialValue {
    final country = countries.firstWhere((country) => country.name.contains("Albania"));

    return SearchFieldListItem(country.name, item: country);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () => FocusScope.of(context).unfocus(),
        child: Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Form(
                    key: _formKey,
                    child: SearchField(
                      focusNode: focus,
                      initialValue: initialValue,
                      suggestions: countries
                          .map((country) =>
                              SearchFieldListItem(country.name, item: country))
                          .toList(),
                      suggestionState: Suggestion.hidden,
                      controller: _searchController,
                      hint: 'Search by country name',
                      maxSuggestionsInViewPort: 4,
                      itemHeight: 45,
                      textCapitalization: TextCapitalization.words,
                      validator: (x) {
                        if (x!.isEmpty || !containsCountry(x)) {
                          return 'Please Enter a valid Country';
                        }
                        return null;
                      },
                      inputType: TextInputType.text,
                      onSuggestionTap: (SearchFieldListItem<Country> x) {
                        setState(() {
                          _selectedCountry = x.item!;
                        });
                        _formKey.currentState!.validate();
                        focus.unfocus();
                      },
                    ),
                  ),
                ),
                Expanded(
                    child: Center(
                        child: _selectedCountry.name.isEmpty
                            ? Text('select Country')
                            : CountryDetail(
                                country: _selectedCountry,
                              ))),
                ElevatedButton(
                    onPressed: () {
                      _formKey.currentState!.validate();
                    },
                    child: Text('validate')),
                SizedBox(
                  height: 100,
                )
              ],
            )));
  }
}

class CountryDetail extends StatefulWidget {
  final Country? country;

  CountryDetail({Key? key, required this.country}) : super(key: key);
  @override
  _CountryDetailState createState() => _CountryDetailState();
}

class _CountryDetailState extends State<CountryDetail> {
  Widget dataWidget(String key, int value) {
    return Container(
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(
          horizontal: MediaQuery.of(context).size.width * 0.15),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text('$key:'),
          SizedBox(
            width: 16,
          ),
          Flexible(
            child: Text(
              '$value',
              style: TextStyle(fontSize: 30),
            ),
          )
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Container(
          padding: EdgeInsets.symmetric(horizontal: 24),
          alignment: Alignment.center,
          child: Text(
            widget.country!.name,
            style: TextStyle(fontSize: 40),
          ),
        ),
        SizedBox(
          height: 20,
        ),
        dataWidget('Population:', widget.country!.population),
        dataWidget('Density', widget.country!.density),
        dataWidget('Land Area (in Km\'s)', widget.country!.landArea)
      ],
    );
  }
}

https://github.com/maheshmnj/searchfield/assets/23741094/602f1f41-41fc-46c0-bcc0-3fb347d23269

Areion0 avatar Jul 10 '24 12:07 Areion0

thanks for the code sample and the demo, The issue is when user executes a search no option is being selected, I believe the first option in non empty result should be selected by default

maheshj01 avatar Jul 10 '24 15:07 maheshj01

Fixed and released in v1.0.6

maheshj01 avatar Jul 10 '24 16:07 maheshj01