ListView is not showing the result if empty list - flutter

I am loading data from a remote api:
This is the dart file that provides the connection and download:
clinica-api.dart
import 'package:flutter_capenergy/modelos/clinica.dart';
import 'package:http/http.dart' as http;
Future<List<Clinica>> fetchClinicas(String idUsuario) async {
String url ="https://..flutter_api/get_clinicas.php";
final response = await http.get(url);
if (response.body == "[]"){
}
return clinicaFromJson(response.body);
}
And this is the piece of code from misclinicas.dart where I am showing the list:
Expanded(
child: Container(
child: FutureBuilder(
future: fetchClinicas(miId),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, index) {
print(index.toString());
Clinica clinica = snapshot.data[index];
return new GestureDetector(
onTap: () {
clinicaProvider.setClinica(clinica.nombreClinica);
clinicaProvider.setClinicaId(clinica.idClinica);
} ,
child: new Card(
elevation: 6,
child: new Column(
children: [
new Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment
.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment
.center,
mainAxisAlignment: MainAxisAlignment
.center,
children: <Widget>[
Image.network(
'https://.../${clinica
.logoClinica}',
height: 180,
alignment: Alignment.center,),
],
),
Text(
'${clinica.nombreClinica}',
style: TextStyle(fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue),
),
Text(
'${clinica.direccionClinica}',
style: TextStyle(fontSize: 14,
color: Colors.grey,
fontStyle: FontStyle.italic),
),
],
),
),
],
),
),
);
},
);
}
else {
Text ("NO HAY CLINICAS");
}
return Text("Cargando clínicas");
},
),
),
),
If there are items on the list, they are shown, but if the list is empty I would like to show a text with a message reporting that the list is empty.
I am trying to do it putting this text widget if snapshot.hasdata is false:
Text ("NO HAY CLINICAS");
but it is not shown, I am only getting a blank listView.

In the empty list case, snapshot.hasData will be true and snapshot.data.length will be 0.
snapshot.hasData == false means it's either loading or an error has happened.

in the api call return empty list if response.body ="[]"
if (response.body == "[]"){
List<Clinica> emptyClinica = [];
return emptyClinica;
}
in misclinicas.dart
snapshot.data.lenth > 0 ? your list work : Text('No Data Found')

Related

How to fix Image overflow in Flutter PageView?

I'm building an app where it shows the title, author name, number of upvotes, and an image from the subreddit in a page view. Everything is working fine but for some images, the page view is overflowing, how do I fix this?
Here's the overflow error:
Here's my code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class Stardew extends StatefulWidget {
const Stardew({ Key? key }) : super(key: key);
#override
State<Stardew> createState() => _StardewState();
}
class _StardewState extends State<Stardew> {
List data = [];
Future<String> getData() async {
List temp_data = [];
var response = await http.get(
Uri.parse("https://m...content-available-to-author-only...p.com/gimme/stardewvalley/100")
);
return response.body;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.data == null){
return Center(child: CircularProgressIndicator(color: Color(0xff008b00)));
}
var jsonData = jsonDecode(snapshot.data);
jsonData = jsonData["memes"];
return PageView.builder(
//scrollDirection: Axis.vertical,
itemCount: jsonData.length,
itemBuilder: (BuildContext context, int index){
return Center(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
getImgCard(
jsonData[index]["title"],
//jsonData[index]["preview"][2],//preview image
jsonData[index]["url"], //original image
jsonData[index]["author"],
(jsonData[index]["ups"]).toString()
)
],
),
),
);
},
);
}
);
}
Widget getImage(String imgUrl){
return Container(
child: Image.network(
imgUrl,
fit: BoxFit.scaleDown,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded/loadingProgress.expectedTotalBytes! : null,
color: Color(0xff008b00),
),
);
},
),
);
}
Widget getImgCard(String title, String imgUrl, String author, String ups){
return Card(
color: Color(0xff000000),
clipBehavior: Clip.antiAlias,
child: Column(
children: [
ListTile(
leading: RichText(
text: TextSpan(
children: [
TextSpan(
text: ups,
),
const WidgetSpan(
child: Icon(Icons.arrow_upward, size: 18, color: Color(0xff008b00),),
)
],
),
),
title: Text(title, style: TextStyle(color: Colors.white),),
subtitle: Text(
"Posted by u/${author}",
style: TextStyle(color: Colors.white.withOpacity(0.6)),
),
),
getImage(imgUrl),
Padding(padding: EdgeInsets.only(bottom: 8))
],
),
);
}
}
How do I fix this? I have tried changing the box fit and it did not work. Then I used expaned and flexible widgets and still can't find the answer to this solution. please help me.
Wrap getImage(imgUrl) inside Expanded widget.
I found the answer myself, removing the parent column and wrapping it with SingleChildScrollView fixed the error.
return PageView.builder(
//scrollDirection: Axis.vertical,
itemCount: jsonData.length,
itemBuilder: (BuildContext context, int index){
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: getImgCard(
jsonData[index]["title"],
//jsonData[index]["preview"][2],//preview image
jsonData[index]["url"], //original image
jsonData[index]["author"],
(jsonData[index]["ups"]).toString()
),
)
);
},
);

