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

selectedValueWidgetFn creates an error. #108

Closed
phranklins opened this issue Feb 18, 2023 · 11 comments
Closed

selectedValueWidgetFn creates an error. #108

phranklins opened this issue Feb 18, 2023 · 11 comments

Comments

@phranklins
Copy link

I'm using selectedValueSingleDialogPagedFuture and would like to make items and futureSearchFn look the same just like the ask here: #103. Using the code in the example or the suggested code in the link (selectedValueWidgetFn: (item) {return DropdownMenuItem(child: (Text(item["capital"])));}), I receive the error "Expected a value of type 'int', but got one of type 'String'". If I remove selectedValueWidgetFn, the code works fine.

@lcuis
Copy link
Owner

lcuis commented Feb 19, 2023

Hi @phranklins ,

Thanks for raising this issue.

I tried again the code example in #103 which seems to be the closest I could get to your situation and I got no issue running with latest version of the plugin.

Could it be that the error occurs because your data is different?

If I had to guess, I would suspect that your items are indexed with integers and not strings. In data terms, they are a list while the example code expects a map. However, without the code, it is hard to confirm.

Do you have some code you could share so that I can try to investigate further?

@phranklins
Copy link
Author

I'm actually pretty much using your example code as is so it's weird that I'm running into issues while you aren't. The biggest differences between your code and mine are:

  • I removed the filter and sorting options since I don't need them
  • added a proxy to your example URL to make it work in web
  • I removed the web conditions since with the proxy, it should work in web
  • I had to replace value: item, with value: item["capital"],. If I didn't I would receive, the message Error: Expected a value of type 'String', but got one of type '_JsonMap'. That seems expected since you mention it should be a map, but if I left it that way, I always got the error and the only way past it was to change value the way that I did
  • I replaced selectedValueWidgetFn with the code in items and futureSearchFn have different styles #103 , but I would receive the error Expected a value of type 'int', but got one of type 'String' regardless of whether I used this code or the code in the example.

Here is basically the code I have for the widget:

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:search_choices/search_choices.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:http/http.dart';

class SearchCapitals extends StatefulWidget {
  const SearchCapitals({
    Key? key,
    this.width,
    this.height,
  }) : super(key: key);

  final double? width;
  final double? height;

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

class _SearchCapitalsState extends State<SearchCapitals> {
  @override
  String? selectedValueSingleDialogPagedFuture;
  PointerThisPlease<int> currentPage = PointerThisPlease<int>(1);

  Widget build(BuildContext context) {
    return SearchChoices.single(
      icon: const Icon(Icons.keyboard_arrow_down_rounded),
      displayClearIcon: false,
      padding: 4,
      value: selectedValueSingleDialogPagedFuture,
      hint: Text(
        "Search capitals",
        style: TextStyle(
            color: black,
            fontWeight: FontWeight.w300),
      ),
      searchHint: Text(
        "Search capitals",
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
      underline: Container(
        height: 0,
      ),
      onChanged: (value) {
        setState(() {
          selectedValueSingleDialogPagedFuture = value;
        });
      },
      isExpanded: true,
      itemsPerPage: 15,
      currentPage: currentPage,
      selectedValueWidgetFn: (item) {
        return DropdownMenuItem(child: (Text(item["capital"])));
      },
      displayItem: (item, selected) {
        return (Row(children: [
          selected
              ? Icon(
                  Icons.radio_button_checked,
                  color: Colors.grey,
                )
              : Icon(
                  Icons.radio_button_unchecked,
                  color: Colors.grey,
                ),
          SizedBox(width: 7),
          Expanded(
            child: item,
          ),
        ]));
      },
      futureSearchFn: (String? keyword, String? orderBy, bool? orderAsc,
          List<Tuple2<String, String>>? filters, int? pageNb) async {
        String filtersString = "";
        int i = 1;
        filters?.forEach((element) {
          filtersString += "&filter" +
              i.toString() +
              "=" +
              element.item1 +
              "," +
              element.item2;
          i++;
        });
        Response response = await get(Uri.parse(
                "https://corsproxy.io/?https://searchchoices.jod.li/exampleList.php?page=${pageNb ?? 1},10${orderBy == null ? "" : "&order=" + orderBy + "," + (orderAsc ?? true ? "asc" : "desc")}${(keyword == null || keyword.isEmpty) ? "" : "&filter=capital,cs," + keyword}$filtersString"))
            .timeout(Duration(
          seconds: 10,
        ));
        if (response.statusCode != 200) {
          throw Exception("failed to get data from internet");
        }
        dynamic data = jsonDecode(response.body);
        int nbResults = data["results"];
        List<DropdownMenuItem> results = (data["records"] as List)
            .map<DropdownMenuItem>((item) => DropdownMenuItem(
                  value: item["capital"],
                  child:
                      Text("${item["capital"]} - ${item["country"]} - ${item["continent"]} - pop.: ${item["population"]}"),
                ))
            .toList();
        return (Tuple2<List<DropdownMenuItem>, int>(results, nbResults));
      },
      searchDelay: 500,
      futureSearchRetryButton: (Function onPressed) => Column(children: [
        SizedBox(height: 15),
        Center(
          child: ElevatedButton.icon(
              onPressed: () {
                onPressed();
              },
              icon: Icon(Icons.refresh),
              label: Text("Enter at least 3 characters\n or click to retry")),
        )
      ]),
    );
  }
}

@lcuis
Copy link
Owner

lcuis commented Feb 20, 2023

Thanks for the additional details!

I cannot run your example right now.

Do you have a specific line/character pointed at with the "got a String while expecting an int" error?

If you run in debug mode with a breakpoint on this line, does the data look as expected?

@phranklins
Copy link
Author

Yeah the data looks as expected, but for some reason I feel the search choices code is what's blocking it. I wish the post had line numbers. Basically I've experienced two errors:

