Flutter: How to solve type 'List<NetworkImage>' is not a subtype of type 'Widget' - firebase

When i am trying to run the code for connecting carousel with Firestore
the error is showing like this '' 'List' is not a subtype of type 'Widget' '' or if there is any other way to connect cloud firestore with carousel images so that i can change my images using firestore.
class About extends StatelessWidget {
List<NetworkImage> _listOfImages = <NetworkImage>[];
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
appBar: AppBar(
title: Text(
'About',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(
color: Colors.black, //change your color here
),
),
body: ListView(
scrollDirection: Axis.vertical,
children: <Widget>[
Padding(
padding: EdgeInsets.only(
left: SizeConfig.safeBlockHorizontal * 5,
top: SizeConfig.safeBlockHorizontal * 5,
right: SizeConfig.safeBlockHorizontal * 5),
child: Material(
borderRadius: BorderRadius.circular(24.0),
child: SizedBox(
width: SizeConfig.safeBlockHorizontal * 80,
height: SizeConfig.safeBlockHorizontal * 100,
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('About').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
_listOfImages = [];
for (int i = 0;
i < snapshot.data.documents[index].data['image'].length;
i++
)
{
_listOfImages.add(NetworkImage(snapshot
.data.documents[index].data['image'][i]));
}
return Carousel(
boxFit: BoxFit.contain,
dotBgColor: Colors.transparent,
dotIncreasedColor: Colors.grey,
dotSize: 6.0,
images: [
_listOfImages
],

You are facing that error as you already have declared
List<NetworkImage> _listOfImages = <NetworkImage>[];
which mens _listOfImages have List type of NetworkImage
And you need to pass List type to parameter to images argument in your Carousel widget.
So you are doing it as
images: [
_listOfImages
],
Mens you are passing 2D array in that argument by mistake.
Please just pass images: _listOfImages, and it will solve your problem.

You need to do this instead..
images: _listOfImages,

You just need to remove [ ] from image : [ _listOfImages ];
do this instead
return Carousel(
boxFit: BoxFit.contain,
dotBgColor: Colors.transparent,
dotIncreasedColor: Colors.grey,
dotSize: 6.0,
images:
_listOfImages,
);

Related

How can I update Listview in Streambuilder in flutter

I have a streambuidler widget that contains a listview to display whom the current user has recently chatted with. When the current user receives a message that message should be pushed to the top of the listview, however that message is always display at the bottom of the list view, it's only display on the top if I refresh my screen.
NoSearchResultScreen() {
final Orientation orientation = MediaQuery.of(context).orientation;
print("hasAlreadyChatWithSomeone: $hasAlreadyChatWithSomeone");
return hasAlreadyChatWithSomeone
? StreamBuilder<QuerySnapshot>(
stream: (FirebaseFirestore.instance
.collection("user")
.where("id", isEqualTo: currentUserId)
.snapshots()),
builder: (context, snapshot) {
List<ProfileChatWith> chatWithList = [];
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: circularProgress(),
);
}
if (snapshot.hasData) {
final chatWithSnapshot = snapshot.data?.docs.first['chatWith'];
//print("chatWithSnapshot: $chatWithSnapshot");
for (var userChatWith in chatWithSnapshot) {
final user = ProfileChatWith(
userId: userChatWith,
currentUserId: currentUserId,
);
chatWithList.add(user);
//print("I have chatted with: $userChatWith");
}
return Container(
width: MediaQuery.of(context).size.width,
child: ListView(
//shrinkWrap: true,
children: chatWithList,
),
);
} else {
return Center(
child: circularProgress(),
);
}
},
)
: Container(
child: Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[
Icon(
Icons.group,
color: Colors.greenAccent,
size: 200.0,
),
Text(
"Search Users",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.greenAccent,
fontSize: 50.0,
fontWeight: FontWeight.bold),
)
],
),
),
);
}
Try reverse: true,
return SizedBox(
width: MediaQuery.of(context).size.width,
child: ListView(
reverse: true,
children: chatWithList,
),
);
Use Listview.builder for performance optimization
return SizedBox(
width: MediaQuery.of(context).size.width,
child: ListView.builder(
reverse: true,
itemBuilder: (BuildContext context, int index) => chatWithList[index],
),
);
The solution which worked is as below, #Leo Tran own words
I found a way to solve my question is that I will rebuild my widget whenever the data got updated.

How to display one particular data from Realtime Database?

In a firebase animated list, how do you put in a conditional statement, or anything else, so that only one set of data in Realtime Database will be displayed? I currently can display all of them in a ListTile but I only want to display a destination whose name is 'Spain' and its description instead of all the database that contains Spain, Italy, USA etc.
class _TestDestinationsState extends State<TestDestinations> {
final destdatabaseref = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF4D71AC),
elevation: 0,
centerTitle: true,
title: Text('Eh',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white)),
),
body: SafeArea(
child: FirebaseAnimatedList(
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 15),
Padding(
padding: const EdgeInsets.all(8),
child: ListTile(
title: Text(snapshot.value['name'],
style: TextStyle(fontSize: 20)),
subtitle: Text(snapshot.value['description'])),
),
],
),
);
},
query: destdatabaseref,
)),
);
}
}
If we need to only display specific data from FirebaseDatabase we can use the following logic:
Visibility(
visible: snapshot.value['name'] == 'Spain',
child: ...
),
The complete snippet can be found below:
class _TestDestinationsState extends State<TestDestinations> {
final destdatabaseref = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF4D71AC),
elevation: 0,
centerTitle: true,
title: Text('Eh',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white)),
),
body: SafeArea(
child: FirebaseAnimatedList(
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return Visibility(
visible: snapshot.value['name'] == 'Spain',
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 15),
Padding(
padding: const EdgeInsets.all(8),
child: ListTile(
title: Text(snapshot.value['name'],
style: TextStyle(fontSize: 20)),
subtitle: Text(snapshot.value['description'])),
),
],
),
),
);
},
query: destdatabaseref,
)),
);
}
}
However a much better solution, would be to retrieve only specific data based on the filter, which can be done by filtering our query to FirebaseDatabase as follows:
final destdatabaseref = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations')
.orderByChild('name')
.equalTo('Spain');

