I am a noobi in flutter.i was tryin to show circular progress on loading data from an api on a button click. i am getting the response from the api. but i am its not entering inside future builder's snapshot condotions...
_onPressed(){
setState(() {
_textCCodeValues=myCCodeController.text;
_textPhoneNumberValues=myPhoneNumberController.text;
_textPasswordValues=myPasswordController.text;
log("mobile",name: _textCCodeValues+" "+_textPhoneNumberValues);
log("Password",name: _textPasswordValues);
requestMap={
'ccode':_textCCodeValues,
'mobile':_textPhoneNumberValues,
'password':_textPasswordValues,
'app_Version':"1.1",
};
//_isOtpFieldVisible =!_isOtpFieldVisible;
requestJson=json.encode(requestMap);
//StringWidget(future: makeLoginRequest(requestJson));
//makeLoginRequest(requestJson);
});
FutureBuilder(
future: makeLoginRequest(requestJson),
builder: (BuildContext context,AsyncSnapshot snapshot){
_logData(snapshot.connectionState.toString());
if(snapshot.connectionState == ConnectionState.waiting){
_logData("in waiting");
return SpinKitRotatingCircle(
color: Colors.blue,
size: 50.0,
);
}
return null;
},
);
}
the future that i am using is
Future<String> makeLoginRequest(String requestJson) async{
final uri = url;
final headers = {
'Content-Type': 'application/json',
"Accept": "application/json",
};
var response=await post(
uri,
headers:headers,
body: requestJson,
);
print("${response.body}");
return response.toString();
}
I dont know what went wrong. please someone help me.
Just try changing your code like,
FutureBuilder(
future: makeLoginRequest(requestJson),
builder: (BuildContext context,AsyncSnapshot snapshot){
if (snapshot.hasData) {
return Text("Data");
} else if(snapshot.hasError) {
return Text("Error");
} else {
return SpinKitRotatingCircle(
color: Colors.blue,
size: 50.0,
);
}
},
);
Related
I want to fetch data from api and showing on app screen but there nothing on screen but a circular progress indicator. if i try to print response.body on consol it works just fine and showing the body of data in consol but it not showing anything to app screen. weher is the problem of my code???
I try to change itemcount: snapshot.data?.length, but didnt do anything. if i want to print api data on consol it works fine. but not showing to screen.
class _MyHomePageState extends State<MyHomePage> {
List<ApiModel> apiModel = [];
final url = "https://newsapi.org/v2/top-headlines?country=us&category=sports&apiKey=bb66e3f0405944a58cb6853e55995bec";
Future<List<ApiModel>> getData() async {
final response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body.toString());
if (response.statusCode == 200) {
for (Map<String, dynamic> index in data) {
apiModel.add(ApiModel.fromJson(index));
}
return apiModel;
} else {
return apiModel;
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: apiModel.length,
itemBuilder: (context, index){
return Container(
height: 200,
child: Column(
children: [
Text.rich(TextSpan(children: [
TextSpan(text: 'data'),
TextSpan(text: 'data'),
]))
],
),
);
});
}else{
return Center(child: CircularProgressIndicator(),);
}
});
}
}
Your data is not correctly mapped in you ApiModel because you mapping in wrong way so update your getData() with below code.
Future<ApiModel> getData() async {
try {
final response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body.toString());
if (response.statusCode == 200) {
apiResposne = ApiModel.fromJson(data);
return apiResposne;
} else {
return apiResposne;
}
} catch (ex) {
return apiResposne;
}
}
Also you need to update FutureBuilder UI widget part to update data so replace below code.
FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: apiResposne.articles?.length ?? 0,
itemBuilder: (context, index) {
return SizedBox(
height: 200,
child: Column(
children: [
Text.rich(TextSpan(children: [
TextSpan(
text:
(apiResposne.articles?[index].author ?? "")),
TextSpan(
text: (apiResposne.articles?[index].publishedAt ??
"")),
]))
],
),
);
});
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
})
StreamController<UserModel> _controller = StreamController<UserModel>.broadcast();
getFriendsName() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString("token");
var username = prefs.getString("username");
final response = await http
.post(Uri.parse("http://192.168.0.111:3000/friendNames"),
headers: {
"Content-Type": "application/json",
"authorization": "$token"
},
body: jsonEncode({"username": username}))
.then((value) => value)
.catchError((e) => print(e));
UserModel usermodel = UserModel.fromJson(json.decode(response.body));
return _controller.sink.add(usermodel);
//return usermodel;
}
i created an infinite loop that reload data every 0 second
void initState() {
Timer.periodic(Duration(seconds: 0), (_) => getFriendsName());
super.initState();
}
here is the stream builder
StreamBuilder<UserModel>( /
stream: _controller.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data!.msg == "no friends to chat with") {
return Center(child: Text("No friends found."));
} else {
return ListView.builder(
itemCount: snapshot.data!.msg.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data!.msg[index]),
subtitle:
Text("${snapshot.data!.msg1![index]}"),
leading: CircleAvatar(
backgroundColor: Colors.orange,
backgroundImage: NetworkImage(
"http://192.168.0.111:3000/getImage/${snapshot.data!.msg[index]}?v=${Random().nextInt(100)}",
),
),
onTap: () async {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) {
return (ChatRoom(
snapshot.data!.msg[index]));
}));
},
);
});
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
What im asking for is a way to use streambuilder and listen to changes without the need of looping the stream infinitly.
so any propositions
i solved the problem by changing Timer to Stream and adding as .asBroadcastStream()
and it should look like this
return Stream.periodic(Duration(seconds: 0))
.asyncMap((event) => getFriendsName0()).asBroadcastStream();
I want to get some data from a movie API but after I reload my screen, It gives me an error of bad state no element although I dont get any compilation error. Below is the method which i used to fetch the data from the api
Please NB: I am using the provider state management
Future<void> loadMovies() async {
const _apiKey = '*****************************';
const _readAccessToken =
'***************************************';
List<Movie> loadedTrendingMovies = [];
TMDB tmdbWithCustomLogs = TMDB(
ApiKeys(_apiKey, _readAccessToken),
logConfig: const ConfigLogger(
showLogs: true,
showErrorLogs: true,
),
);
try {
final trendingMoviesResponse = await tmdbWithCustomLogs.v3.trending
.getTrending() as Map<String, dynamic>;
final discoverMoviesResponse = await tmdbWithCustomLogs.v3.discover
.getMovies() as Map<String, dynamic>;
final trendingReults = trendingMoviesResponse['results'] as List;
for (var movieData in trendingReults) {
loadedTrendingMovies.add(Movie(
id: movieData['id'].toString(),
title: movieData['original_title'] ?? 'Loading...',
description: movieData['overview'] ?? 'Loading...',
rate: movieData['vote_average'],
releaseDate: movieData['release_date'],
imageUrl: movieData['poster_path'],
));
}
_trendingMovies = loadedTrendingMovies;
notifyListeners();
} catch (error) {
print(error);
rethrow;
}
print(_trendingMovies.last.title);
}
Also below is the future builder which listens to changes in my provider
FutureBuilder(
future: Provider.of<Movies>(context, listen: false).loadMovies(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Text(snapshot.error.toString()); //This error does not even appears on the screen as text but so that means, the code breaks before making it here.
} else {
return Consumer<Movies>(
builder: (context, movieData, _) => Swiper(
itemBuilder: (BuildContext context, i) {
return MovieContainer(
imageUrl: movieData.trendingMovies[i].imageUrl,
id: movieData.trendingMovies[i].id,
rate: movieData.trendingMovies[i].rate,
title: movieData.trendingMovies[i].title,
);
},
itemCount: movieData.trendingMovies.length,
viewportFraction: 0.25,
scale: 0.4,
),
);
}
}),
final String apiUrl = 'https://api.covid19api.com/summary';
Future globaldata() async{
var res = await http.get(Uri.parse(apiUrl));
Map s = await jsonDecode(res.body);
return s['Global']['NewConfirmed'];
}
//....
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("global cases"),
centerTitle: true,
),
body:Container(
child: FutureBuilder(
future: globaldata(),
builder: (context, snapshot){
if(snapshot.data != null){
print(globaldata());
return Text(globaldata().toString());
}else{
return Text("loading ...");
}
},
),
),
);
}
}
I am getting error of Instance of 'future'
Try doing these
child: FutureBuilder(
future: globaldata(),
builder: (context, snapshot) {
if(snapshot.data != null){
print(snapshot.data);
return Text(snapshot.data);
}else{
return Text("loading ...");
}
},
),
),
);
Check Print and return Text statement
You get this error because you are printing and returning Futures without await:
print(globaldata());
return Text(globaldata().toString());
Your FutureBuilder provides you the values you are trying to access in snapshot:
print(snapshot.data);
return Text(snapshot.data.toString());
Change your function to this.
Future globaldata() async {
http.Response response = await http.get(Uri.parse(apiUrl));
if (response.statusCode == 200) {
return jsonDecode(response.body)['Global']['NewConfirmed'];
} else {
throw Exception('Failed to load post');
}
}
and change your body to this.
body: FutureBuilder(
future: globaldata(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: Text(snapshot.data.toString()),
);
} else if (snapshot.hasError) {
return Center(
child: Text("${snapshot.error}"),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
This works perfectly.
I have a Future function in my Provider Repository. However it is Future<bool> since I need async for http request.
Future<bool> hasCard() async {
String token = await getToken();
var body = jsonEncode({"token": token, "userID": user.getUserID()});
var res = await http.post((baseUrl + "/hasCard"), body: body, headers: {
"Accept": "application/json",
"content-type": "application/json"
});
print(res.toString());
if (res.statusCode == 200) {
this.paymentModel = PaymentModel.fromJson(json.decode(res.body));
return true;
}
return false;
}
And in my Widget I want to check this value:
Widget build(BuildContext context) {
var user = Provider.of<UserRepository>(context);
if(user.hasCard())
{
//do something
}
But I get an error message:
Conditions must have a static type of 'bool'.dart(non_bool_condition)
Since it is a Widget type I cannot use async here. What could be the way to solve this?
You can use a FutureBuilder, it will build the widget according to the future value, which will be null until the future is completed. Here is an example.
FutureBuilder(
future: hasCard(),
builder: (context, snapshot) {
if (snapshot.data == null)
return Container(
width: 20,
height: 20,
child: CircularProgressIndicator());
if (snapshot.data)
return Icon(
Icons.check,
color: Colors.green,
);
else
return Icon(
Icons.cancel,
color: Colors.red,
);
},
)
Well not only for a Future<bool> for any other future you can use the FutureBuilder where the future is what returns you the type of future and snapshot is the data recieved from it.
FutureBuilder(
future: hasCard(),
builder: (context, snapshot) {
if (snapshot.data != null){
print(snapshot.data)}
else{
print("returned data is null!")}
},
)
and I would suggest assigning a default value to your bool.
good luck!