how to fetch data correctly with flutter

i have problem with fetch data from database (firestore)
im fetching data from firestore and listing them with listview.builder, when a card is tapped it routes to new page that show all the details of tapped item.
on the screenshot i have 2 item listed with header,body and date when i tapped on item routes to new page and fetch from firestore rest of the data (client,start date,finish date etc.)
my question is what is the best way to fetch tapped items datas
my idea is store somehow items store unique id to the builded item and when it tapped route to new page with id and query with id
my code block
Widget listener(Stream<QuerySnapshot> tasks) {
return Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: tasks,
builder: (
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (snapshot.hasError) {
return Center(
child: Text(
"something went wrong.",
style: TextStyle(
color: ColorConstants.instance.headerColor, fontSize: 20),
textAlign: TextAlign.center,
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.data!.size > 0) {
final data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: (context, index) => buildNotificationCards(
context,
data.docs[index]['header'],
data.docs[index]['body'],
data.docs[index]['startDate']));
} else {
return Center(
child: SizedBox(
width: MediaQuery.of(context).size.width * .8,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
LineIcons.fileAlt,
size: 60,
color: ColorConstants.instance.headerColor,
),
SizedBox(
height: 20,
),
Text(
'Maalesef Kayıtlı Bir Veri Bulunamadı',
textAlign: TextAlign.center,
style: TextStyle(
color: ColorConstants.instance.headerColor,
fontSize: 20,
height: 2),
),
]),
),
);
}
},
));
}
The normal practice is under ListView.builder, you create a ListTile and on its onTap argument, you use the Navigator to switch to another page for the detail view:
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailPage(data[index])),
);
where data[index] is the particular element of the data list you want to display the contents of.

How to handle the uniqueness of widget in flutter?

