Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I trigger the FieldSuggestion dropdown to display all options when I click on the input field without entering anything? #61

Open
yuruotong1 opened this issue Nov 25, 2024 · 8 comments

Comments

@yuruotong1
Copy link

How can I trigger the FieldSuggestion dropdown to display all options when I click on the input field without entering anything?

@theiskaa
Copy link
Owner

Hey!
You can define a BoxController() and assign that to the FieldSuggestion widget.
Then you can use like controller.open() anywhere you want.

For more information about external control you can check:
https://github.com/theiskaa/field_suggestion/wiki/External-Control

@yuruotong1
Copy link
Author

yuruotong1 commented Nov 25, 2024

Hey! You can define a BoxController() and assign that to the FieldSuggestion widget. Then you can use like controller.open() anywhere you want.

For more information about external control you can check: https://github.com/theiskaa/field_suggestion/wiki/External-Control

Thank you very much for your response! I did exactly as you suggested, but when I open BoxController, it shows a blank page with no content inside!
image

below is my code:

image
image

@theiskaa
Copy link
Owner

Yeah that's normal since the state doesn't have anything inside to display. You have to modify the search algorithm probably in order to achive something like that. But in any case i'll give you an example tomorrow.

@yuruotong1
Copy link
Author

Yeah that's normal since the state doesn't have anything inside to display. You have to modify the search algorithm probably in order to achive something like that. But in any case i'll give you an example tomorrow.

I printed "hello" in the search, but when I open the BoxController, it doesn't print "hello". I feel like the search logic is not being triggered.

@theiskaa
Copy link
Owner

