Flutter: When the snapshot is empty the widget disappears - flutter

I am trying to build a search list, the list is working fine but if the result is empty i need to show no data. I tried the following code but the widget holds for a second and then disappear
FutureBuilder(
future: getSearchedProducts(widget.searchString),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: Container(
child: Text('No Data Found.'),
),
);
} else {
return ListView.builder(
shrinkWrap: true,
itemCount: searchResult.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Image.network(searchResult[index].proThumbnail),
title: Text(searchResult[index].proName),
onTap: () {
print(searchResult[index].proName);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductPage(prodid: searchResult[index].proId);
}));
},
),
);
});
}
})
Can anyone help me with this.
Thank you in advance.

I just write the code as below and it works.
FutureBuilder(
future: getSearchedProducts(widget.searchString),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('length of list ${searchResult.length}');
if (searchResult.length==0) {
return Center(
child: Text('No data'),
);
}
else if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else {
return ListView.builder(
shrinkWrap: true,
itemCount: searchResult.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Image.network(searchResult[index].proThumbnail),
title: Text(searchResult[index].proName),
onTap: () {
print(searchResult[index].proName);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductPage(prodid: searchResult[index].proId);
}));
},
),
);
});
}
}),

In your Code in Else part before - return ListView.builder- add the following code.
...
else {
if (searchResult.length == 0)
{
return Center(child: const Text('No Date'));
}
return ListView.builder .....

Related

FutureBuilder and Services function

I am using future builder with service function like this, when getUserProfile function
When I get the APIService.getUserProfile() funciton return Forbidden(403) error I want to navigate to login page, how can I do that where can I add this navigator function in my code ? How can I detect and return Login Page
body: FutureBuilder(
future: APIService.getUserProfile(),
builder: (
BuildContext context,
AsyncSnapshot<String> model,
) {
if (model.hasData) {
return SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 240,
child: FutureBuilder(
future: getCurrentUser(),
builder: (context, snapshot) {
return ListView.separated(
itemBuilder: (context, index) {
print(index);
if (username == null) {
return Text(
""); //GetUserName(documentId: user!.uid);
} else {
print("build");
return MyCard(
balance: balance ?? 0,
creditCardsNumber: 0,
userName: username.toString(),
card: myCards[index],
);
}
},
separatorBuilder: (context, index) {
return SizedBox(width: 10);
},
itemCount: 1,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
);
}),
),
SizedBox(
height: 30,
),
Text(
"Recent Payments",
style: ApptextStyle.BODY_TEXT,
),
SizedBox(
height: 15,
),
FutureBuilder(
future: getCurrentUser(),
builder: (context, snapshot) {
return ListView.separated(
itemBuilder: (context, index) {
if (username != null) {
return TransactionCard(
transaction: myTransactions[index]);
} else {
return Text("");
}
},
separatorBuilder: (context, index) {
return SizedBox(height: 10);
},
itemCount: myTransactions.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
);
})
],
),
),
);
} else if (model.hasError) {
print("HEREEE");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LoginScreen();
},
),
);
return Text("asdasfsdfd");
}
return Text("");
}));

Data from Firestore not displaying on screen (Flutter)