I have a project. I am trying to make a scanner. Everything is going well except for previews of the images.
This how preview should like this. I manage the make the view of this screenshot. But the problem is that I couldn't manage to make unique for every title. I hold my titles in location table and my images in image table. Image table has location id so that in every location has different images from each other. But my code is overriding all the locations so they are showing same images of one location. Like this:
:
Only first one has a picture but my code show like they have all the same image.
This is my part of the homescreen code:
future: future,
builder: (ctx, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: Consumer<Titles>(
builder: (ctx, titles, ch) => Expanded(
child: ListView.builder(
controller: ScrollController(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: titles.items.length,
itemBuilder: (ctx, i) { if (searchString=="") {
getimages(titles.items[i].id);
print(list_of_images);
print("yukarıda");
return TitleList(titleList:titles.items[i],images:list_of_images);
}
getimages(titles.items[i].id);
return titles.items[i].title!.contains(searchString) ? TitleList(titleList:titles.items[i],images:list_of_images) :Container();
}
I am sending the title and images to TitleList widget:
I am getting the images like this :
List<File> list_of_images=[];
void getimages(id) {
print("fark ne kadar");
Future<List<Map<String, dynamic>>> futureTasks = DBHelper.selectImageforlist(id); //first make the querylist_of_images
futureTasks.then((data){
print("heyoo");
print(data);
for(int i = 0; i < data.length; i++) {
list_of_images.add(File(data[i]['Image']));
}
});
}
And this is the part of the TitleListWidget code:
var titleList;
List<File> images;
TitleList({required this.titleList,required this.images});
return GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(
ImageScreen.routeName,
arguments: {'id': titleList.id, 'name': titleList.title},
);
},
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(top: 15, left: 12),
child: Row(
children: [
Container(
child: Text(
truncate(titleList.title, length: 7),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
Container(
padding: EdgeInsets.only(left: 15),
child: Text(
getdate(titleList.date),
style: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.white),
)),
],
),
),
Container(
padding: EdgeInsets.only(left: 10),
child: Row(children: [
Stack(
overflow: Overflow.visible,
children: makephoto(
images.length, images),
),
]),
)
],
),
I used map. I took every image to Map. The key of the image is the FileId.
Future<Map> getimages() async {
print("fark ne kadar");
Future<List<Map<String, dynamic>>> futureTasks = DBHelper.selectImageforlist(); //first make the querylist_of_images
futureTasks.then((data){
print("heyoo");
print(data);
for(int i = 0; i < data.length; i++) {
if (identifier.containsKey(data[i]['fileId'])){
identifier[data[i]['fileId']].add(File(data[i]['Image']));
}
else {
identifier[data[i]['fileId']]=[File(data[i]['Image'])];
}
}
});
return identifier;
}

Refresh Indicator does not update a list after deleting data

I have a FutureBuilder getting specific items from Firestore and returning them in a list. To update this list I added RefreshIndicator. When something is added to the list and I refresh it, it works normally and the item appears. The problem occurs when I remove an item from the list, it loads but the list remains the same.
This is my Widget:
#override
void initState() {
super.initState();
userFuture = getCollectionItems();
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Container(
margin: EdgeInsets.only(top: 5.5),
padding: EdgeInsets.only(left: 17, right: 17),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Collection",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
],
),
),
createGridAndList(),
Expanded(
child: RefreshIndicator(
onRefresh: () => getCollectionItems(),
child: displayCollection(),
),
)
],
),
);
}
When I restart the app or go to another page and come back with pushNamedRemoveUntil the list updates properly, this indicates that the query is working.
getCollectionItems() and displayCollection():
getCollectionItems() async {
QuerySnapshot querySnapshot = await Firestore.instance
.collection("users")
.document(_userId)
.collection("userCollection")
.getDocuments();
List<Users> collectionID = [];
for (DocumentSnapshot item in querySnapshot.documents) {
var data = item.data;
Users user = Users();
user.id = data["id"];
collectionID.add(user);
}
final collectionIDs = collectionID.map((doc) => doc.id).toList();
var splitCollection = partition<dynamic>(collectionIDs, 10);
for (int i = 0; i < splitCollection.length; i++) {
QuerySnapshot querySnapshotCollections = await Firestore.instance
.collection('items')
.where('itemId', whereIn: splitCollection.elementAt(i))
.orderBy('timestamp', descending: true)
.getDocuments();
setState(() {
countItem = querySnapshotCollections.documents.length;
itemsList = querySnapshotCollections.documents
.map((documentSnapshot) =>
CollectionItem.fromDocument(documentSnapshot))
.toList();
});
}
}
displayCollection() {
return FutureBuilder(
future: userFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Padding(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>
(Colors.grey),
),
width: 20.0,
height: 20.0,
),
);
}
if (itemsList == null) {
return Container(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>
(Colors.grey),
),
width: 20.0,
height: 20.0,
),
);
} else if (itemsList.isEmpty) {
return ListView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: [
Center(
child: Container(
padding: EdgeInsets.only(
top: MediaQuery.of(context).size.width * 0.50,
left: 17,
right: 17),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Nothing here.",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 13,
color: Color(0xff9e9999),
fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
),
],
),
),
),
],
);
} else if (itemOrientation == "grid") {
List<GridTile> gridTilesList = [];
itemsList.forEach((eachItem) {
gridTilesList.add(GridTile(child:
CollectionItemTile(eachItem)));
});
return GridView.count(
crossAxisCount: 2,
padding: EdgeInsets.fromLTRB(10, 15, 10, 0),
childAspectRatio: 3 / 2,
mainAxisSpacing: 15,
crossAxisSpacing: 10,
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: gridTilesList,
);
} else {
return Container(
padding: EdgeInsets.only(bottom: 15),
child: ListView(
padding: EdgeInsets.all(0.0),
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: itemsList),
);
}
});
}
I've tried several things, switched to Stream (it didn't work), added another setState to the Widget itself, rebuilt the classes but the problem persists.
Hmmm, your displayCollection widget is displaying data based on userFuture, but halfway through you using itemList instead, and your onRefresh function is updating the itemList but not userFuture.
I won't do exactly like you do, but i refactored a bit.
You can try something like this, i didn't test it but let me know if it works 😊
// I changed `userFuture` to `futureItems`
Future<List<CollectionItem>> futureItems;
#override
void initState() {
super.initState();
futureItems = getCollectionItems();
}
Future<List<CollectionItem>> getCollectionItems() async {
// ... Do your query here
return querySnapshotCollections.documents.map((documentSnapshot) {
return CollectionItem.fromDocument(documentSnapshot);
}).toList();
}
Future<void> refreshCollectionItems() async {
setState(() {
// This will update the futureItems
futureItems = getCollectionItems();
});
}
Widget displayCollection() {
return FutureBuilder<List<CollectionItem>>(
future: futureItems, // The data returned will be inside `snapshot` below
builder: (context, snapshot) {
if (snapshot?.hasData ?? false) {
List<CollectionItem> items = snapshot.data; // This is the return value from `futureItems`
return RefreshIndicator(
onRefresh: refreshCollectionItems,
child: ListView.builder(
itemCount: items.length, // This is how to get the length, so no need to use `countItem`
itemBuilder: (context, index){
CollectionItem item = items[index];
return // ...Display your widget with item data
},
),
);
}
return // Display widget to handle loading/error/no data
},
);
}
Plus it is important to define the return type of a function so that you will know what you will get after executing a function.
One of the simplest ways to solve this is re-setting the state onRefresh.
Expanded(
child: RefreshIndicator(
onRefresh: () {
getCollectionItems();
setState(() {
userFuture = getCollectionItems();
});
},
child: displayCollection(),
),
),
Your Firestore query might be reading from cache. Try disable persistence. Official Tutorial
FirebaseFirestore.instance.settings = Settings(persistenceEnabled: false);
Or if want to clear any persisted data, you can call the clearPersistence() method.
await FirebaseFirestore.instance.clearPersistence();

