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
Related
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 have created a demo project for showing orders using FutureBuilder but Its not showing order, instead its giving infinite loop , so where should I correct my code
Why it is infinite?
herewith I am sending code for my provider method to fetch orders and the code where I am using it
or is there any other better option to replace future builder..
Future<void> fetchandsetorders() async {
print('I am fetchandsetorders method of provider');
final url = Uri.parse(
mylink);
final response = await http.get(url);
final List<OrderItem> loadedorders = [];
final Map<String, dynamic> extradeddata = json.decode(response.body) as Map<String, dynamic>;
extradeddata.forEach((orderid, orderdata) {
loadedorders.add(
OrderItem(
id: orderid,
products: (orderdata['products'] as List<dynamic>).map((item) {
return CartItem(
id: item['id'],
title: item['title'],
quantity: item['qty'],
price: item['price']);
}).toList(),
amount: orderdata['amount'],
date: DateTime.parse(orderdata['date']),
),
);
});
_orders=loadedorders.reversed.toList();
notifyListeners();
}
class _OrderScreenState extends State<OrderScreen> {
#override
Widget build(BuildContext context) {
final orderdata = Provider.of<Orders>(context);
print('I am buildmethod');
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(Random().nextInt(255),
Random().nextInt(255), Random().nextInt(255), 1),
title: Text('List of orders: ' + orderdata.ordercount.toString()),
),
drawer: AppDrawer(),
body: FutureBuilder(
future: Provider.of<Orders>(context, listen: false).fetchandsetorders(),
builder: (context,snapshop){
if(snapshop.connectionState==ConnectionState.waiting)
{
return Center(child: CircularProgressIndicator());
}
else
{
if(snapshop.error!=null)
{
return Text(snapshop.error.toString());
}
else
{
return ListView.builder(
itemCount: orderdata.ordercount,
itemBuilder: (context, index) {
return OrderItemWidget(
order: orderdata.orders[index],
);
});
}
}
},
),
);
}
}
create a state variable for Future like
late myFuture = Provider.of<Orders>(context, listen: false).fetchandsetorders(),
And use on
body: FutureBuilder(
future: myFuture,
I'm trying to display the featured image from a Wordpress api using the following code:
home.dart:
class _PostTileState extends State<PostTile> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(children: [
FutureBuilder(
future: fetchWpPostImageUrl(widget.href),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Image.network(snapshot.data["guid"]["rendered"]);
}
return CircularProgressIndicator();
},
),
Text(widget.title),
Text(widget.desc)
]));
}
}
wp-api.dart:
Future fetchWpPostImageUrl(href) async {
final response =
await http.get(href, headers: {"Accept": "application/json"});
var convertedDatatoJson = jsonDecode(response.body);
return convertedDatatoJson;
}
The code executes at the CircularProgressIndicator but does not display the image despite the map being correct.
I solved the issue:
Future fetchWpPostImageUrl(href) async {
final response =
await http.get(Uri.parse(href), headers: {"Accept": "application/json"});
var convertedDataJson = jsonDecode(response.body);
return convertedDataJson;
}
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!
EDIT:
Since the data is now showing if I do hot reload after the error. I am assuming that my other Futurethat isn't included in my FutureBuilder triggers that error.
HERE ARE THE ACTUAL FUNCTIONS:
List<Feed> feedList = [];
List<User> userList = [];
#override
void initState() {
super.initState();
getFeed = getFeedAll();
this.getUsers();
}
Future<List<Feed>> getFeedAll() async {
var res = await http.get(
Uri.encodeFull(APIServices.httpDomain + APIServices.postGetAll),
headers: {"Authorization": "Bearer " + Constants.token});
if (res.statusCode == 200) {
var data = json.decode(res.body);
this.getUsers();
feedList = data.map<Feed>((json) => Feed.fromJson(json)).toList();
}
return feedList;
}
Future<List<User>> getUsers() async {
var res = await http.get(
Uri.encodeFull(APIServices.httpDomain + APIServices.usersAll),
headers: {"Authorization": "Bearer " + Constants.token});
if (res.statusCode == 200) {
var data = json.decode(res.body);
userList = data.map<User>((json) => User.fromJson(json)).toList();
}
return userList;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getFeed,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Container(
padding: EdgeInsets.all(10),
child: feedListWidget(snapshot.data));
} else {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: Container(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
),
);
}
},
);
}
Now, I am using userList inside my feedListWidget widget.
I am pretty sure that getUsers() triggers that error.
How can I include getUsers() inside my FutureBuilder?
Because that's what I think will solve my problem.
Seems like you are not waiting for the data here:
getFeed = getFeedAll();
this.getUsers();
Use await to perform that
getFeed = await getFeedAll();
await this.getUsers();