CachedNetworkImage in flutter does not work on local images - flutter

This maybe a stupid question given the name but does CachedNetworkImage works on local images?
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class SigninPage extends StatelessWidget {
const SigninPage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Welcome"),
),
body:
Center(
child: CachedNetworkImage(
imageUrl: 'assets/images/apple_cartoon.png',
imageBuilder: (context, imageProvider) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider, fit: BoxFit.cover)));
},
placeholder: (context, url) =>
const CircularProgressIndicator(),
errorWidget: (context, url, error) => const Icon(Icons.error),
),
),
);
}
}
This returns the error icon
Using Image.asset instead works fine, so I assume that you cannot use local images with CachedNetworkImage but couldn't find any confirmation online and want to be sure this was the case.

CachedNetworkImage is used to retrieve an image from the internet to store it in a cache and then be able to redisplay it even if you have no connection. While your local image doesn't need a connection to be displayed. That's why you can only give a url in the imageUrl field of the Widget

CachedNetworkImage is a flutter library to show images from the internet and keep them in the cache directory.

Related

error with PhotoView : Looking up a deactivated widget's ancestor is unsafe

I have read few stackoverflow posts about "Looking up a deactivated widget's ancestor is unsafe" error but couldn't find an answer which work.
I've tried to set a global key with the scaffold, and to use WidgetsBinding.instance.addPostFrameCallback() without success.
I'm pretty sure I'm doing something stupid and easy to fix, but I can't figure out what.
This is a simple version of the code which replicates the error when you go back from PhotoViewPage (photo_view package) :
my_home_page.dart
import 'package:flutter/material.dart';
import 'package:phototest/photo_view_page.dart';
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton(
child: const Text("to PhotoView"),
onPressed: () => _toPhotoView(context),
);
}
void _toPhotoView(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute<dynamic>(
builder: (BuildContext context) => const PhotoViewPage(),
),
);
}
}
photo_view_page.dart
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
class PhotoViewPage extends StatelessWidget {
const PhotoViewPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return PhotoView(imageProvider: AssetImage("assets/image.png"));
}
}
UPDATE - Fixed in photo_view 0.14.
The problem is coming from photo_view package.
In pubspec.yaml remove photo_view from dependecies
dependencies:
photo_view: ^0.13.0
Add:
dependency_overrides:
photo_view:
git:
url: https://github.com/bluefireteam/photo_view
ref: master
This way you will avoid errors in dependencies that come from the same version.
To be honest you would be better off using InteractiveViewer than some unstable library for example
child: InteractiveViewer(
child: Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(color: Colors.transparent, borderRadius: BorderRadius.circular(0)),
child: CachedNetworkImage( imageUrl: image,
imageBuilder: (context, imageProvider) => Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5) ,
image: DecorationImage(
image: imageProvider, fit: BoxFit.scaleDown,
),
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator(color: Colors.black,)),
errorWidget: (context, url, error) => const Icon(Icons.error),),
margin: const EdgeInsets.symmetric(horizontal: 20),
),
),
you archieve the same effect if all you need is to zoom in
OR
You can rebuild and stop using hot reload
I'm not sure where the error was from, but switching flutter channel from master to stable fixed it.
flutter channel stable
flutter upgrade --force

State not updating in dialog box when using providers