I am trying to asynchronously pull data from firestore in a streambuilder (all within a list view). The problem with the approach I have now is that nothing displays on the screen. I tried setting state (after i check if the screen is mounted, but to no avail).
I know the correct data is being pulled because it prints to the screen, but for some reason, it just does not display on the screen.
here's a sample of my list view below:
Widget _showFriends() {
return Expanded(
child: StreamBuilder<List<Future<AppUser>>>(
stream: db.friendStream(),
builder: (context, snapshot) {
if(snapshot.hasData) {
print("in here");
final friends = snapshot.data!;
var friend;
String username = "";
String firstName = "";
String photoUrl = "";
return ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
friends[index].then((value) {
friend = value;
username = value.username;
firstName = value.firstName;
//photoUrl = value.photoUrl;
print("friend username -- " + username);
});
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context)
=> IndividualProfile(
sourceType: SourceType.friends,
name: friend.firstName,
userName: friend.username,
aboutMe: friend.aboutMe,
photoUrl: friend.photoUrl,
individualUid: friend.uid,
)
));
},
child: ListTile(
title: Text(username),
subtitle: Text(firstName),
/*leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(photoUrl),
),*/
),
);
},
);
}
else if(snapshot.hasError) {
print(snapshot.error.toString());
return const Center(
child: Text("An error occurred please try again later"),
);
}
else {
return const Center(
child: CircularProgressIndicator(),
);
}
}
)
);
}
Pleas let me know any suggestions. I thoroughly appreciate it!
The problem is that friends[index] is a future. You did not await it before returning GestureDetector.
return GestureDetector(...) would be called before friends[index].then callback. Variable friend would still be null when return GestureDetector(...) is called.
The correct way to wait for it would be with a FutureBuilder.
Try this:
Widget _showFriends() {
return Expanded(
child: StreamBuilder<List<Future<AppUser>>>(
stream: db.friendStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final friends = snapshot.data!;
return ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
// here I am awaiting friends[index]'s future
return FutureBuilder<AppUser>(
future: friends[index],
builder:
(BuildContext context, AsyncSnapshot<AppUser> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text(
'An error occurred. Please try again later',
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final friend = snapshot.data!;
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => IndividualProfile(
sourceType: SourceType.friends,
name: friend.firstName,
userName: friend.username,
aboutMe: friend.aboutMe,
photoUrl: friend.photoUrl,
individualUid: friend.uid,
),
),
);
},
child: ListTile(
title: Text(friend.username),
subtitle: Text(friend.firstName),
// leading: CircleAvatar(
// backgroundImage:
// CachedNetworkImageProvider(friend.photoUrl),
// ),
),
);
},
);
},
);
}
if (snapshot.hasError) {
print(snapshot.error.toString());
return const Center(
child: Text('An error occurred please try again later'),
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}

why am not getting data from firestore?

hello I have the list of categories in my firestore i want to fetch them in to a listview it only shows CircularProgressIndicator then after it disapear nothing comes on screen where did get wrong?
Expanded(
child: _appwidget.customeTextFormField(
hint: "Select category",
icon: InkWell(
onTap: () {
showFancyCustomDialog(context,
height: 500,
width: 500,
title: "Add / Select / Delete Category",
widget: FutureBuilder<DocumentSnapshot>(
future: _services.category
.doc("ProductCategories")
.get(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot>
snapshot) {
if (snapshot.hasError) {
return Text('error');
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child:
Center(child: CircularProgressIndicator()),
);
}
return ListView.builder(
itemBuilder:
(BuildContext context,
int index) {
return ListTile(
title: Text(snapshot.data!["Categories"]
[index]["name"]),
);
},
itemCount: snapshot.data!['Categories'] ==
null
? 0
: snapshot.data!['Categories'].length,
);
}),
addButton: Container());
},
child: Icon(Icons.list)))),
You forgot to tackle the condition in which you have data.
Just add if(snapshot.hasData) & return your ListView.builder(), because in other conditions you are not sure whether you have received the data or not.
return Expanded(
child: _appwidget.customeTextFormField(
hint: "Select category",
icon: InkWell(
onTap: () {
showFancyCustomDialog(context,
height: 500,
width: 500,
title: "Add / Select / Delete Category",
widget: FutureBuilder<DocumentSnapshot>(
future: _services.category
.doc("ProductCategories")
.get(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot>
snapshot) {
if (snapshot.hasError) {
return Text('error');
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child:
Center(child: CircularProgressIndicator()),
);
}
if(snapshot.hasData){
return ListView.builder(
itemBuilder:
(BuildContext context,
int index) {
return ListTile(
title: Text(snapshot.data!["Categories"]
[index]["name"]),
);
},
itemCount: snapshot.data!['Categories'] ==
null
? 0
: snapshot.data!['Categories'].length,
);
}
}),
addButton: Container());
},
child: Icon(Icons.list))));

How can I create a listview.builder in column?

