Eric Su
Eric Su

Reputation: 1175

How to Implement TextField with dropdown selection?

I am looking for a way to display a TextField with dropdown list showing previous inputs by the user. The contents of the dropdown list are stored in shared preferences. I know how to implement them separately but I am struggling to find a solution that combines the two.

I have searched everywhere but all the solutions I've found are for autocomplete or something similar. I'm not looking for an autocomplete solution, just a dropdown that appears under the TextField widget when it is focused, and fills in the text that's selected. The image below is taken from a website, I want to do the same thing for my mobile app.

enter image description here

What I have so far:

  child: Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      OutlinedButton(
        style: OutlinedButton.styleFrom(
          primary: Colors.red,
          backgroundColor: Colors.white,
          side: const BorderSide(color: Colors.red, width: 1.0),
          shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(32.0))),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const [Icon(LineIcons.googlePlusG, size: 20.0), SizedBox(width: 8.0), Text('Google 登入')],
        ),
        onPressed: () {
          focusNode.unfocus();
        },
      ),
      OutlinedButton(
        style: OutlinedButton.styleFrom(
          primary: Colors.indigo,
          backgroundColor: Colors.white,
          side: const BorderSide(color: Colors.indigo, width: 1.0),
          shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(32.0))),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const [Icon(LineIcons.facebookF, size: 16.0), SizedBox(width: 8.0), Text('Facebook 登入')],
        ),
        onPressed: () {
          focusNode.unfocus();
        },
      ),
      Container(margin: const EdgeInsets.symmetric(vertical: 16.0), color: Colors.grey.shade300, height: 1.0),
      Row(
        children: const [
          Expanded(
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 16.0),
              child: Text('帳號', style: MyFonts.inputText),
            ),
          ),
        ],
      ),
      TextFormField(
        validator: RequiredValidator(errorText: 'Username is required'),
        controller: usernameEdit,
        style: MyFonts.inputText,
        keyboardType: TextInputType.text,
        cursorColor: Colors.black,
        cursorHeight: 20.0,
        decoration: InputDecoration(
          isDense: true,
          hintText: '請輸入帳號',
          hintStyle: MyFonts.hintText,
          contentPadding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(32.0),
            borderSide: const BorderSide(color: MyColors.theme),
          ),
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(32.0),
          ),
        ),
        textInputAction: TextInputAction.next,
        onEditingComplete: () => focusNode.nextFocus(),
      ),
      const SizedBox(height: 16.0),
      Row(
        children: const [
          Expanded(
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 16.0),
              child: Text('密碼', style: MyFonts.inputText),
            ),
          ),
        ],
      ),
      TextFormField(
        validator: RequiredValidator(errorText: 'Password is required'),
        controller: pwdEdit,
        style: MyFonts.inputText,
        keyboardType: TextInputType.visiblePassword,
        cursorColor: Colors.black,
        cursorHeight: 20.0,
        obscureText: hidePwd,
        decoration: InputDecoration(
          suffixIcon: IconButton(
            color: Colors.grey,
            onPressed: () => setState(() {
              hidePwd = !hidePwd;
            }),
            icon: Icon((hidePwd) ? Icons.visibility_off : Icons.visibility),
          ),
          isDense: true,
          hintText: '請輸入密碼',
          hintStyle: MyFonts.hintText,
          contentPadding: const EdgeInsets.symmetric(horizontal: 16.0),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(32.0),
            borderSide: const BorderSide(color: MyColors.theme),
          ),
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(32.0),
          ),
        ),
        textInputAction: TextInputAction.next,
        onEditingComplete: () => focusNode.nextFocus(),
      ),
      const SizedBox(height: 16.0),
      Row(
        children: [
          Expanded(
            child: ElevatedButton(
              style: ElevatedButton.styleFrom(
                primary: MyColors.theme,
                shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(32.0))),
              ),
              onPressed: () async {
                var sharedPrefs = await SharedPreferences.getInstance();
                sharedPrefs.setString('username', usernameEdit.text);
                sharedPrefs.setString('pwd', pwdEdit.text);
                focusNode.unfocus();
              },
              child: const Padding(
                padding: EdgeInsets.symmetric(vertical: 8.0),
                child: Text(
                  '登入',
                  style: MyFonts.buttonText,
                ),
              ),
            ),
          ),
        ],
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          TextButton(
              onPressed: () {
                focusNode.unfocus();
                Navigator.pushNamed(context, '/signup');
              },
              child: const Text('加入會員', style: MyFonts.textButtonText)),
          TextButton(onPressed: () {}, child: const Text('忘記密碼?', style: MyFonts.textButtonText)),
        ],
      )
    ],
  )

enter image description here

Upvotes: 0

Views: 762

Answers (1)

Zeeshan Ayaz
Zeeshan Ayaz

Reputation: 1038

use flutter package flutter_typeahead

Demo:

enter image description here

sample code:

Padding(
    padding: EdgeInsets.symmetric(horizontal: 16),
    child: TypeAheadField<Story>(
          noItemsFoundBuilder: (value) {
             return ListTile(
               title: Text('no Story Found'));
            },
          hideOnEmpty: false,
          getImmediateSuggestions: true,
          textFieldConfiguration: TextFieldConfiguration(
          autofocus: true,
          controller: storyTextController,
          style: TextStyle(
                 fontFamily: 'Roboto',
                 fontSize: 25,
                 fontWeight: FontWeight.bold,
                 color: ThaqalaynColors.colorPurple),
          decoration: InputDecoration(
                 border: InputBorder.none,
                 hintText: 'search',
                 hintStyle: TextStyle(
                            fontFamily: 'Roboto',
                            fontSize: 25,
                            fontWeight: FontWeight.bold,
                            color: ThaqalaynColors.colorPurple),
                   ),
            ),
          suggestionsCallback: (String pattern) async {
                 return pattern.isNotEmpty
                        ? storyController.storyList
                                .where((item) => item.title
                                    .toLowerCase()
                                    .contains(pattern.toLowerCase()))
                                .toList()
                        : storyController.storyList.getRange(0, 3).toList();
                  },
          itemBuilder: (context, Story suggestion) {
                  return ListTile(
                      title: Text(suggestion.title),
                    );
                  },
          onSuggestionSelected: (Story suggestion) {
                    storyTextController.text = suggestion.title;
                    print("Suggestion selected ${suggestion.title}");
                    
                  },
                ),
              ),

Upvotes: 3

Related Questions