Problem pageview reload first page after setState ( Flutter )

I have a code, this code create a pageview about some user, data is get from firebase
return new Scaffold(
appBar: new AppBar(
title: new Text("Carousel"),
),
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('users').snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new CircularProgressIndicator();
default:
return new PageView(
onPageChanged: _onPageViewChange,
controller: _controller,
scrollDirection: Axis.horizontal,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return new Column(
children: <Widget>[
new Container(
child: new ClipOval(
child: new CachedNetworkImage(
width: 150.0,
height: 150.0,
imageUrl: document['img'],
fit: BoxFit.fill,
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error),
)),
),
new ListTile(
title: new Text(
isPerson
? 'My name is'
: (isPlace
? 'My favourite is'
: (isNote
? 'I am from'
: (isPhone
? 'My phone is'
: (isLock ? '' : '')))),
textAlign: TextAlign.center),
subtitle: new Text(
isPerson
? document['name']
: (isPlace
? document['place']
: (isNote
? document['note']
: (isPhone
? document['phone']
: (isLock
? document['lock'].toString()
: "")))),
textAlign: TextAlign.center,
),
),
buildButton1(Icons.person)
],
);
}).toList(),
);
}
},
));
}
this is fuction buildButton1()
Widget buildButton1(IconData icon) {
return new Column(
children: <Widget>[
new Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 20.0),
child: new IconButton(
icon: Icon(icon),
onPressed: () {
setState(() {
//isChecked ? true : false;
isPerson = true;
isNote = false;
isPlace = false;
isPhone = false;
isLock = false;
});
},
iconSize: 32.0,
color: isPerson ? Colors.green : Colors.grey,
),
)
],
);
}
When I press a button to set variable then Pageview reload and show firstpage. How can I solved this problem. This is example picture https://imgur.com/nKC358E
................................................................................................
The issue comes from the _onPageViewChange function.
The last page doesn't return an integer value. If you have 3 pages, than the last returned index will be 1.99999999999... and not 2.
I solved the problem like this
onPageChanged: (index){
setState(() {
if (index > 1.99){
lastPage=true;
}else{
lastPage=false;
}
});
}