I have a problem, this problem is that I want to run listview.builder in column. But when I set the scroll feature to column, the application does not work even though it does not give an error. I have codes below.
return SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
Flexible(
child: buildFutureBuilder(),
),
],
),
);
}
FutureBuilder<MarkaResponse> buildFutureBuilder() {
return FutureBuilder<MarkaResponse>(
future: getData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
break;
default:
if (snapshot.hasError) {
return Center(child: Text("Hata"));
} else {
return ListView.builder(
itemCount: 7,
itemBuilder: (context, index) {
this.response = snapshot.data.liste;
Marka item = response[index];
return ListTile(
title: Text(item.name),
leading: Icon(Icons.motorcycle),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Modeller(
numMarka: item.id,
)));
},
);
});
}
}
},
);
}
SingleChildScrollView forces infinite height, so you can have, Spacers, Expanded,Flexible etc. If you give a fixed height for your ListView it will work
try this:
return SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
Container(height: 50),
Expanded(
child: buildFutureBuilder(),
),
],
),
);
}
FutureBuilder<MarkaResponse> buildFutureBuilder() {
return FutureBuilder<MarkaResponse>(
future: getData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
break;
default:
if (snapshot.hasError) {
return Center(child: Text("Hata"));
} else {
return ListView.builder(
shrinkWrap: true,
itemCount: 7,
itemBuilder: (context, index) {
this.response = snapshot.data.liste;
Marka item = response[index];
return ListTile(
title: Text(item.name),
leading: Icon(Icons.motorcycle),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Modeller(
numMarka: item.id,
)));
},
);
});
}
}
},
);
}
Make a parent container for ListView.builder, and set a height of that container.

Better way to pass selected index in flutter

I have data from firestore
I need to display the name in a single page and the name of content in another page and so episodes
Is there a better way than this
tvshow page
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
tvSelected = index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return SeasonsPage(selectedTv: tvSelected);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[index].name),
),
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
seasons page:
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedTv].content.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
selectedSeason = index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return EpisodesPage(
selectedTv: selectedTv,
selectedSeason: selectedSeason,
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(
snapshot.data[selectedTv].content[index].name),
),
);
},
)
: Center(child: CircularProgressIndicator()),
),
episodes page:
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedTv].content[selectedSeason]
.episodes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[selectedTv]
.content[selectedSeason].episodes[index]),
);
},
)
: Center(child: CircularProgressIndicator()),
),
look at when I pass a route page
.............................
.............................
...........................
Firstly you you create a new provider class that will hold the indices of the currently selected tv,episode and season as shown bellow:
class CurrentIndexProvider with ChangeNotifier {
int _selectedTv;
int _selectedSeason;
int _selectedEpisode;
set selectedTv(int newIndex) {
this._selectedTv = newIndex;
notifyListeners();
}
set selectedSeason(int newIndex) {
this._selectedSeason = newIndex;
notifyListeners();
}
set selectedEpisode(int newIndex) {
this._selectedEpisode = newIndex;
notifyListeners();
}
int get selectedTv => this._selectedTv;
int get selectedSeason => this._selectedSeason;
int get selectedEpisode => this._selectedEpisode;
}
Then your tv shows page becomes:
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
// tvSelected = index; -->we dont need this anylonger
//we set the current show number to the index of the current
//listview item when tapped
selectedItems.selectedSeason=index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return SeasonsPage(
//we don't need to pass anything in the constructor again
// selectedTv: tvSelected
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[index].name),
),
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
your seasons page becomes
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[ selectedItems.selectedTv].content.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
//selectedSeason = index; --> we dont need this any longer
selectedItems.selectedSeason=index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return EpisodesPage(
//we don't need any parameter in the constructor now
// selectedTv: selectedTv,
// selectedSeason: selectedSeason,
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(
snapshot.data[selectedItems.selectedTv].content[index].name),
),
);
},
)
: Center(child: CircularProgressIndicator()),
),
And finally the episodes page becomes
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedItems.selectedTv ].content[selectedItems.selectedSeason]
.episodes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[selectedItems.selectedTv]
.content[selectedItems.selectedSeason].episodes[index]),
);
},
)
: Center(child: CircularProgressIndicator()),
),
With this you can have access to the currently selected tv show or seasons or even episodes anywhere within you code by using provider. hope this helped