Flutter listview moving when loading images from network - flutter

i am trying to make a listview with lots of images with different dimensions loaded from network.
When I scroll down everything is okay, but when i am scrolling up, first images start to load changing the position of listview. I suppose I need to have a placeholder for images with height of image, but i dont know how to get their dimensions before them loaded. Or i need smth like listview that does not move because of smth upper loading.

I wrote a function that gets a height of image in a cycle (array favorites contains Maps which contain url parameter and height parameter if loaded). The function also converts image's height in the size i actually need by formula with MediaQuery.of(context).size.width - 80 which is the width of images on screen. (Width of the screen minus 40 padding from each side).
This is the code of getting height function:
Future<void> getImageHeight(String url, index) async {
Image image = Image.network(url);
image.image
.resolve(const ImageConfiguration())
.addListener(ImageStreamListener((ImageInfo info, bool isSync) {
favorites[index]['height'] = info.image.height *
((MediaQuery.of(context).size.width - 80) / info.image.width);
setState(() {});
}));
}
And this is the code of image with placeholder (Container):
Container(
color: const Color(0xffE8E6EA),
height: favorites[index]['height'] == null || favorites[index
['height'].isNaN ? 0: favorites[index]['height'],
child: Image.network(
favorites[index]['url'],
width: double.infinity,
),
),
On first loading size of placeholder is still going to be zero, because it takes time to get height of them. But after few time (it usually takes about a second or less) setState is going to be called and images will not lag on scrolling. And if you are loading your images urls from database not all at once you'll need to rerun function to get height of newly added images.

Set minCacheExtend large enough
(to be higher than the highest picture's height)

I had the same issue, I resolved it by Increasing the cache of the flutter app. Default cache size is 100mb. You can place this at the beginning of main.dart
WidgetsFlutterBinding.ensureInitialized();
PaintingBinding.instance!.imageCache!.maximumSizeBytes = 1024 * 1024 * 300; // 300 MB
More Info Here

Related

How can i make image in full screen without to lose some parts of image on screen

to display image according to it's original size i do the following
Image.file(File(fileCreated!.path)),
to display image to fit height and width (full screen) i do the following
Image.file(File(fileCreated!.path),fit: BoxFit.cover,height: double.infinity,width: double.infinity,),
ok it is now in full screen but i noticed there is some missing parts of the image contents is no longer visible on screen like 20 pixel from width and height , i know flutter do it for keeping the image quality the same. but How i ignore that behavior
How could i display image in full screen so the whole image parts is visible on screen ?
i tried with following
height:MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
whatever i change the height. the width will be auto changing too, even if i did not change the width !!!!
but same result
edit :
full parent
#override
Widget build(BuildContext context) {
return Scaffold(
body: Image.file(File(widget.imageFile,),fit: BoxFit.cover,height: MediaQuery.of(context).size.height,),
);
}
}
You can use MediaQuery, but I will prefer using LayoutBuilder on top widget. Also check the parent widget if it is having padding/margin.
Image.file(
File(fileCreated!.path),
fit: BoxFit.fill,
height:MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
),

Is it possible to have a listview maintain its position as images of variable height load in flutter?

Lets say you have a ListView of variable height:
List items are a Container with a mix of text and images. As such, the list items are of variable height. Sometimes no images. The text renders immediately as expected, but the images may take time to retrieve and render on screen
The images are retrieved from the network using CachedNetworkImage
Images are of variable height
When the Screen is opened the ListView automatically scrolls to item#11 (using ensureVisible technique)
So there are items both above and below your current position
At this point, when one of the network images above your position load up, the entire ListView will be pushed and you will no longer be looking at Item #11, rather somewhere randomly higher up
I considered initiating a new scroll in a callback after each image loads, however, due to network speeds, usually the listview scroll will finish before all the images load. If there are a lot of images, the images could take time to load, so it would be unreasonable to initiate a new scroll each time a new image is loaded, the screen just keeps scrolling forward every few seconds. It becomes dizzying and annoying.
Alternatively, the scrollview could jumpTo a new position as soon as the image loads, but I'm imagining there would be a slight delay between the two events and the user perceive a small "glitch" as the image loads and the listview immediately jumps to offset the image load. Even using a Future.microtask there is a very small perceptible 'glitch' as the image loads and the jumpto fires
It would be most preferable to have the listview expand the content upward somehow, so that the users current scroll position is maintained, as far as they are concerned.
Is it possible to have the ListView keep its position as the images load?
Assuming you have a predefined size for your images, you can wrap the image in a SizedBox(). This way your list will always have the same height and your items won't get pushed around.
EDIT:
Since your images are of variable size, I would probably animate to the desired location on every image load.
CachedNetworkImage has a callback
imageBuilder: (context, imageProvider) {
/// Animate to desired index
return Image(image: imageProvider);
}
Animated container, might help you. It can adjust the height automatically, depending on the height u provide in builder.
Also you can use this answer to determnin image height and width in rnutime.
Images are of variable height
To overcome this, Either we take the image size or aspect ratio of the image while storing the image along with other data.
While retrieving data, along with other text data we will receive the aspect ratio or height for the image.
I would use the same height or ratio and show placeholder image till images are loaded.
CachedNetworkImage(
imageUrl: countryList[index].flagUrl,
height: 60, // Set your height according to aspect ratio or fixed height
width: 60,
fit: BoxFit.cover,
placeholder: (_, __) => Container(
alignment: Alignment.center,
height: 60, // Set your height
width: 60,
color: Colors.red.withAlpha(80),
child: Text(
countryList[index].name[0],
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
)
Image must be of specific aspect ratio I believe. You can define height according to aspect ratio.
Try as follows:
ListView.builder(
shrinkWrap: true,
itemCount: images.length,
itemBuilder: (ctx, i) {
return Column(children: [
ButtonItems(i),
const SizedBox(height: 10),
]);
}));
Button Items class
class ButtonItems extends StatefulWidget {
final int i;
ButtonItems(this.i);
#override
_ButtonItems createState() => _ButtonItems();
}
class _ButtonItems extends State<ButtonItems> {
var images = [
"https://opengraph.githubassets.com/2ddb0ff05ef9ccfce35abb56e30d9c5068e01d1d10995484cfd07becee9accf7/dartpad/dartpad.github.io",
null,
"https://opengraph.githubassets.com/2ddb0ff05ef9ccfce35abb56e30d9c5068e01d1d10995484cfd07becee9accf7/dartpad/dartpad.github.io"
];
#override
Widget build(BuildContext context) {
print(images[widget.i]);
return Container(
height: 50,
color: Colors.grey,
child: Row(children: [
AspectRatio(
aspectRatio: 3 / 2,
child: images[widget.i] == null
? Container()
: Image.network(images[widget.i]!, fit: BoxFit.cover),
),
Text("Title " + widget.i.toString()),
]));
}
}

How to implement image fit mode change animation in flutter

This is how I set up a fullscreen image
Image.network(
"https://cdn.pixabay.com/photo/2017/02/21/21/13/unicorn-2087450_1280.png",
fit: BoxFit.contain,
height: double.infinity,
width: double.infinity,
)
I want to implement resize animation to switch between image fit BoxFit.contain or BoxFit.cover(like a lot of video app did)
I am pretty new for the animation behavior, could anyone give a help, thanks.
If you just want to switch the fit value then you'll need to create a StatefulWidget and keep the the fit value as a member variable and then call setState() and change the value in there. There won't be animations but you can take care of that using an animated container. So the steps would be:
Switching fit value
Turn your view widget into a StatefulWidget
Store your BoxFit value as a memberVariable. BoxFit _imageFit
Change _imageFit inside a setState call.
setState(() {_imageFit = BoxFit.fitHeight;});
Animating your image size
Wrap your image in a AnimatedContainer() widget and set it's dimensions using local member variables
double _animatedContainerHeight = yourStartingValue;
double _animatedContainerWidth = yourStartingValue;
...
AnimatedContainer(height: _animatedContainerHeight,
width: _animatedContainerWidth, child: YourImage);
Supply the AnimatedContainer with the duration and interpolation and then Change the size of it in the setState function mentioned above.
setState((){
...
_animatedContainerWidth = newValue;
// same for height
});

How to fix Listview scrolling jank when loading images from network

I am loading images using Image.network for each item in a list using the following code:
Image getEventImageWidget(AustinFeedsMeEvent event) {
return event.photoUrl.isNotEmpty ?
Image.network(
event.photoUrl,
width: 77.0,
height: 77.0,
) : Image.asset(
'assets/ic_logo.png',
width: 77.0,
height: 77.0,
);
}
When I scroll up and down, the list sometimes hangs when loading the images. Is there a way I can load the images on a background thread? What can I do to help fix scrolling performance?
NOTE: When I looked back at this, I found that the images that I was using were really large.
There are two was to speed up the rendering of your ListView of images.
The first is to set the cacheExtent property to a larger value in your ListView constructor. This property controls how much offscreen widgets are rendered, and will help by causing the rendering to start a bit sooner.
The second is to pre-cache your images using precacheImage. Flutter has an in-memory cache, so it is generally to necessary to cache everything to disk to get good read performance. Instead, you can ask Flutter to download these images ahead of time so that they are ready when the widget is built. For example, if you have a list of urls of your image, then in an initState method you could ask Flutter to cache all of them.
final List<String> imageUrls = [ /* ... */ ];
#override
void initState() {
for (String url in imageUrls) {
precacheImage(new NetworkImage(url), context);
}
super.initState();
}
Are you sure your images are not very heavy? Check the size of the images first.
Also you can use the package named: cache_network_image
It's very simple :
new Image(image: new CachedNetworkImageProvider(url))
UPDATE (Package was updated)
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => new CircularProgressIndicator(),
errorWidget: (context, url, error) => new Icon(Icons.error),
),
you can also use:
FadeInImage.assetNetwork(
placeholder: 'assets/ic_logo.png',
image: event.photoUrl,
height: 77.0,
width: 77.0,
fit: BoxFit.cover,
fadeInDuration: new Duration(milliseconds: 100),
),
but yeah, per diegoveloper, you sure your images aren't huge? Listview has no problem rendering anything that's close to reasonable in size.
You can create a Stateful Widget that creates the ListView with placeholder images, then have it have an async method you call after build() that loads the images from network (one by one) and then changes the state of the previously mentioned widget to replace the placeholder with the correct image. As a bonus, you can create a cache that stores the images so they don't have to be downloaded each time the ListView enters scope (here you would have the async method look in the cache for the image and if it doesn't find it there, download it).
As a side note, this would obviously require giving each of the images in the ListView an index.