I am trying to open a dialog box to let users add 2 things:
1- Video thumbnail(Image file)
Video URL(String)
The user taps on an Inkwell which opens the dialog box, then the user clicks the inkwell in the dialog box to pick a video thumbnail i.e image file. The image file gets set, but it doesn't update the state of the inkwell which needs to show the picked file. The state is updated after hot reload. I am using the provider to set the image file and using its instance to check if the file is picked or not.
Change Notifier ->
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class GetThumbnailImage extends ChangeNotifier {
PickedFile? image;
Future setImage(var file) async {
image = file;
notifyListeners();
}
}
Widgets using the change notifier ->
InkWell(
onTap: () {
getVideoImage().then((image) {
print(image);
if (image != null) {
_imageInstance.setImage(image);
}
});
},
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.25,
child: Center(
child: _imageInstance.image == null
? SizedBox(
width: 200,
height: 200,
child: FadeInImage(
image: NetworkImage(
apiBase + productId.toString() + apiLaterPart),
placeholder:
AssetImage("assets/images/Otherimages/addvideo.jpg"),
imageErrorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/images/Otherimages/addvideo.jpg',
fit: BoxFit.fitWidth);
},
fit: BoxFit.fitWidth,
),
)
: Image.file(
File(_imageInstance.image!.path),
),
),
))
Add a consumer widget on top of widgets that you want to rebuild
Center(
child: Consumer<GetThumbnailImage>(
builder:(context, model, child) => model.image == null
? SizedBox(
width: 200,
height: 200,
child: FadeInImage(
image: NetworkImage(apiBase +
productId.toString() +
apiLaterPart),
placeholder: AssetImage(
"assets/images/Otherimages/addvideo.jpg"),
imageErrorBuilder:
(context, error, stackTrace) {
return Image.asset(
'assets/images/Otherimages/addvideo.jpg',
fit: BoxFit.fitWidth);
},
fit: BoxFit.fitWidth,
),
)
: Image.file(
File(model.image!.path),
),
),
),
made this changes to your code and try
class GetThumbnailImage extends ChangeNotifier {
PickedFile? _image;
PickedFile get image => _image;
Future setImage(var file) async {
_image = file;
notifyListeners();
}
}
The reason is "dialog is not part of the widget tree"
Your code is not complete to help but here are some suggestions.
Solution 1.
You can pass the context of the parent widget to the dialog box, So the dialog box should also behave like a part of the widget tree
Solution 2. (Better)
Use something like this in the showDialogBox method...
final image = context.watch<GetThumbnailImage>();
ChangeNotifierProvider.value(
value: image
child: Dialog(...)
);
Now you can use instance of provider in the dialog box and it will also rebuild the widget tree to update the UI

Dynamic S3 Bucket URL Caching Flutter

I would like to cache a Network Image from an S3 bucket URL against an id or key. I tried to use the key: parameter but it didn't work. Please advise how I can achieve this. Heres the Cached Network Image code with the 'key' parameter that doesn't seem to do what I intend.
CachedNetworkImage(
key: Key(uni['university_name'].toString()), // Doesn't seem to do the job
imageUrl: uni['image_url'],
placeholder: (context, url) => CardPlaceHolder(),
errorWidget: (context, url, error) => Padding(
padding: EdgeInsets.all(21),
child: Icon(
Icons.error,
size: 30,
color: Colors.red.withOpacity(0.8),
),
),
imageBuilder: (context, imageProvider) => ...
cached_network_image has been updated to handle keys with the cacheKey value. Check out the code below:
CachedNetworkImage(
cacheKey: "my-key",
imageUrl: _url!,
fit: BoxFit.cover,
progressIndicatorBuilder:
(context, url, downloadProgress) =>
Center(
child: Platform.isAndroid
? CircularProgressIndicator()
: CupertinoActivityIndicator(),
),
)
Currently CachedNetworkImage lack cached image retrival by key
Key you used is Widgets key, not related with CachedNetworkImage behavior
Let see what is in inside of CachedNetworkImage
cached_network_image depends on flutter_cache_manager
And flutter_cache_manager has implementation (issue #179) of retrival by key but it still not released in any version
So CachedNetworkImage will miss this functionality till flutter_cache_manager version has released it new versions
Though we can use this functionality, but we need to implement widget from our side
Temporary solution
depend on flutter_cache_manager
/// this is pubspec dependencies:
flutter_cache_manager:
git:
url: https://github.com/Baseflow/flutter_cache_manager
path: flutter_cache_manager
ref: f109a48c31c4b3ed0a2c1ea2a5749c495e4fa2f4
evolve this naive implementaion to match your use case
FutureBuilder<File>(
future: DefaultCacheManager().getSingleFile(
'url', key: 'file-id'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image.file(snapshot.data);
}
// implement here some behavior like CachedNetworkImage does
return CircularProgressIndicator();
},
);
PS I'll update answer when CachedNetworkImage will support

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,
),

Better way to load images from network flutter