  1. For code line 98, I receive the error Error: Expected a value of type 'String', but got one of type '_JsonMap' if the line is value: item,. I was able to get passed the error by changing the line to value: item["capital"], As you mentioned, I ideally do want a map and when I look at the data, I see a map. But the error is saying it wants a string, which is why I changed it.
  2. For lines 53-55 for selectedValueWidgetFn, I receive the error Expected a value of type 'int', but got one of type 'String'. Not sure why it's expecting an int since I haven't specified it as such. If I delete these lines, the code works just fine except that the styling doesn't match the other dropdowns in the app.

@lcuis
Copy link
Owner

lcuis commented Feb 20, 2023

Your code turns the item as in the value: item["capital"], part into a String. It is already the capital.
So, running this code instead worked for me:

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:search_choices/search_choices.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:http/http.dart';

class SearchCapitals extends StatefulWidget {
  const SearchCapitals({
    Key? key,
    this.width,
    this.height,
  }) : super(key: key);

  final double? width;
  final double? height;

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

class _SearchCapitalsState extends State<SearchCapitals> {
  @override
  String? selectedValueSingleDialogPagedFuture;
  PointerThisPlease<int> currentPage = PointerThisPlease<int>(1);

  Widget build(BuildContext context) {
    return SearchChoices.single(
      icon: const Icon(Icons.keyboard_arrow_down_rounded),
      displayClearIcon: false,
      padding: 4,
      value: selectedValueSingleDialogPagedFuture,
      hint: Text(
        "Search capitals",
        style: TextStyle(
            color: black,
            fontWeight: FontWeight.w300),
      ),
      searchHint: Text(
        "Search capitals",
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
      underline: Container(
        height: 0,
      ),
      onChanged: (value) {
        setState(() {
          selectedValueSingleDialogPagedFuture = value;
        });
      },
      isExpanded: true,
      itemsPerPage: 15,
      currentPage: currentPage,
      selectedValueWidgetFn: (item) {
        return DropdownMenuItem(child: (Text(item)));
      },
      displayItem: (item, selected) {
        return (Row(children: [
          selected
              ? Icon(
                  Icons.radio_button_checked,
                  color: Colors.grey,
                )
              : Icon(
                  Icons.radio_button_unchecked,
                  color: Colors.grey,
                ),
          SizedBox(width: 7),
          Expanded(
            child: item,
          ),
        ]));
      },
      futureSearchFn: (String? keyword, String? orderBy, bool? orderAsc,
          List<Tuple2<String, String>>? filters, int? pageNb) async {
        String filtersString = "";
        int i = 1;
        filters?.forEach((element) {
          filtersString += "&filter" +
              i.toString() +
              "=" +
              element.item1 +
              "," +
              element.item2;
          i++;
        });
        Response response = await get(Uri.parse(
                "https://corsproxy.io/?https://searchchoices.jod.li/exampleList.php?page=${pageNb ?? 1},10${orderBy == null ? "" : "&order=" + orderBy + "," + (orderAsc ?? true ? "asc" : "desc")}${(keyword == null || keyword.isEmpty) ? "" : "&filter=capital,cs," + keyword}$filtersString"))
            .timeout(Duration(
          seconds: 10,
        ));
        if (response.statusCode != 200) {
          throw Exception("failed to get data from internet");
        }
        dynamic data = jsonDecode(response.body);
        int nbResults = data["results"];
        List<DropdownMenuItem> results = (data["records"] as List)
            .map<DropdownMenuItem>((item) => DropdownMenuItem(
                  value: item["capital"],
                  child:
                      Text("${item["capital"]} - ${item["country"]} - ${item["continent"]} - pop.: ${item["population"]}"),
                ))
            .toList();
        return (Tuple2<List<DropdownMenuItem>, int>(results, nbResults));
      },
      searchDelay: 500,
      futureSearchRetryButton: (Function onPressed) => Column(children: [
        SizedBox(height: 15),
        Center(
          child: ElevatedButton.icon(
              onPressed: () {
                onPressed();
              },
              icon: Icon(Icons.refresh),
              label: Text("Enter at least 3 characters\n or click to retry")),
        )
      ]),
    );
  }
}

If you wish to keep the item as a full result from the json, you will need to keep the value set as item instead of item["capital"] as follows:

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:search_choices/search_choices.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:http/http.dart';

class SearchCapitals extends StatefulWidget {
  const SearchCapitals({
    Key? key,
    this.width,
    this.height,
  }) : super(key: key);

