Flutter how to show placeholder if image in assets not available - flutter

I am getting some list of images names only I need to get it from my asset folder. But there are some images which are not available so I need to add some error placeholder If it's not exists. I tried with Future But When I show it on screen its showing error that Future Widget can't be added in Widget
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(100)),
child: Image.asset(
'assets/images/${image}.png',
height: 50,
width: 50,
fit: BoxFit.fill,
)),
This is what I get
Future<Widget> Imagee(image) async {
try {
await rootBundle.load(image);
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(100)),
child: Image.asset(
'assets/team/${image}.png',
height: 60,
width: 60,
fit: BoxFit.fill,
));
} catch (_) {
return SizedBox(); // Return this widget
}
}
Its showing that Future can't be added in Widget

Maybe you can try using a validation:
await rootBundle.load(image);
if (image != null) {
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(100)),
child: Image.asset(
'assets/team/${image}.png',
height: 60,
width: 60,
fit: BoxFit.fill,
));
} else {
//...
}

You can use FutureBuilder if you have async call to load the Image.
Future<Widget> ImageOrNot() async {
try {
await rootBundle.load(image);
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(100)),
child: Image.asset(
'assets/team/${image}.png',
height: 60,
width: 60,
fit: BoxFit.fill,
));
} catch (_) {
return SizedBox(); // Return this widget
}
}
and inside build use FutureBuilder
class ImageOrBox extends StatelessWidget {
const ImageOrBox({super.key});
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: imageOrNot(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return snapshot.data??Text('Error');
},
);
}
}

Related

Stream builder returning error screen when there is no data existing in Firebse Firestore

I created a function that returns a widget. My firestore has no data.
here is the code for that function:
Widget _getImage() {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(100.0),
child: SizedBox(
height: 48,
width: 48,
child: StreamBuilder(
stream: UserImages.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
String profileImageURL =
snapshot.data!.get('passport URL')?.toString() ?? "";
// if profileImageURL empty, use holder image
if (profileImageURL == "") {
profileImageURL =
"https://firebasestorage.googleapis.com/v0/b/[name of my firebase project].appspot.com/o/app_assets%2FprofileHolder.png?"; //another default image
}
return CachedNetworkImage(
imageUrl: profileImageURL,
progressIndicatorBuilder:
(context, url, downloadProgress) =>
CircularProgressIndicator(
value: downloadProgress.progress,
color: Theme.of(context).primaryColor,
),
errorWidget: (context, url, downloadProgress) =>
const Icon(Icons.error_outline),
);
} else
return SizedBox();
}),
),
),
);
}
I want the StreamBuilder to return a different widget in case there is no data in Firestore and the user has not yet uploaded the profile image.

Flutter cannot use URL variable inside Image.network widget

Im trying to get url from cloud firebase and then use it in Image.network but it doesn't work..
When i hardcode the url inside Image.network it works.. the variable did get the url as the data.
I get an error from image.dart - ImageStreamListener throw error.
this is my code:
class _MemoryCardState extends State<MemoryCard> {
Map<String, dynamic> photos = {};
Future getPhoto() async {
photos.clear();
var db = FirebaseFirestore.instance.collection('photos');
await db.doc(widget.id).get().then((DocumentSnapshot snapshot) {
photos = snapshot.data() as Map<String, dynamic>;
});
}
#override
Widget build(BuildContext context) {
var deviceWidth = MediaQuery.of(context).size.width;
var deviceHeight = MediaQuery.of(context).size.height;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
elevation: 10,
color: Theme.of(context).colorScheme.surfaceVariant,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: SizedBox(
width: deviceWidth * 0.8,
height: deviceWidth * 0.35,
child: InkWell(
splashColor: Colors.blue.withAlpha(30),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const Memory()));
},
child: Stack(
children: [
FutureBuilder(
future: getPhoto(),
builder: (context, snapshot) {
String url = photos['url'].toString();
return Hero(
tag: 'image',
child: Image.network(
url,
fit: BoxFit.cover,
width: deviceWidth * 0.8,
color: Colors.white.withOpacity(0.5),
colorBlendMode: BlendMode.modulate,
));
}),
],
),
),
),
),
SizedBox(height: deviceHeight * 0.2),
],
);
}
}
The error you are encountering is likely caused by the fact that the url variable is not yet available when the Image.network widget is first rendered. The FutureBuilder widget is used to handle this issue, but it is not being used correctly in your code.
A FutureBuilder widget should be used to rebuild the widget tree when the future completes.
You should move the FutureBuilder outside of the InkWell and Stack widgets.
FutureBuilder(
future: getPhoto(),
builder: (context, snapshot) {
if (snapshot.hasData) {
String url = photos['url'].toString();
return InkWell(
splashColor: Colors.blue.withAlpha(30),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const Memory()));
},
child: Stack(
children: [
Hero(
tag: 'image',
child: Image.network(
url,
fit: BoxFit.cover,
width: deviceWidth * 0.8,
color: Colors.white.withOpacity(0.5),
colorBlendMode: BlendMode.modulate,
)),
],
),
);
} else {
return CircularProgressIndicator();
}
},
);
Also, it will be a better practice to check if the snapshot hasData before trying to access the url.
Also, you can use await keyword to wait for the data retrieval to complete, before using the url value in the Image.network.
give the url which you found from firebase.
If you used URI as DataType in firebase then it is the problem.

Calling setState doesn't updateshowDialog content

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

Retrieve array of images from Firestore - flutter

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

Flutter Slider, problem with lists, map and elements inside

I try to put a Corousel Slider but with a List that is updated automatically in another part of the app.
The documentation for the slider is with static photos. I want to access to the items inside a list and iterate.
var itemss = [];
for (var e = 0; e < model.listFireforce.length; e++) {
itemss.add(model.listFireforce[e].image);
}
return CarouselSlider(
height: 350.0,
items: [ itemss // HERE IS THE PROBLEM !!
].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 2.0),
decoration: BoxDecoration(color: Colors.white),
child:
Image.network(
i,
fit: BoxFit.cover,
));
},
);
}).toList());
i itearate only 1 time, but no for all the items inside. I try forEach() but i have no solution.
Thanks!
Your problem is that you wrap your itemss array in another array and map over that. You may want to try this:
return CarouselSlider(
height: 350.0,
items: itemss.map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 2.0),
decoration: BoxDecoration(color: Colors.white),
child: Image.network(
i,
fit: BoxFit.cover,
));
},
);
}).toList(),
);