Error is not showing after scanning a item thats not on the firestore database

I did this personal project of mine where a barcode scanner would scan for data inside firestore database. I have this problem when I scanned a barcode thats not on the database it wont show the error message is just shows a empty scan item container which I made. Let me know if someone can figure why. I tried everything still couldnt fix it.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("products")
.where("barcode", isEqualTo: '$barcodeScanRes')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Dialog(
child: Container(
height: 300,
child: Text('Product Not Found'),
),
);
} else {
return Dialog(
child: Container(
height: 350,
child: Column(children: [
Container(
height: 350,
width: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot products =
snapshot.data!.docs[index];
return ScanCard(products: products);
},
)),
]),
),
);
#Scan Card
class ScanCard extends StatelessWidget {
const ScanCard({
Key? key,
required this.products,
}) : super(key: key);
final DocumentSnapshot products;
#override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
String _userId = user!.uid;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(10.0),
height: 180,
width: 160,
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(16)),
child: Image.network(products['img']),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0 / 4),
child: Text(
products['name'],
style: TextStyle(
color: Colors.blueGrey,
fontSize: 18,
),
),
),
Column(
children: [
Text(
"Size: " + products['size'],
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.brown),
),
SizedBox(
width: 30,
),
],
),
Row(
children: [
Text(
"\tRs. " + products['price'],
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
SizedBox(
width: 40,
),
Icon(
Icons.add_shopping_cart,
color: Colors.black,
size: 25,
),
],
),
SizedBox(
width: 10,
),
SizedBox(
child: Padding(
padding: const EdgeInsets.all(10),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Colors.red,
child: Text(
"Add to cart",
style: TextStyle(color: Colors.white),
),
onPressed: () {
DocumentReference documentReference = FirebaseFirestore.instance
.collection('userData')
.doc(_userId)
.collection('cartData')
.doc();
documentReference.set({
'uid': FirebaseAuth.instance.currentUser!.uid,
'barcode': products['barcode'],
'img': products['img'],
'name': products['name'],
'size': products['size'],
'price': products['price'],
'id': documentReference.id
}).then((result) {
addToCartMessage(context).then((value) => {
Navigator.pop(context)
});
}).catchError((e) {
print(e);
});
},
),
),
)
],
);
}
}
The thing is you are showing Product not found based on the condition:- !snapshot.hasData but this conditon means that data is being fetched so at this time rather show a progress indicator.
And to handle when data is not present in backend then add another condition:- if(snapshot.data.docs.isEmpty) and here show your dialogbox of Product not found...
Final Code Snippet will look like:-
if (!snapshot.hasData)
return Center(child:CircularProgressIndicator));//or return a black container if you don't want to show anything while fetching data from firestore
else if (snapshot.data.docs.isEmpty) {
return Dialog(
child: Container(
height: 300,
child: Text('Product Not Found'),
),
);
} else {
return Dialog(
child: Container(
height: 350,
child: Column(children: [
Container(
height: 350,
width: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot products =
snapshot.data!.docs[index];
return ScanCard(products: products);
},
)),
]),
),
);
}

