Search code examples
flutterapidartwidgetyoutube-api

How to update an API request and a widget that displays the response in dart?


here is my problem:
I'm coding an application in Flutter that retrieves content from a TextFormField, makes a request to the youtube API with that retrieved content, and displays the received data in a widget. The problem is that as soon as I launch the application, the TextFormField is empty and without me pressing the search button, the API receives a request and therefore returns the results (results of an empty search). I don't have too much problem with this, I solved it by just adding a visibility widget, but the problem is that I can't refresh the API request (and therefore the results display) when I click on the search button. I'm a beginner on Flutter and despite all my research I didn't manage to implement well the setState widget which seems to be the solution to my problem. Here is my code :

TextEditingController searchController = TextEditingController();


Future<Video> fetchVideo() async {
  final response = await http
      .get(Uri.parse('https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelType=any&eventType=completed&maxResults=3&q=${searchController.text}&safeSearch=none&type=video&videoLicense=any&videoSyndicated=any&videoType=any&key=[my api key]'));

  if (response.statusCode == 200) {
    return Video.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to load video');
  }
}


//classe API Youtube v.3 Search
class Video {
  final String title0;
  final String author0;
  final String thumbnail0;

  const Video({
    required this.title0,
    required this.author0,
    required this.thumbnail0,
  });

  factory Video.fromJson(Map<String, dynamic> map) {
    return Video(
      title0: map['items'][0]['snippet']['title'],
      author0: map['items'][0]['snippet']['channelTitle'],
      thumbnail0: map['items'][0]['snippet']['thumbnails']['medium']['url'],


    );
  }
}


class AddPage extends StatefulWidget {
  const AddPage({super.key});

  @override
  State<AddPage> createState() => _AddPageState();
}

class _AddPageState extends State<AddPage> {
  late Future<Video> futureVideo;
  bool isVisible=false;

  @override
  void initState() {
    super.initState();
    futureVideo = fetchVideo();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       ....

and later on in the code:

Row(
  children: [
    Container(
      color: Colors.black,
      width: 320.0,
      height: 55.0,
      padding: const EdgeInsets.fromLTRB(55.0, 0.0, 0.0, 0.0),
      child: TextFormField(
        controller: searchController,
        decoration: InputDecoration(
            fillColor: Colors.white,
            filled: true,
            labelText: 'Search the song (accurately)',
            enabledBorder: OutlineInputBorder(
              borderSide: const BorderSide(width: 1, color: Colors.black38),
              borderRadius: BorderRadius.circular(10),
            ),
            focusedBorder: OutlineInputBorder(
              borderSide: const BorderSide(width: 1, color: Colors.black38),
              borderRadius: BorderRadius.circular(10),
            ),
        ),
      ),
    ),


    Container( 
      width: 59.0,
      height: 55.0,
      color: Colors.black,
      padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
      child: ElevatedButton.icon(
        style: ElevatedButton.styleFrom(
          primary: Colors.black,
          onSurface: Colors.black,
        ),
        icon: const Icon(
          Icons.search,
          color: Colors.white,
        ),
        label: const Text(
            'Check',
            style: TextStyle(
              fontSize: 0.0,
            ),
        ),
        onPressed: () {
          setState(() {
            isVisible=true;
          });
        },
      ),
    ),
  ],
),


Solution

  • If you want that when the SetState(() {}) is executed, the FutureBuilder rebuild so it gets a new snapshot.data, just use your fetch method directly in the future property like this :

    FutureBuilder() {
      future: fetchVideo(),
      /* builder more code */ 
    }
    

    now every time the setState is executed it will rebuild the whole FutureBuilder, in other words it will will fetch a new data based on changes the TextField have