This is how you can keep the suggestion box visible when the field is focused:
I set the text in the field to a space whenever it gains focus. In the search method, I return true if the field is focused, ensuring the suggestion box stays open. Once an item is selected, the box is hidden, and the selected email is filled in the text field

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  final boxController = BoxController();
  final textController = TextEditingController();
  final focusNode = FocusNode();

  @override
  void initState() {
    focusNode.addListener(
      () => setState(() {
        if (focusNode.hasFocus) textController.text = ' ';
      }),
    );
    super.initState();
  }

  final suggestions = <UserModel>[
    UserModel(email: '[email protected]', username: 'Johnny', password: '1234567'),
    UserModel(email: '[email protected]', username: 'Charlie', password: 'test123'),
    UserModel(email: '[email protected]', username: 'Batu', password: 'test123')
  ];

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        boxController.close?.call();
        FocusScope.of(context).requestFocus(FocusNode());
      },
      child: Scaffold(
        appBar: AppBar(title: const Text("FieldSuggestion Example")),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(15),
          child: Center(
            child: Column(
              children: [
                FieldSuggestion<UserModel>(
                  focusNode: focusNode,
                  inputDecoration: const InputDecoration(hintText: 'Email'),
                  inputType: TextInputType.emailAddress,
                  textController: textController,
                  suggestions: suggestions,
                  boxController: boxController,
                  search: (item, input) {
                    if (focusNode.hasFocus) return true;

                    // Disable box, if item selected.
                    if (item.email == input) return false;

                    return item.email.toString().toLowerCase().contains(input.toLowerCase());
                  },
                  separatorBuilder: (_, __) => const Divider(),
                  itemBuilder: (BuildContext context, int index) {
                    return GestureDetector(
                      onTap: () {
                        setState(() => textController.text = suggestions[index].email!);

                        textController.selection = TextSelection.fromPosition(
                          TextPosition(offset: textController.text.length),
                        );
                      },
                      child: Card(
                        child: ListTile(
                          title: Text(suggestions[index].username!),
                          subtitle: Text(suggestions[index].email!),
                          trailing: IconButton(
                            icon: const Icon(Icons.clear),
                            onPressed: () {
                              suggestions.removeAt(index);
                              boxController.refresh?.call();
                            },
                          ),
                        ),
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

@yuruotong1
Copy link
Author

yuruotong1 commented Nov 26, 2024

Thank you for you response. When I click on the suggested item that pops up, the tap method is not triggered.

import 'package:flutter/material.dart';
import 'package:namer_app/utils/back_http.dart';
import 'package:namer_app/utils/storage.dart';
import 'package:field_suggestion/field_suggestion.dart';

class UploadTab extends StatefulWidget {
  UploadTab({super.key});

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

class SuggestionModel {
  final String? tittle;
  final String? content;
  const SuggestionModel({this.tittle, this.content});
}

class UploadTabState extends State<UploadTab> {
  final TextEditingController textController = TextEditingController();
  List<SuggestionModel> suggestions = [];
  final textControllerNetworkTw = TextEditingController();
  final boxController = BoxController();
  int selectedIndex = -1;
  final focusNode = FocusNode();
  final TextEditingController deadlineController = TextEditingController();
  @override
  void initState() {
    super.initState();
    _initializeData();
    focusNode.addListener(() {
      if (focusNode.hasFocus) {
        setState(() {
          textControllerNetworkTw.text = " ";
       });
      }
    });
  }

  Future<void> _initializeData() async {
    final response = await BackHttp.post('get_history', {
      "user_id": await Storage.getData('user_id')
    });
    if (response["status"] == 0) {
      setState(() {
        suggestions = [
          for (var e in response["content"]) SuggestionModel(tittle: e["tittle"], content: e["content"])
        ];
      });
    }
  }


  
  Widget _buildAttributePanel() {
    return Row(
        children: [
                  Expanded(
                    child: DropdownButtonFormField<String>(
                      decoration: InputDecoration(
                        labelText: '任务状态',
                        border: OutlineInputBorder(),
                      ),
                      value: '未开始',
                      items: ['未开始', '进行中', '已完成', '延期']
                          .map((status) => DropdownMenuItem(
                                value: status,
                                child: Text(status),
                              ))
                          .toList(),
                      onChanged: (value) {
                        // Handle change
                      },
                    ),
                  ),
                  Expanded(
                    child: DropdownButtonFormField<String>(
                      decoration: InputDecoration(
                      labelText: '优先级',
                      border: OutlineInputBorder(),
                    ),
                    value: '中',
                    items: [
                      DropdownMenuItem(
                        value: '高',
                        child: Text('高', style: TextStyle(color: Colors.red)),
                      ),
                      DropdownMenuItem(
                        value: '中',
                        child: Text('中', style: TextStyle(color: const Color.fromARGB(255, 225, 206, 30))),
                      ),
                      DropdownMenuItem(
                        value: '低',
                        child: Text('低', style: TextStyle(color: Colors.green)),
                      ),
                    ],
                    onChanged: (value) {
                      // Handle change
                    },
                  ),
                ),
                Expanded(
                  child: TextField(
                      controller: deadlineController,
                    readOnly: true,
                    decoration: InputDecoration(
                      labelText: '截止时间',
                      border: OutlineInputBorder(),
                    ),
                    onTap: () async {
                      DateTime? pickedDate = await showDatePicker(
                        context: context,
                        initialDate: DateTime.now(),
                        firstDate: DateTime(2000),
                        lastDate: DateTime(2101),
                        locale: Locale('zh', 'CN'),
                      );
                      if (pickedDate != null) {
                        setState(() {
                          deadlineController.text = pickedDate.toLocal().toString().split(' ')[0];
                        });
                      }
                    },
                  ),
                ),
        ],
      );
  }
  
  @override
  Widget build(BuildContext context) {

    return Column(
      children: [
        Center(
          child: SizedBox(
            width: 300,
            child: FieldSuggestion<SuggestionModel>(
              inputDecoration: InputDecoration(
                labelText: '分类',
                hintText: '分类', // optional
              ),
              textController: textControllerNetworkTw,
              boxController: boxController,
              suggestions: suggestions,
              focusNode: focusNode,
              itemBuilder: (context, item) {
                return GestureDetector(
                  onTap: () {
                    print(suggestions[item].tittle);
                    setState(() {
                      textControllerNetworkTw.text = suggestions[item].tittle!;
                      textController.text = suggestions[item].content!;
                    });
                    boxController.close?.call();
                  },
                  child: Card(
                    color: selectedIndex == item ? Colors.blue : null,
                    child: Text(suggestions[item].tittle ?? "")
                  )
                );
              },
              search: (item, input) {
                if (focusNode.hasFocus && input == " ") return true;
                return item.tittle?.toLowerCase().contains(input.toLowerCase()) ?? false;
              },
            ),
          ),
        ),
        SizedBox(height: 20),
         _buildAttributePanel(), 
        SizedBox(height: 20),
        Expanded(
            child: TextField(
            maxLines: 300,
            controller: textController,
            decoration: const InputDecoration(
              border: OutlineInputBorder(),
              hintText: '内容',
                ),
              ),
        ), 
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(onPressed: () {
                textController.clear();
                textControllerNetworkTw.clear();
            }, child: const Text('清空')),
            ElevatedButton(onPressed: () async {
              final response = await BackHttp.post('save', {
                "user_id": await Storage.getData('user_id'),
                "content": textController.text,
                "tittle": textControllerNetworkTw.text
              });
              if (response["status"] == 0) {
                if (!context.mounted) return;
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('保存成功'))
                );
              }
            }, child: const Text('保存'))
          ],
        )
      ],
    );
  }

}

@theiskaa
Copy link
Owner

You've to controll that state, if something is selected, you should not append a empty text to the controller, that's the problem in that case.

@yuruotong1
Copy link
Author

yuruotong1 commented Nov 26, 2024

You've to controll that state, if something is selected, you should not append a empty text to the controller, that's the problem in that case.

Sorry, I didn’t quite understand. Could you please specify which line of code you are referring to? Could you explain it in more detail?

I think the problem is focusNode.The problem is solved When i delete focusNode:

FieldSuggestion<SuggestionModel>(
              inputDecoration: InputDecoration(
                labelText: '分类',
                hintText: '分类', // optional
              ),
              textController: textControllerNetworkTw,
              boxController: boxController,
              suggestions: suggestions,
              focusNode: focusNode,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants