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;
});
},
),
),
],
),
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