I am currently trying to retrieve a list of images from Firestore and display them on the screen like this
But I can only retrieve one image from the list. Please help me display all images from Firestore.
How I stream data from Firestore
StreamBuilder<QuerySnapshot>(
stream:
FirebaseFirestore.instance.collection('properties').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Fail to load..');
} else if (snapshot.hasData || snapshot.data != null) {
return ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount:
snapshot.data?.docs.length,
itemBuilder: (BuildContext context, int index) {
QueryDocumentSnapshot<Object?>? documentSnapshot =
snapshot.data!.docs[index];
return TestPropertyCard(itemData: documentSnapshot);
});
}
return Container();
},
),
Property Card
class TestPropertyCard extends StatelessWidget {
final dynamic itemData;
const TestPropertyCard({Key? key, required this.itemData}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PropertyDetailPage(
itemData: itemData,
)));
},
child: Container(
height: 200,
margin: const EdgeInsets.only(bottom: 8, left: 5, right: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(7),
color: NayyaaColorTheme.nayyaaBlue),
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10), topRight: Radius.circular(10)),
child: Image.network(
itemData["CoverPhoto"],
height: 130,
width: MediaQuery.of(context).size.width,
fit: BoxFit.fitWidth,
),
),
When user tap on the property card, the app would the user take to the detail page. On this detail page, I would like to display a list of images that is stored in Firestore as array. See my firestore data structure here.
However, my problem is I can only retrieve one image. Here is my code for detail page
class PropertyDetailPage extends StatelessWidget {
final dynamic itemData;
const PropertyDetailPage({Key? key, required this.itemData}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
children: [
Image.network(
itemData['Gallery'][0],
fit: BoxFit.fill,
),
],
),
),
);
}
}
I have tried with ListView.builder(). But I couldn't make it work. Thanks in advance.
Looking into the code you are using, it seems fine. It just lacks the mapping from an image url to image widget.
Replace the children in the ListView as below:
children: (itemData['Gallery'] as List).map((imageUrl){
return Image.network(
imageUrl as String,
fit: BoxFit.fill,
);
}).toList(),
it should work assuming the itemData['Gallery'] is a List<String> stored in the firestore
Related
I have a custom popup built, and an image is supposed to change whenever one of my variables is changed. When I call the setState method, the content in my showDialog doesn't change.
What am I doing wrong, or is there a better approach? Trying to change the state so the image can be changed in the showDialog.
Here's my code:
class LocationManagerPage extends StatefulWidget {
const LocationManagerPage({Key? key}) : super(key: key);
#override
State<LocationManagerPage> createState() => _LocationManagerPageState();
}
class _LocationManagerPageState extends State<LocationManagerPage> {
String downloadURL = "";
Future _uploadFile(String path) async {
// Logic that gets the download url to an image...
// When the download url is found, calling setState method
setState(() {
downloadURL = fileUrl;
});
}
showLocationPopup() {
return showDialog(
context: context,
builder: (context) {
return Center(
child: Material(
child: Container(
width: 427,
height: 676,
decoration: BoxDecoration(...),
child: SingleChildScrollView(
child: Column(
children: [
// Popup UI Widgets,
Center(
child: Container(
height: 150,
width: 150,
decoration: BoxDecoration(),
child: ClipRRect(
child: Image.network(
image,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(20),
),
),
),
SizedBox(
height: 15,
),
Center(
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () async {
String? imageUrl = await urlFromWebImage();
print(imageUrl);
setState(() {
downloadURL = imageUrl!;
});
},
child: Button(
name: imageName,
),
),
),
),
// The rest of the popup UI
],
),
),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
.... // Not Important
);
}
}
To update dialog ui you need to use StatefulBuilder widget on showDialog's builder and use StatefulBuilder's setState.
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
you can use the following approach
first initialize download url like this
ValueNotifier<String> downloadUrl = ValueNotifier("");
ValueListenableBuilder(
valueListenable: downloadUrl,
builder: (context, value, Widget? c) {
return Container(
height: 150,
width: 150,
decoration: BoxDecoration(),
child: ClipRRect(
child: Image.network(
downloadUrl.value, // here put download url it will auto update
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(20),
));
});
and without using setstate put value in download url it will auto update ui
downloadUrl.value = "" //your image url
or you can use StateFulBuilder
setstate rebuild your whole widget but upper approach only build image widget
This code is for picking multiple images from the gallery using Image Picker Package and showing them on the home screen. After that drag one of the image from the selected images and drop it to drop target by removing the dropped image from the list of selected images. But I'm unable to do as it is showing an exception.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class Gallery extends StatefulWidget {
const Gallery({Key? key}) : super(key: key);
#override
_GalleryState createState() => _GalleryState();
}
class _GalleryState extends State<Gallery> {
final List<XFile>? selectedImagesList = [];
final List<XFile> dropTargetList = [];
#override
Widget build(BuildContext context) {
double size = 230;
return Scaffold(
appBar: AppBar(
title: const Text('Gallery'),
),
body: Column(
children: [
ElevatedButton(
onPressed: () async {
final List<XFile>? selectedImages = await ImagePicker().pickMultiImage();
if (selectedImages!.isNotEmpty) {
setState(() {
selectedImagesList!.addAll(selectedImages);
});
}
},
child: const Text('Select Images'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
itemCount: selectedImagesList!.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5),
itemBuilder: (BuildContext context, int index) {
return Draggable<XFile>(
child: Image.file(File(selectedImagesList![index].path),
fit: BoxFit.cover, width: 230, height: 230),
feedback: Image.file(
File(selectedImagesList![index].path),
fit: BoxFit.cover,
width: 230,
height: 230),
childWhenDragging: Container(
color: Colors.red, width: size, height: size),
);
}),
),
),
Container(
width: size,
height: size,
color: Colors.green,
child: DragTarget<XFile>(
builder: ((context, candidateData, rejectedData) {
return Row(
children: dropTargetList
.map((target) => Image.file(File(target.path),
fit: BoxFit.cover, width: size, height: size))
.toList());
}),
onWillAccept: (data) => true,
onAccept: (data) {
setState(() {
dropTargetList.add(data);
selectedImagesList
?.removeWhere((photo) => photo.path == data.path);
});
})),
const SizedBox(height: 200)
],
),
);
}
}
This is the exception while dropping the image https://i.stack.imgur.com/ParUz.png
Emulator screen getting stuck on dropping https://i.stack.imgur.com/5lEsN.png
To prevent the exception from occuring, make sure to only accept data if data is not null:
onWillAccept: (data) => data != null,
This will prevent the exception.
Looking further on why data is null, it's because you don't set it when you create your draggable:
return Draggable<XFile>(
child: ...,
...
data: selectedImagesList![index],
);
Now it will work as you expect it to. Dragging multiple images down causes some overflow, but you should be able to work on that with the exception out of your way :)
I am calling API data and want to show it without using listview.builder in flutter but the error I am facing is that the data is not get loading and the loading indicator is active all the time.
For Example, When I open my app the data get starts loading itself (as I am using future). but it always loads and data didn't get fetched from API.
I am looking for someone who can help me to fix this issue?
For this purpose here is myClass Code.
class _TestState extends State<Test> {
Future<List<dynamic>> getLiveMatches() async {
http.Response response = await http.get(
Uri.parse("https://api.cricket.com.au/matches/2780/50837/live")
);
final Map parseData = await json.decode(response.body.toString());
var matches = parseData['liveMatch'];
return matches;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(left: 10, right: 10, top: 15),
child: RefreshIndicator(
color: Colors.white,
backgroundColor: Colors.purple,
strokeWidth: 5,
onRefresh: ()async{
getLiveMatches();
},
child: Container(
height: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6),
topRight: Radius.circular(6)
),
color: Colors.white
),
child: FutureBuilder<List<dynamic>>(
future: getLiveMatches(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> matches = snapshot.data;
print(matches);
return Column(
children: [
Text(matches.toString())
],
);
}
return Center(
child: CupertinoActivityIndicator(
animating: true, radius: 10));
}
),
),
),
),
);
}
}
You are not requesting json. Try: https://api.cricket.com.au/matches/2780/50837/live?format=json
Btw, your onRefresh call does nothing.
From the image below, i want to set border around the first image when the page loads and also set it as the main image. Once the next image is clicked, it has to be set as the main image with the border around it. I am trying to achieve this in flutter and the list of images were built using a ListView builder. Can anyone be of help. The heroImage widget depends on the image selection from the images widget and on page load, the heroImage must be set to the first image in the images widget.
// Selected image tile being used in the listView
class SelectedImageTile extends StatelessWidget {
final GestureTapCallback onTap;
final String imageAsset;
final BoxDecoration decoration;
SelectedImageTile({
this.onTap,
this.imageAsset,
this.decoration
});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.all(2),
decoration: decoration,
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.asset(
imageAsset,
fit: BoxFit.cover,
height: 60,
width: 60,
),
),
),
);
}
}
Widget heroImage() {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/image-1.png',
height: 350,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover
),
);
}
Widget images() {
return SizedBox(
height: 75,
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final galleryImage = galleryImages[index];
return SelectedImageTile(
imageAsset: galleryImage,
onTap: () {},
decoration: BoxDecoration(
border: Border.all(color: EShopColors.primary),
borderRadius: BorderRadius.circular(8)
),
);
},
itemCount: galleryImages.length,
),
);
}
Convert images() widget to a StateFulWidget
Create a variable selectedImage and set it to URL of the image tapped (when tapped)
And finally add a condition to the border of the image tile.
class Images extends StatefulWidget {
#override
_ImagesState createState() => _ImagesState();
}
class _ImagesState extends State<Images> {
var selectedImage = galleryImages[0]
#override
Widget build(BuildContext context) {
return SizedBox(
height: 75,
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final galleryImage = galleryImages[index];
return SelectedImageTile(
imageAsset: galleryImage,
onTap: () {
this.selectedImage = galleryImage;
setState(() {});
},
decoration: BoxDecoration(
border:galleryImage==this.selectedImage? Border.all(color: EShopColors.primary):null,
borderRadius: BorderRadius.circular(8)),
);
},
itemCount: galleryImages.length,
),
);
}
}
Hello Everyone!
I am using Modal BottomSheet for view comments. When I click TextField (Comment Page) all widgets rebuilding also parents! Why all Flutter rebuilding all widgets. And why also parents widgets rebuilding? I know when keyboard appears or rotation changed eg. both StatefulWidget and StateLessWidget rebuilded. But I can’t do something in this situation. Please help me
Here CommentPage.
class CommentPage extends StatelessWidget {final String activityname;
final String postid;
final String usernameuid;
const CommentPage(
{Key key,
#required this.activityname,
#required this.postid,
#required this.usernameuid,
}): super(key: key);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
height: MediaQuery.of(context).size.height * 0.8,
child: Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text('Coments'),
centerTitle: true,
),
body: Container(
height:MediaQuery.of(context).size.height * 0.8,
width: MediaQuery.of(context).size.width,
child: ChangeNotifierProvider(
create: (context) => CommentLikeController(),
builder: (context, child) => FutureBuilder<List<Comment>>(
future: Provider.of<CommentLikeController>(context,
listen: false)
.initial(),
builder: (context, snapshot) {
if (snapshot.hasData)
return Stack(children: [
Positioned(
bottom: 50,
child: Container(
height:
MediaQuery.of(context).size.height * 0.8 -
105,
width: MediaQuery.of(context).size.width,
child: AnimatedList(
shrinkWrap: true,
reverse: true,
key: Provider.of<CommentLikeController>(
context)
.listkey,
initialItemCount: snapshot.data.length,
itemBuilder: (context, index, animation) {
return ListItem(
index: index,
postid: postid,
activityname: activityname,
usernameuid: usernameuid,
);
},
),
),
),
Positioned(
bottom: 0,
right: 0,
child: IconButton(
icon: Icon(Icons.add),
onPressed: () {
Provider.of<CommentLikeController>(
context,
listen: false)
.add();
})),
Positioned(
bottom: 0,
left: 0,
child: Container(
height: 50,
width:
MediaQuery.of(context).size.width - 50,
child: TextField()))
]);
else
return LinearProgressIndicator();
})),
)),
),
);
}
}
Parents CommentPage
class PageviewItem extends StatelessWidget {
final DBcontroller value;
final int index;
final String activityname;
final String username;
const PageviewItem(
{Key key,
#required this.value,
#required this.index,
#required this.activityname,
#required this.username})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Stack(
children: [
Container(
child: value.posts[index].urln.contains('.mp4')
? VideoItem(value: value, index: index)
: PhotoItem(value: value, index: index),
),
UserInfo(value: value, index: index),
Positioned(
bottom: 5,
left: 5,
child: GestureDetector(
onTap: () {
showpopup(context);
}, //show pop up
child: Container(
decoration: BoxDecoration(
color: Colors.blue[400].withOpacity(0.3),
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(text: TextSpan(text: 'Comment')),
),
),
)),
Header(activityname: activityname),
],
),
);
}
showpopup(context) {
return showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10), topRight: Radius.circular(10))),
isScrollControlled: true,
context: context,
builder: (context1) {
return CommentPage(
activityname: activityname,
postid: value.posts[index].from_uid,
usernameuid: username,
);
},
);
}
}
Note I am also have PageviewItem parents cLass. And each one rebuilded when click TextField(keyboard appears )
I'm not good at explaining but maybe I can give an example
class _CommentPageState extends State<CommentPage> {
late Future<QuerySnapshoot> commentStream; //late initialization for future comment
#ovveride
void initState(){
commentStream = FirebaseFirestore.instance.collection('forumchat').snapshot //add this on initstate
} //first initial when commentpage loaded
Widget build(BuildContext context) {
FutureBuilder(
future: commentStream, //add the initialization here
....//
}
}
so future comments must be initialized when the page is first opened, then when the keyboard appears, the build will not rebuild because it has already been initiated.
i hope this helping you, im very bad at explanation lol ;D