How to Determine Screen Height and Width

I've created a new application on Flutter, and I've had problems with the screen sizes when switching between different devices.
I created the application using the Pixel 2XL screen size, and because I've had containers with a child of ListView it's asked me to include a height and width for the container.
So when I switch the device to a new device the container is too long and throws an error.
How can I go about making it so the application is optimized for all screens?
You can use:
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
To get height just of SafeArea (for iOS 11 and above):
var padding = MediaQuery.of(context).padding;
double newheight = height - padding.top - padding.bottom;
Getting width is easy but height can be tricky, following are the ways to deal with height
// Full screen width and height
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
// Height (without SafeArea)
var padding = MediaQuery.of(context).viewPadding;
double height1 = height - padding.top - padding.bottom;
// Height (without status bar)
double height2 = height - padding.top;
// Height (without status and toolbar)
double height3 = height - padding.top - kToolbarHeight;
To clarify and detail the exact solution for future researchers:
Without context:
import 'dart:ui';
var pixelRatio = window.devicePixelRatio;
//Size in physical pixels
var physicalScreenSize = window.physicalSize;
var physicalWidth = physicalScreenSize.width;
var physicalHeight = physicalScreenSize.height;
//Size in logical pixels
var logicalScreenSize = window.physicalSize / pixelRatio;
var logicalWidth = logicalScreenSize.width;
var logicalHeight = logicalScreenSize.height;
//Padding in physical pixels
var padding = window.padding;
//Safe area paddings in logical pixels
var paddingLeft = window.padding.left / window.devicePixelRatio;
var paddingRight = window.padding.right / window.devicePixelRatio;
var paddingTop = window.padding.top / window.devicePixelRatio;
var paddingBottom = window.padding.bottom / window.devicePixelRatio;
//Safe area in logical pixels
var safeWidth = logicalWidth - paddingLeft - paddingRight;
var safeHeight = logicalHeight - paddingTop - paddingBottom;
With context:
//In logical pixels
var width = MediaQuery.of(context).size.width;
var height = MediaQuery.of(context).size.height;
var padding = MediaQuery.of(context).padding;
var safeHeight = height - padding.top - padding.bottom;
Extra info about physical and logical pixels for the curious:
https://blog.specctr.com/pixels-physical-vs-logical-c84710199d62
The below code doesn't return the correct screen size sometimes:
MediaQuery.of(context).size
I tested on SAMSUNG SM-T580, which returns {width: 685.7, height: 1097.1} instead of the real resolution 1920x1080.
Please use:
import 'dart:ui';
window.physicalSize;
Using the following method we can get the device's physical height.
Ex. 1080X1920
WidgetsBinding.instance.window.physicalSize.height
WidgetsBinding.instance.window.physicalSize.width
MediaQuery.of(context).size.width and MediaQuery.of(context).size.height works great, but every time need to write expressions like width/20 to set specific height width.
I've created a new application on flutter, and I've had problems with the screen sizes when switching between different devices.
Yes, flutter_screenutil plugin available for adapting screen and font size. Let your UI display a reasonable layout on different screen sizes!
Usage:
Add dependency:
Please check the latest version before installation.
dependencies:
flutter:
sdk: flutter
# add flutter_ScreenUtil
flutter_screenutil: ^0.4.2
Add the following imports to your Dart code:
import 'package:flutter_screenutil/flutter_screenutil.dart';
Initialize and set the fit size and font size to scale according to the system's "font size" accessibility option
//fill in the screen size of the device in the design
//default value : width : 1080px , height:1920px , allowFontScaling:false
ScreenUtil.instance = ScreenUtil()..init(context);
//If the design is based on the size of the iPhone6 ​​(iPhone6 ​​750*1334)
ScreenUtil.instance = ScreenUtil(width: 750, height: 1334)..init(context);
//If you wang to set the font size is scaled according to the system's "font size" assist option
ScreenUtil.instance = ScreenUtil(width: 750, height: 1334, allowFontScaling: true)..init(context);
Use:
//for example:
//rectangle
Container(
width: ScreenUtil().setWidth(375),
height: ScreenUtil().setHeight(200),
...
),
////If you want to display a square:
Container(
width: ScreenUtil().setWidth(300),
height: ScreenUtil().setWidth(300),
),
Please refer updated documentation for more details
Note: I tested and using this plugin, which really works great with all devices including iPad
Hope this will helps someone
Hey you can use this class to get Screen Width and Height in percentage
import 'package:flutter/material.dart';
class Responsive{
static width(double p,BuildContext context)
{
return MediaQuery.of(context).size.width*(p/100);
}
static height(double p,BuildContext context)
{
return MediaQuery.of(context).size.height*(p/100);
}
}
and to Use like this
Container(height: Responsive.width(100, context), width: Responsive.width(50, context),);
How to access screen size or pixel density or aspect ratio in flutter ?
We can access screen size and other like pixel density, aspect ration etc.
with helps of MediaQuery.
syntex : MediaQuery.of(context).size.height
Just declare a function
Size screenSize() {
return MediaQuery.of(context).size;
}
Use like below
return Container(
width: screenSize().width,
height: screenSize().height,
child: ...
)
A bit late as I had asked the question about 2 years ago and was a newbie back then, but thanks all for the responses as at the time when learning it was a massive help.
To clarify, what I probably should have been asking for was a the Expanded widget, as I believe (hazy memory on what I was trying achieve) I was looking to have a ListView as one of the children of a Column. Instead of using the specific screen size to fit this ListView in the Column I should have been looking to optimise the maximum space available, therefore wrapping the ListView in the Expanded would have had the desired impact.
MediaQuery is great, but I try only to use it to decipher what form factor the screen is using the Material breakpoints, otherwise I try to use the Expanded/Spacer widgets as much as possible, with BoxConstaints on minimum/max sizes, also need to consider the maximum space that is actually available using the SafeArea widget to avoid notches/navigation bar,
Initally I also got stucked in to the issue.
Then I got to know that for mobile we get the exact screen height using MediaQuery.of(context).size.height but for web we will not use that approach so i have use window.screen.height from dart.html library then I also added the max screen size that we can use in web by making some calculations...
import 'dart:html';
getViewHeight =>
window.screen!.height! *
((window.screen!.height! -
kToolbarHeight -
kBottomNavigationBarHeight -
120) /
window.screen!.height!);
kIsWeb
? getViewHeight
: MediaQuery.of(context).size.height * 0.7)
By Using this approach we get max usable screen size dynamically.
import 'dart:ui';
var pixelRatio = window.devicePixelRatio;
//Size in physical pixels
var physicalScreenSize = window.physicalSize;`
Very good, problem is that when you build for --release it does not work.
The reason is that Size is zero at app start so if the code is fast the value of phisicalSize is (0.0, 0.0).
did you find a solution for this ?