I am trying to load images from network and show them in a GridView. I am using a StatefulWidget and loading the images inside the build method. But according to my understanding its not good to make a network call inside the build method. How can I download images from the network inside my BLoC file and later pass the list of downloaded images to the widget? Below is my current implementation.
class MovieList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MovieListState();
}
}
class MovieListState extends State<MovieList> {
#override
void initState() {
super.initState();
bloc.fetchAllMovies();
}
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Popular Movies'),
),
body: StreamBuilder(
stream: bloc.allMovies,
builder: (context, AsyncSnapshot<ItemModel> snapshot) {
if (snapshot.hasData) {
return buildList(snapshot);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(child: CircularProgressIndicator());
},
),
);
}
Widget buildList(AsyncSnapshot<ItemModel> snapshot) {
return GridView.builder(
itemCount: snapshot.data.results.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return GridTile(
child: InkResponse(
enableFeedback: true,
child: Image.network(
'https://image.tmdb.org/t/p/w185${snapshot.data
.results[index].poster_path}',
fit: BoxFit.cover,
),
onTap: () => openDetailPage(snapshot.data, index),
),
);
});
}
openDetailPage(ItemModel data, int index) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return MovieDetailBlocProvider(
child: MovieDetail(
title: data.results[index].title,
posterUrl: data.results[index].backdrop_path,
description: data.results[index].overview,
releaseDate: data.results[index].release_date,
voteAverage: data.results[index].vote_average.toString(),
movieId: data.results[index].id,
),
);
}),
);
}
}
You can use loadingBuilder which is inbuilt feature from flutter for Image.Network
Image.network(
widget.networkUrl,
fit: BoxFit.fill,
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,
),
);
},
),
I would recommend you to use
https://pub.dartlang.org/packages/cached_network_image
It's really works good for my cases.
Simple code example from their r
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => new CircularProgressIndicator(),
errorWidget: (context, url, error) => new Icon(Icons.error),
),
or
Image(image: CachedNetworkImageProvider(url))
You should add to the pubspec file
cached_network_image: <actual version here>
into the dependencies section
If you want a circular shape for your image. You can use Circle Avatar in such a way that it will act as loader and displayer both....
Parent circle avatar will be having loader and If we put transparent color to child circle avatar it will show loading until it is loaded...
Plus point with this way is you can simply give border also by setting background color of parent circle avatar and increasing it's radius slightly.
CircleAvatar(
backgroundColor: Colors.red,
radius: 65,
backgroundImage: AssetImage('assets/bottombar/loading.gif'),
child: CircleAvatar(
radius: 65,
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(_url),
),
),
You can also use FadeInImage
https://flutter.dev/docs/cookbook/images/fading-in-images
FadeInImage.assetNetwork(
placeholder: 'assets/loading.gif',
image: 'https://picsum.photos/250?image=9',
),
It is a good practice to handle errors related to, for example, lack of Internet, when trying to load an image from the Internet. ErrorBuilder is really good if you use Image.network()
Image.network(
'https://example.does.not.exist/image.jpg',
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
// Appropriate logging or analytics, e.g.
// myAnalytics.recordError(
// 'An error occurred loading "https://example.does.not.exist/image.jpg"',
// exception,
// stackTrace,
// );
return Text('😢');
},
),
Best way to Handle Network Images
The best way to load image from the network in flutter is to use flutter's built-in network function and customize it according to your requirements, I do not recommend/prefer to use any package like CachedNetworkImage because it sometimes works on mobile phone and give unexpected error on the web.
You can manage the Network image like this:
Image.network(
netWorkImageURL,
fit: BoxFit.fill,
// When image is loading from the server it takes some time
// So we will show progress indicator while loading
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,
),
);
},
// When dealing with networks it completes with two states,
// complete with a value or completed with an error,
// So handling errors is very important otherwise it will crash the app screen.
// I showed dummy images from assets when there is an error, you can show some texts or anything you want.
errorBuilder: (context, exception, stackTrace) {
return Image.asset(
AppAssets.dummyPostImg,
fit: BoxFit.cover,
height: (widget.hideBelowImage == null ||
widget.hideBelowImage == false)
? 170.h
: 130.h,
width: double.infinity,
);
},
),