  final double? width;
  final double? height;

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

class _SearchCapitalsState extends State<SearchCapitals> {
  @override
  String? selectedValueSingleDialogPagedFuture;
  PointerThisPlease<int> currentPage = PointerThisPlease<int>(1);

  Widget build(BuildContext context) {
    return SearchChoices.single(
      icon: const Icon(Icons.keyboard_arrow_down_rounded),
      displayClearIcon: false,
      padding: 4,
      value: selectedValueSingleDialogPagedFuture,
      hint: Text(
        "Search capitals",
        style: TextStyle(
            color: black,
            fontWeight: FontWeight.w300),
      ),
      searchHint: Text(
        "Search capitals",
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
      underline: Container(
        height: 0,
      ),
      onChanged: (value) {
        setState(() {
          selectedValueSingleDialogPagedFuture = value;
        });
      },
      isExpanded: true,
      itemsPerPage: 15,
      currentPage: currentPage,
      selectedValueWidgetFn: (item) {
        return DropdownMenuItem(child: (Text(item["capital"])));
      },
      displayItem: (item, selected) {
        return (Row(children: [
          selected
              ? Icon(
                  Icons.radio_button_checked,
                  color: Colors.grey,
                )
              : Icon(
                  Icons.radio_button_unchecked,
                  color: Colors.grey,
                ),
          SizedBox(width: 7),
          Expanded(
            child: item,
          ),
        ]));
      },
      futureSearchFn: (String? keyword, String? orderBy, bool? orderAsc,
          List<Tuple2<String, String>>? filters, int? pageNb) async {
        String filtersString = "";
        int i = 1;
        filters?.forEach((element) {
          filtersString += "&filter" +
              i.toString() +
              "=" +
              element.item1 +
              "," +
              element.item2;
          i++;
        });
        Response response = await get(Uri.parse(
                "https://corsproxy.io/?https://searchchoices.jod.li/exampleList.php?page=${pageNb ?? 1},10${orderBy == null ? "" : "&order=" + orderBy + "," + (orderAsc ?? true ? "asc" : "desc")}${(keyword == null || keyword.isEmpty) ? "" : "&filter=capital,cs," + keyword}$filtersString"))
            .timeout(Duration(
          seconds: 10,
        ));
        if (response.statusCode != 200) {
          throw Exception("failed to get data from internet");
        }
        dynamic data = jsonDecode(response.body);
        int nbResults = data["results"];
        List<DropdownMenuItem> results = (data["records"] as List)
            .map<DropdownMenuItem>((item) => DropdownMenuItem(
                  value: item,
                  child:
                      Text("${item["capital"]} - ${item["country"]} - ${item["continent"]} - pop.: ${item["population"]}"),
                ))
            .toList();
        return (Tuple2<List<DropdownMenuItem>, int>(results, nbResults));
      },
      searchDelay: 500,
      futureSearchRetryButton: (Function onPressed) => Column(children: [
        SizedBox(height: 15),
        Center(
          child: ElevatedButton.icon(
              onPressed: () {
                onPressed();
              },
              icon: Icon(Icons.refresh),
              label: Text("Enter at least 3 characters\n or click to retry")),
        )
      ]),
    );
  }
}

But I guess there must be a reason why you chose to set the value as item["capital"].

@phranklins
Copy link
Author

The reason I set value as item["capital"] is what I mention in point 1 of my previous comment. If I set it to just value: item like you did, I get an error that says Expected a value of type 'String', but got one of type '_JsonMap'

@lcuis
Copy link
Owner

lcuis commented Feb 20, 2023

Oh, indeed, you explained that. Sorry, I didn't pay enough attention.
Yet, I don't see why you get this issue for now.
Is it fine for you to run with the first solution mentioned in my previous comment?

@phranklins
Copy link
Author

Yep. Appreciate that. Just tried that and it did work. So for now, will make do with this for now. Thank you!

@lcuis
Copy link
Owner

lcuis commented Feb 20, 2023

Great, thanks!
I hope you don't mind if I close this issue for now.
Don't hesitate to reopen if needed.

@lcuis lcuis closed this as completed Feb 20, 2023
@phranklins
Copy link
Author

FYI, I figured out why I was getting that first error with string vs. jsonmap. It was a stupid mistake. You can see above that I have the String? selectedValueSingleDialogPagedFuture; which should be dynamic? selectedValueSingleDialogPagedFuture;. I was using selectedValueSingleDialog elsewhere in my code and only replaced the latter half of the this line instead of the updating the whole thing.... Given that, updated the code to reflect that mistake and everything is working as it should.

@lcuis
Copy link
Owner

lcuis commented Feb 22, 2023

Thank you very much for the explanation @phranklins . I'm glad it is working now fine for you!

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