I searched in a lot of topics but couldn't find solution to my problem. I try to make lazy loading with FutureBuilder to listview. All topics I found the code different from what I have. I don't want to apply it from a package. Sorry about that but I'm still not a professional in flutter. Can someone help me.
thank you
My code:
class _HomePageState extends State<YourPost> {
#override
void initState() {
super.initState();
}
Future ApiUserPost() async {
final String url = '*************';
var response = await http.post(Uri.parse(url));
var responsebody = jsonDecode(response.body);
if (responsebody.length > 0) {
return responsebody;
} else {
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
FutureBuilder(
future: ApiUserPost(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length.clamp(0, 6),
itemBuilder: (context, index) {
return Card(
// elevation: 1,
child: Container(
padding: EdgeInsets.all(6),
child: Row(
children: <Widget>[
Flexible(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
MergeSemantics(
child: Row(
children: <Widget>[
Flexible(
child: Text(
"${snapshot.data[index]['name']}",
),
)
],
),
),
SizedBox(height: 5),
],
),
)
],
),
),
);
});
} else if (snapshot.hasError) {
Center(
child: CircularProgressIndicator(
valueColor:
new AlwaysStoppedAnimation<Color>(Colors.black),
),);
}
return SizedBox(
height: MediaQuery.of(context).size.height / 1.3,
child: Center(
child: CircularProgressIndicator(
valueColor:
new AlwaysStoppedAnimation<Color>(Colors.black),
),
),
);
})
],
),
),
),
));
}
}
I think the main problem is that you're not indicating the generic types (Class)on some places. When you working with Futures and FutureBuilder you have to specify what type you're working with..
Future<TypeYouExpected> apiUserPost() async {
//Try to specify the type of the variables too
}
The FutureBuilder widget also has to know that type..
body: FutureBuilder<TypeYouExpected>(
future: apiUserPost(),
builder: ..
Related
I want to show API data in the list view.
enter image description here
listview looks like this.
Future<List<Post>> getData() async {
final response = await http.get(
Uri.parse("http://192.168.0.9:8000/api/buildingdata/"),
headers: {"Access-Control-Allow-Origin": "*"});
if (response.statusCode == 200) {
List list = (json.decode(utf8.decode(response.bodyBytes)));
var postList = list.map((element) => Post.fromJson(element)).toList();
return postList;
} else {
throw Exception('Failed to load post');
}
}
class Post {
final String buildingName;
final String buildingLoc;
final String buildingCall;
Post({this.buildingName, this.buildingLoc, this.buildingCall});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
buildingName: json["building_name"],
buildingLoc: json["building_loc"],
buildingCall: json["building_call"],
);
}
I used this code to get API data to the app.
enter image description here
and my API looks like this. (Ignore image for each building)
_makeDataList(List<Map<String, dynamic>> datas) {
return ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 10),
itemBuilder: (BuildContext _context, int index) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Expanded(
child: Container(
height: 100,
padding: const EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
datas[index]["building_name"].toString(),
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15),
),
SizedBox(height: 5),
Text(
datas[index]["building_loc"].toString(),
style: TextStyle(
fontSize: 12, color: Colors.black.withOpacity(0.3)),
),
SizedBox(height: 5),
],
),
),
)
],
),
);
},
separatorBuilder: (BuildContext _context, int index) {
return Container(height: 1, color: Colors.pink.withOpacity(0.4));
},
itemCount: datas.length,
);
}
Widget _bodyWidget() {
return FutureBuilder(
future: postList,
builder: (BuildContext context, dynamic snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Center(
child: Text("Failed to load data"),
);
}
if (snapshot.hasData) {
return _makeDataList(snapshot.data);
}
return Center(child: Text("No data for this location"));
});
}
This is my code I want to use for listview, but this error occurs when I run app.
type 'List<Post>' is not a subtype of type 'List<Map<String, dynamic>>'
When I tried basic listview code(only shows building name), it worked well.
return ListView.builder(
itemCount : snapshot.data.length,
itemBuilder : (context,index){
Post post = snapshot.data[index];
return Card(
child: ListTile(
title:Text(post.buildingName)
)
);
},
);
Well this code is just for checking connection with API, so I don't use this code.
where should I change to fix this error?
You have to change List<Map<String,dynamic>> to List<Post> to your _makeDataList
_makeDataList(List<Post> datas) {
return ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 10),
itemBuilder: (BuildContext _context, int index) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Expanded(
child: Container(
height: 100,
padding: const EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
datas[index].building_name.toString(),
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15),
),
SizedBox(height: 5),
Text(
datas[index].building_loc.toString(),
style: TextStyle(
fontSize: 12, color: Colors.black.withOpacity(0.3)),
),
SizedBox(height: 5),
],
),
),
)
],
),
);
},
separatorBuilder: (BuildContext _context, int index) {
return Container(height: 1, color: Colors.pink.withOpacity(0.4));
},
itemCount: datas.length,
);
}
Widget _bodyWidget() {
return FutureBuilder(
future: postList,
builder: (BuildContext context, dynamic snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Center(
child: Text("Failed to load data"),
);
}
if (snapshot.hasData) {
return _makeDataList(snapshot.data);
}
return Center(child: Text("No data for this location"));
});
}
I am having a hard time figuring out how to get my FilterChips in a row.
I would like to display all FilterChips per category in a row and not in a seperated column.
Tried different approaches but non of them seems to work.
Hopefully someone can help me with this, thanks for help!
Here is my code:
Widget _buildListView(List<Category> categories) {
return Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: categories.length,
itemBuilder: (context, index) {
return _buildSection(categories[index]);
},
),
),
],
);
}
Widget _buildSection(Category category) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
category.title,
style: TextStyle(fontSize: 18),
),
// How can I get my FilterChips side by side?
Row(
children: [
Flexible(
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: category.interests.length,
itemBuilder: (context, index) {
Interest interest = category.interests[index];
return FilterChip(label: Text(interest.interest), onSelected: (isSelected){
selectedInterests.add(interest.id);
});
return Text(interest.interest);
}),
),
],
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: categories,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Category> categories = snapshot.data;
return _buildListView(categories);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
class StackOver extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(
top: 60,
left: 10.0,
right: 10.0,
),
child: Wrap(
children: List.generate(
10,
(index) {
return FilterChip(
label: Text('index $index'),
onSelected: (val) {},
);
},
),
),
),
);
}
}
RESULT:
My problem is like that:
Widget buildBottem(MyCart ordercart) {
//return buildItemsList(ordercart); // this is a Expanded
//return buildPriceInfo(ordercart); //this is a Row
return Container(
child: Column(
children: <Widget>[
buildItemsList(ordercart), //show items ordered
Divider(),
buildPriceInfo(ordercart),
]
)
);
}
In the above code, I can successfully return either buildItemsList(ordercart) or buildPriceInfo(ordercart) from the function with correct results if I uncomment the respective statement. However, if I try to combine both together as a Column, the result is a blank. The function is called within a FutureBuilder:
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.center,
width: double.infinity,
child: Container(
width: 90,
height: 8,
decoration: ShapeDecoration(
shape: StadiumBorder(), color: Colors.black26),
),
),
buildTitle(ordercart),
Divider(),
Container(
child: FutureBuilder<Widget>(
future: retrieveItemsFromFirebase(ordercart),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new CircularProgressIndicator();
default:
if (ordercart.cartItems.length <= 0)
return noItemWidget();
else
return buildBottom(ordercart);
}
}
)
),
SizedBox(height: 8),
]
) //addToCardButton(ordercart, context),
);
}
This is in a Web-Firebase application so it is difficult to debug because every time I have to modify the index.html so that it can use Firebase.
I am including the screenshots:
With 'return buildItemsList(ordercart);'
With 'return buildPriceInfo(ordercart);'
and the code of the two implementations:
Widget buildItemsList(MyCart cart) {
return Expanded(
child: ListView.builder(
itemCount: cart.cartItems.length,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundImage:
NetworkImage(cart.cartItems[index].food.image)),
title: Text('${cart.cartItems[index].food.name}',
style: subtitleStyle),
subtitle: Text('\$ ${cart.cartItems[index].food.price}'),
trailing: Text('x ${cart.cartItems[index].quantity}',
style: subtitleStyle),
),
);
},
),
);
}
and
Widget buildPriceInfo(MyCart cart) {
double total = 0;
for (CartItem cartModel in cart.cartItems) {
total += cartModel.food.price * cartModel.quantity;
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Total:', style: headerStyle),
Text('\$ ${total.toStringAsFixed(2)}', style: headerStyle),
],
);
}
and the implementation of buildTitle(cart):
Widget buildTitle(cart) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Your Order', style: headerStyle),
],
);
}
The reason why the Widgets buildItemsList() and buildPriceInfo() doesn't appear on screen as you expect is because both Widgets doesn't have bounds defined, and therefore could fill the entire screen. What you can do here is set bounds on those Widgets by using either Expanded or Container with defined height.
I cannot seem to get my screen to scroll. I have tried a few variations of the following code but I have not been able to get it to work. I also tried it with a ListView that did not work very well either. Admittedly, I did not try to troubleshoot the ListView for very long because I was assuming the issue was being caused by something else. I have looked on SO and seen a few questions about this topic and they helped me fix some issues, but I cannot seem to get it to work. I do not get any error messages or anything, my screen simply does not scroll. Below you will see the general layout of my code. What am I doing wrong?
class _TripPackagesState extends State<TripPackages> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(...),
GestureDetector(...),
],
),
),
),
Container(
margin: EdgeInsets.only(top: 1.0),
child: SingleChildScrollView(
child: StreamBuilder<QuerySnapshot>(
stream:
Firestore.instance.collection('trip_package').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> docSnapshot) {
if (!docSnapshot.hasData) return const Text('Loading...');
final int docCount = docSnapshot.data.documents.length;
return GridView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: docCount,
itemBuilder: (_, int index) {
DocumentSnapshot document =
docSnapshot.data.documents[index];
return GestureDetector(
child: Container(
margin: EdgeInsets.all(3.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(...),
Row(...),
],
),
),
);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(...),
);
},
),
),
),
],
),
);
}
}
Try using primary: false in your GridView.builder
I am struggling to understand why there is a yellow space between my two widgets... I am using a listView widget which use a snapshot to create a list
Here is my code
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'My least tested groups',
),
Container(
color: Colors.yellowAccent,
child: SchoolList(),
),
],
),
),
),
),
);
}
And this is the Widget
class SchoolList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: myStream1,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text(
'Loading...',
style: kSendButtonTextStyle.copyWith(color: kColorText),
);
default:
return new ListView(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return Container(
height: 100,
width: 100,
color: Colors.white,
child: Text('xxx'));
}).toList(),
);
}
},
);
}
}
Can anyone understand why I have a yellow gap and more importantly how to get rid of it?
I think the ListView trying to applying top padding for status bar.
you can remove it by setting padding: const EdgeInsets.all(0.0) in the ListView widget.