How to use Future<bool> in Widget - flutter

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!

Related

Flutter - how can i use use stream controller without reloading the stream itself

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 send data from an api from one route to another in flutter

I have a built an app using flutter that fetches information from an api and it displays it in a list tile. I would like to enable the ontap function so that whenever anyone taps on an item in the list tile it opens another page and displays that information
This is the list tile code
itemCount: prospectList.data.length,
itemBuilder: (context, i) {
final x = prospectList.data[i];
return ListTile(
title: Text(x.firstname),
subtitle: Text(x.lastname),
leading: CircleAvatar(
backgroundColor: Colors.green,
child: Text(x.firstname[0],
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
)),
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomerInfo())),
);
I get this error
The following NoSuchMethodError was thrown building CustomerInfo(dirty, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#5a50d]]):
The getter 'data' was called on null.
Receiver: null
Tried calling: data
The relevant error-causing widget was
CustomerInfo
The api call
Future<String> _fetchData() async {
setState(() => loading = true);
final response = await http.get('run.mocky.io/v3/ad6092cd-3b2d-4b62-92f1-4198f697f3d3');
if (response.statusCode == 200) {
final datas = jsonDecode(response.body);
final prospectListFromJson = ProspectList.fromJson(datas);
setState(() {
prospectList = prospectListFromJson;
loading = false;
});
} else {
print(response.statusCode);
}
}
#override
void initState() {
super.initState();
_fetchData();
}
I would suggest using FutureBuilder.
Future<ProspectList> _fetchData() async {
final response = await http.get('run.mocky.io/v3/ad6092cd-3b2d-4b62-92f1-4198f697f3d3');
if (response.statusCode == 200) {
var datas;
datas = jsonDecode(response.body);
final prospectListFromJson = ProspectList.fromJson(datas);
return prospectListFromJson;
} else {
return null;
}
}
FutureBuilder(
future: _fetchData(),
builder: (BuildContext context, AsyncSnapshot<ProspectList> snapshot){
if(snapshot.hasData){
prospectList = snapshot.data;
if(prospectList != null){
return List.separated...
}
else{
// Show some error message...
}
}
else if(snapshot.hasError){
// Show some error message...
}
return CircularProgressIndicator();
}
)
If you use FutureBuilder, you won't have to wonder if the data has been loaded or not.

flutter A build function returned null

I am trying to use FutureBuilder but its showing error of A build function returned null
My code
class _EventsState extends State<Events> {
#override
Future<List> doSomeAsyncStuff() async {
final storage = new FlutterSecureStorage();
String value = await storage.read(key: 'token');
print(value);
String url = 'http://sublimeapi.netcodesolution.com/api/NewsAndEvents/';
String token = value;
final response = await http.get(url, headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
print('Token : ${token}');
var eventData = json.decode(response.body);
print(eventData["Data"]);
List _events = eventData["Data"];
return _events;
}
#override
Widget build(BuildContext context) {
double statusBarHeight = MediaQuery
.of(context)
.padding
.top;
return Expanded(
child: FutureBuilder(
future: doSomeAsyncStuff(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
List<Widget> children;
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
print('working');
print(snapshot.data);
return Container(
child: Column(
children: <Widget>[
Text('working')
],
),
);
}
}
}),
);
}
As you can see in code I am fetching data from API and its working fine. In code i print the value of _events in setState its also printing the value like this
I/flutter (32627): [{Id: 38, Description: Two days Workshop on Prevention of Suicide organized for GPs of Thar., ImagePath: /journals/2e8a55f3-6612-4b23-a0ea-e91022c159a8.pdf, CreatedBy: 4447, CreatedOn: 2019-09-18T14:56:13.357, Active: false, Type: Events, Attachment: null, AttachmentType: Events}
I need to print the Description value of this data in future widget but don't know why its showing error
The Error says it clearly! It returned null.
So you have to return something! Do something like this,
Future<List> doSomeAsyncStuff() async {
final storage = new FlutterSecureStorage();
String value = await storage.read(key: 'token');
print(value);
String url = 'http://sublimeapi.netcodesolution.com/api/NewsAndEvents/';
String token = value;
final response = await http.get(url, headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
print('Token : ${token}');
var eventData = json.decode(response.body);
print(eventData["Data"]);
List _events = eventData["Data"];
return _events;
}
and also, we missed another case here.
Scaffold(
appbar: AppBar(
title: const Text('Sample Future Builder'),
),
body: Expanded(
child: FutureBuilder(
future: doSomeAsyncStuff(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
print('working');
print(snapshot.data);
return Container(
child: Column(
children: <Widget>[
Text('working')
],
),
);
}
}
return Center(child: Text("Not Loaded Yet!!"),)
}
),
),
);
Hope that solves your issue!
Tip: Move all your widgets under Scaffold. It would be the best practice. Refer this

Circular progress indicator not showing while using future builder

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

how to convert Stream<T> to return only T?

I have a function named checkAuth() in my APIService class which checks whether there is token in my SharedPreferences. if there is a token it returns AuthenticatedState or else it returns NotAuthenticatedState. Running the below code doesn't have any AuthenticationState at the start. so I tried to add the checkAuth() in the seeded but it throws an error that Stream<AuthenticationState> can't be assigned to AuthenticationState.
How can I convert Stream<AuthenticationState> to AuthenticationState?
BehaviorSubject<AuthenticationState> _authStatus =
BehaviorSubject<AuthenticationState>();
Stream<AuthenticationState> get loginStream => _authStatus;
submit() async {
final String validEmail = _email.value;
final String validPassword = _password.value;
APIService.login(validEmail, validPassword)
.then((onValue) {
if (onValue is Success) {
_authStatus.add(AuthenticatedState());
} else {
_authStatus.add(NotAuthenticatedState());
}
});
}
This is for UI
return StreamBuilder(
stream: stream.loginStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
width: double.infinity,
height: double.infinity,
color: Theme.of(context).cardColor,
child: Center(
child: CircularProgressIndicator(
valueColor: const AlwaysStoppedAnimation(Colors.white),
),
),
);
}
if (snapshot.hasError || snapshot.data is NotAuthenticatedState) {
return Login();
}
if (snapshot.data is AuthenticatedState) {
return App();
}
return Container(width: 0, height: 0);
},
);
It doesn't display any thing on the screen because it doesn't have a value at start , I think so ...
I think you have to user StreamBuilder to get T form Stream<T>, because of the time cost, It's take a time when read data with SharedPreferences.
StreamBuilder<T>( /// T represent the type of fetched data, Assuming it's String
stream: /// put the stream var in there or function with stream as a return
builder:(BuildContext context, AsyncSnapshot<T> snapshot){
if(snapshot.hasData) /// Check If it finishes fetching the data
retrun Text( snapshot.data ); /// snapshot.data variable store the data that fetched
}
);
Check this page form more:
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
/// Update answer
You have to initial the data on SharedPreferences with:
SharedPreferences.getInstance().then((SharedPreferences sp) {
bool _testValue = sharedPreferences.getBool(spKey);
// will be null if never previously saved
if (_testValue == null) {
_testValue = false;
// set an initial value
}
});