firestore map to StreamBuilder => ListView.Builder

i want to show the songs list inside document (singer that user clicked on). Every song should load in list tile but all of them load in one tile.
and it loads the 'songs list' from all documents(all singers).
this is the FireStore DB
this is list of singers to choose from.
this should show only the songs from selected singer each one in a tile but shows all songs from all singers. and every singers song in one tile
class SongsList extends StatefulWidget {
#override
_SongsListState createState() => _SongsListState();
}
class _SongsListState extends State<SongsList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: Firestore.instance.collection('singers').snapshots(),
builder: (
context,
snapshot,
) {
if (snapshot.data == null)
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red,
valueColor: new AlwaysStoppedAnimation<Color>(Colors.teal),
),
);
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/back.png'), fit: BoxFit.contain)),
child: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
var result = snapshot.data.documents[index]['songs list'];
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
left: 10, right: 10, top: 10, bottom: 0),
child: Container(
height: 50,
width: 300,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(0.5),
spreadRadius: 1.5,
blurRadius: 1.5,
//offset: Offset(0, 1), // changes position of shadow
),
],
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Colors.red[200],
width: 0.5,
style: BorderStyle.solid)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (var res in result.entries)
Text(
res.key,
style: TextStyle(
fontSize: 20, color: Colors.red[500]),
),
]),
),
),
);
}),
);
},
),
);
}
}
If you want to get only the songs of one singer, then you need to specify the document id to retrieve one document, change this:
stream: Firestore.instance.collection('singers').snapshots(),
into this:
stream: Firestore.instance.collection('singers').document('aryana sayeed').snapshots(),
List tile has a corresponding index. I think you might have to build a list tile instead of a container. If you need a container, you have to write a code that would specifically get the singers name (documentID) wired on each container

Flutter Gridview button functionality to new screen with Firebase

I've made a Gridview using Firebase and Streambuilder and Gridview.builder. This grid displays album titles, the album cover art for each album, and the artists that make each album. I'd like for each grid tile to be able to be pressed and navigate to a separate page with its specific album details. The plan was on press, the app would be able to identify the entire document the grid tile was referring to, move to a new page, and display the document in full to unveil the album details. The thing is, I don't know how to do that. Since snapshot.data.documents[index]['Title'] worked when iterating though all the documents to create the gridview, I thought that typing snapshot.data.documents[index] would work, but it just displays Instance of 'DocumentSnapshot' in the debug console. I'm out of ideas on how to tackle this, so any suggestions are welcome
My code is shown below
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final Services services = Services();
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: bgcolour,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: Icon(Icons.menu),
title: Text("Home"),
actions: <Widget>[
Padding(padding: EdgeInsets.all(10), child: Icon(Icons.more_vert))
],
),
body: StreamBuilder(
stream: Firestore.instance.collection('music').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text("Loading...");
return GridView.builder(
itemCount: snapshot.data.documents.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 0.655172413),
//cacheExtent: 1000.0,
itemBuilder: (BuildContext context, int index) {
var url = snapshot.data.documents[index]['Cover Art'];
return GestureDetector(
child: Container(
width: 190.0,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(32)),
color: hexToColor(
snapshot.data.documents[index]['Palette'][0]),
elevation: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 12),
ClipRRect(
borderRadius: BorderRadius.circular(21.0),
child: Image.network(url,
height: 180.0, width: 180)),
SizedBox(height: 10),
Text(
snapshot.data.documents[index]['Artist']
.join(', '),
textAlign: TextAlign.center,
style: GoogleFonts.montserrat(
textStyle: TextStyle(color: Colors.white),
fontSize: 14,
fontWeight: FontWeight.w300)),
SizedBox(height: 10),
Text(snapshot.data.documents[index]['Title'],
style: GoogleFonts.montserrat(
textStyle: TextStyle(color: Colors.white),
fontSize: 16,
fontWeight: FontWeight.w600),
textAlign: TextAlign.center),
],
),
),
),
onTap: () {
print("Tapped ${snapshot.data.documents[index]}");
},
);
},
);
}
),
);
}
}
Is there an ID for your snapshot.data.documents[index]? If yes, add it to the end.
onTap: () {
print("Tapped ${snapshot.data.documents[index]['the property you want']}");
},

Resources