use loadingBuilder in NetworkImage class flutter - flutter

I faced some problems in my flutter app when I use network image:
the image takes too much time to load.
I found the solution by adding loading Builder prosperity in the network.image widget like that :
Image.network( 'URL image}',
fit: BoxFit.fitHeight,
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,
),
);
},
),
This works out right now, but then I need to add the same property (loadingBuilder) with the NetworkImage class (into the BoxDecoration)
as follows:
image: DecorationImage(
image: NetworkImage(widget.cover),
fit: BoxFit.cover,
),
How do I add the loadingBuilder with the NetworkImage class (in the last part of the code)?

There is a solution that I used although it seems like trick.
Using 'Stack' widget, at first show loading widget and draw image widget over that.
If that, at first loading widget will be shown and after image loading completed, image will be shown over the loading widget.
Stack(
children: <Widget>[
// Locate loading widget
LoadingWidget(),
//
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(widget.cover),
fit: BoxFit.cover,
),
),
),
],
);

Related

Flutter: avoid opacity effect for FlexibleSpaceBar

I use a FlexibleSpaceBar but only for an image. The AppBar is always on top so there is no transition between these 2 elements at all, just the effect of scrolling the image up using parallax.
So I don't need to make the opacity effect for the image.
I know it's possible because I've seen it on Aliexpress's app, which I know it uses Flutter. You can see the no opacity effect here:
https://youtu.be/ESSsY2m7vTY?t=28
when mine is looking like this:
https://youtu.be/Jnm9jN4-wWY
This is my widget:
FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
background: Hero(
tag: widget.newRent == false
? 'rent-image${Provider.of<MyRents>(context).currenRentIndex}'
: 'new-rent-image',
child: _imageEdited == false
? myRent['images'].isNotEmpty
? CachedNetworkImage(
imageUrl: myRent['images'][0],
fit: BoxFit.cover,
placeholder: (context, url) =>
Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
errorImage(context),
)
: Provider.of<MyRents>(context, listen: false)
.newGalleryImages
.isNotEmpty
? Image.file(
Provider.of<MyRents>(context, listen: false)
.newGalleryImages[0],
fit: BoxFit.cover)
: Container(
padding: const EdgeInsets.all(20.0),
child: Image(
image:
AssetImage('assets/images/icon.jpg'),
),
)
: Image.file(myRent['images'][0], fit: BoxFit.cover),
),
),
After removing all the checks it's a basic FlexibleSpaceBar with a Hero transition, nothing fancy
I think the problem is because it is indeed a "Bar" like the name of the widget says: FlexibleSpaceBar. You would have to propably extend this function and overwrite this behaviour or maybe you could use this:
https://pub.dev/packages/stretchy_header
Followed this approach:
https://stackoverflow.com/a/58973381/4858133
Stack(
children: <Widget>[
Positioned.fill(
child: Hero(
tag: widget.newRent == false
? 'rent-image${Provider.of<MyRents>(context).currenRentIndex}'
: 'new-rent-image',
child: _imageEdited == false
// TODO: move it to a separate function
? myRent['images'].isNotEmpty
? CachedNetworkImage(
imageUrl: myRent['images'][0],
fit: BoxFit.cover,
placeholder: (context, url) => Center(
child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
errorImage(context),
)
: Provider.of<MyRents>(context, listen: false)
.newGalleryImages
.isNotEmpty
? Image.file(
Provider.of<MyRents>(context,
listen: false)
.newGalleryImages[0],
fit: BoxFit.cover)
: Container(
padding: const EdgeInsets.all(20.0),
child: Image(
image: AssetImage(
'assets/images/icon.jpg'),
),
)
: Image.file(myRent['images'][0],
fit: BoxFit.cover),
),
),
],
),

How can I make a Card height extend based on content?

I am creating a Card which has a header and a grid view of photos. Below is the code:
_getImageWidget(Post post, AppConfig config) {
if (post.photoPaths != null && post.photoPaths.length > 0) {
var url = config.imagePathDomain + post.photoPaths[0];
try {
return Expanded(
child: GridView.count(
crossAxisCount: 3,
shrinkWrap: false,
children: post.photoPaths.map<Widget>((String path) {
return CachedNetworkImage(
imageUrl: url,
);
}).toList()));
} catch (err) {
return Container();
}
}
return Container();
}
#override
Widget build(BuildContext context) {
var config = AppConfig.of(context);
return BlocBuilder<UserInfoBloc, UserInfo>(builder: (_, userInfo) {
return Container(
width: MediaQuery.of(context).size.width,
child: Card(
child: Column(children: <Widget>[
Row(
children: <Widget>[
IconButton(
iconSize: 30,
icon: roundImage(post.userPicture, Icon(Icons.person)),
onPressed: () {},
),
Text('#${userInfo.username}')
],
),
this._getImageWidget(post, config),
])));
});
}
The header in the Card is a Row includes a IconButton and Text.
The body of the Card is a gridview which includes a few photo.
Below is the screenshot when I run the code. You can see that the photo is shown only a half. And I can scroll vertically on the grid view. The number of photos is dynamic which means there could be many rows of photos in the GridView. How can I make the Card extend based on its children?
By simply setting your CachedNetwork image to use a fit: BoxFit.cover that will resize to fill the content while preserving ratio (this means you may lose some of its details) or fit: BoxFit.contain that will try to be as big as possible while containing the entire source (image) within the bounds.
If that doesn't help as well (as I'm not seeing the entire tree so I'm not sure about the ancestors of your Card) you can also replace the return of your BlocBuilder's child to be a FittedBox instead of a Container and apply the same logic for the fit property, but for the whole card instead.
Try using a fixed size container and using the BoxFit property on the container.
Something like this :
new Container(
width: 80.0,
height: 80.0,
decoration: new BoxDecoration(
shape: BoxShape.rectangle,
image: new DecorationImage(
fit: BoxFit.fill,
image: CachedNetworkImageProvider(url),
),
),
),
Edit : Try to remove itemExtent from ListView.builder

How to show Hero Animation on a NetworkImage while switching from one screen to another?

In my flutter project, in one screen I show some list of data(including image) from API call. In that list I have wrapped the NetworkImage with Hero and added the productId as the tag.
Here's the code-
Expanded(
child: Hero(
tag: '${snapshot.data[position].products[index].id}',
child: FadeInImage(
width: 130.0,
height: 186.0,
placeholder: AssetImage('assets/placeholder.jpg'),
image:
NetworkImage(snapshot.data[position].products[index].image),
),
),
),
While switching to next screen, I pass the entire object which I get from API which following code-
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
snapshot.data[position].products[index])));
Now, in next Screen(where I have passed the entire object product), I have wrapped my NetworkImage with Hero and used the products.id as tag.
Here's the code-
Padding(
padding: EdgeInsets.only(top: ScreenUtil.instance.setHeight(15)),
child: Hero(
tag: '${products.id}',
child: FadeInImage(
height: ScreenUtil.instance.setHeight(330),
fit: BoxFit.fill,
placeholder: AssetImage('assets/placeholder.jpg'),
image: NetworkImage(
"${products.image}",
),
),
),
),
Now the problem is ------
After running the project, when I press one item from the list(where the NetworkImage includes), it shows black in the next screen and showing the following error in the console-
I have followed the same way described in some documentation that each items for animation should have the same tag name, but in my case it is not working. Showing that-
There are multiple heroes that share the same tag within a subtree.
So, I need a solution to solve this issue.
Save Network image file to a File variable then pass it throw pages (u can use file path as tag for hero and use file)
Retrieve data from ur database save to URL variable in my case it is just 'String url'(I'm getting it from another widget so I used widget.url)
from 1st screen
Hero(
tag: 'anything' + widget.url,
child: CircleAvatar(
backgroundImage: widget.url!= null
? ExtendedNetworkImageProvider(
widget.url,
)
: null,
radius: 23,
),
Navigator.of(context).push(TransparentRoute(
builder: (BuildContext context) => SecondScreen(
image: widget.url,
)));
(image URL is unique so we can use it as tag for hero)
2nd screen
class SecondScreen extends StatefulWidget {
final image;
SecondScreen(
{this.image});
#override
_SecondScreenState createState() => _SecondScreenState();
}
Hero(
tag: 'anything' + widget.image,
child: CircleAvatar(
backgroundImage: widget.image!= null
? ExtendedNetworkImageProvider(
widget.image
)
: null,
radius: 23,
),

Why the BoxFit enum not working as expected?

I am trying to cover the entire canvas with an image. For this, I am using BoxFit.cover enum. But it is not working.
Code :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body:
Image(
image: AssetImage("assets/choco.jpg"),
fit: BoxFit.cover,
),
),
);
}
}
Output Screenshot :
Why is it not covering the entire screen? None of the BoxFit enum values are working. Please explain in detail. Thank you.
Stack also has a fit property, so set fit to StackFit.expand.
Stack(
fit: StackFit.expand,
...
More info here: https://docs.flutter.io/flutter/widgets/Stack/fit.html
If you don't have a parent widget for you Image try using the height and width property.
Image(
image: AssetImage("assets/choco.jpg"),
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
),
Images are not knowing the width to cover.
Wrap your image with a Container() and set width: double.infinity, for it.
Container(
width: double.infinity,
child: Image(
image: AssetImage("assets/choco.jpg"),
fit: BoxFit.cover,
),
),

Make FadeInImage.memoryNetwork render circular cropped image

I am trying to create something like CircleAvatar, but with a Stacked widget having a CircularProgressIndicator and a FadeInImage.memoryNetwork as children so that I get a nice loading animation (bonus: placeholder image) when I fetch the image on slow Internet.
Everything else is working fine but I am not being able to figure out how to crop the fetched image into a circular form. I read here that I can use a ClipOval, but I couldn't find any tutorial on how to use it.
Stack(children: <Widget>[
Center(child: CircularProgressIndicator(valueColor: new AlwaysStoppedAnimation<Color>(Colors.lightBlue))),
Center(
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image:
'https://s3-media2.fl.yelpcdn.com/bphoto/7BlRoSOG3AsAWHMPOaG7ng/ls.jpg',
),
),
],
),
));
Note: I am using transparent_image library for the placeholder to get a transparent placeholder while displaying the loading animation.
This is what I am using in one of my projects, I guess you can create something similar, then instead of using FadeInImage directly use the custom widget.
class AvatarFadeImage extends StatelessWidget {
final String imageUrl;
AvatarFadeImage(this.imageUrl);
#override
Widget build(BuildContext context) {
return ClipOval(
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageUrl,
fit: BoxFit.contain,
),
);
}
}
Use it like this:
Stack(children: <Widget>[
Center(child: CircularProgressIndicator(valueColor: new AlwaysStoppedAnimation<Color>(Colors.lightBlue))),
Center(
child: AvatarFadeImage("https://s3-media2.fl.yelpcdn.com/bphoto/7BlRoSOG3AsAWHMPOaG7ng/ls.jpg"),
),
],
),
));
PS: You may also look at https://pub.dartlang.org/packages/cached_network_image and https://flutter.io/cookbook/images/cached-images/
Plugin might just do the trick for you.
Just in case this helps anyone, the modified code which worked for me:
Stack(
fit: StackFit.passthrough,
children: <Widget>[
Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.lightBlue))),
Center(
child: ClipOval(
child: FadeInImage.memoryNetwork(
fit: BoxFit.contain,
placeholder: kTransparentImage,
image: 'https://s3-media2.fl.yelpcdn.com/bphoto/7BlRoSOG3AsAWHMPOaG7ng/ls.jpg',
),
),
),
],
),
));