I am trying to use NetworkImage as the image in a BoxDecoration for a Container. The url passed to the NetworkImage will sometimes hold a bad image type (the url works and is correct but the actual image at the specified url is bad) resulting in an error.
To handle this occurrence I set up a method that uses a try-catch block where it returns the NetworkImage if successful, and a preset AssetImage in the event of an error. The try-catch block is not handling this exception, and throws an error instead of returning the AssetImage specified in the catch.
I've seen that Image.network has an onError parameter which looks like it would solve the problem, but Image.network is of type "Image" and BoxDecoration requires an "ImageProvider" (NetworkImage, AssetImage), so that does not help in this case.
Which is the best way to handle this error so that I can show an AssetImage in the case of the NetworkImage throwing an error?
Here is the Widget holding the BoxDecoration where I call the method I created to handle fetching the NetworkImage:
class CharacterPreviewCard extends StatelessWidget {
final CharacterPreview character;
const CharacterPreviewCard({Key? key, required this.character})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
context.router
.push(CharacterDetailsRoute(characterId: character.characterId));
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 171,
width: 171,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.black,
image: DecorationImage(
image: getCharacterAvatar(character.characterAvatarUrl),
fit: BoxFit.fill,
),
),
),
const SizedBox(height: smallMargin),
Padding(
padding: const EdgeInsets.only(left: smallMargin),
child: SizedBox(
width: 165,
child: Text(
character.characterName,
style: Theme.of(context).textTheme.bodyLarge,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
);
}
Here is the method "getCharacterAvatar" which should return either NetworkImage or AssetImage:
ImageProvider<Object> getCharacterAvatar(String url) {
try {
final image = NetworkImage(url);
return image;
} catch (e) {
return const AssetImage('assets/images/village-not-found-logo.png');
}
}
And here is the error in Debug Console:
The following _Exception was thrown resolving an image codec:
Exception: Invalid image data
When the exception was thrown, this was the stack
#0 _futurize (dart:ui/painting.dart:5718:5)
#1 ImageDescriptor.encoded (dart:ui/painting.dart:5574:12)
#2 instantiateImageCodec (dart:ui/painting.dart:2056:60)
<asynchronous suspension>
Image provider: NetworkImage("https://narutoql.s3.amazonaws.com/Hana.jpg", scale: 1.0)
Image key: NetworkImage("https://narutoql.s3.amazonaws.com/Hana.jpg", scale: 1.0)
Currently, there is no way to catch errors with NetworkImage or Image.network. More on this can be found here: https://github.com/flutter/flutter/issues/20910.
Thanks to Tom3652's comment suggesting the use of CachedNetworkImage I was able to find a solution that works using the Widget CachedNetworkImage with an errorWidget parameter to display an AssetImage when an error was thrown.
I replaced the Container that had the NetworkImage as a parameter for DecorationImage with a custom widget (to minimized code in the file). The custom widget returns the CachedNetworkImage.
Here is the solution that worked for me:
class PreviewCardImage extends StatelessWidget {
final String url;
final AssetImage errorImage;
const PreviewCardImage({
Key? key,
required this.url,
required this.errorImage,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return CachedNetworkImage(
imageUrl: url,
imageBuilder: (context, imageProvider) => Container(
height: 171,
width: 171,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.black,
image: DecorationImage(
image: imageProvider,
fit: BoxFit.fill,
),
),
),
placeholder: (context, url) => const CircularProgressIndicator(),
errorWidget: (context, url, error) => Container(
height: 171,
width: 171,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.black,
image: DecorationImage(
image: errorImage,
fit: BoxFit.fill,
),
),
),
);
}
}
I think you should use the Image.network class replacing your NetworkImage.
It contains especially an errorBuilder parameter that allows you to set a custom widget in case of error :
Image.network(
String src,
{Key? key,
double scale = 1.0,
ImageFrameBuilder? frameBuilder,
ImageLoadingBuilder? loadingBuilder,
ImageErrorWidgetBuilder? errorBuilder,
...
int? cacheWidth,
int? cacheHeight}
)
If you need the ImageProvider, then your method can look like this :
ImageProvider<Object> getCharacterAvatar(String url) {
final image = Image.network(url, errorBuilder: (context, object, trace) {
return Image(image: AssetImage('assets/images/village-not-found-logo.png'));
},).image;
return image;
}
Please note the .image at the end of the Image.network().image to return the ImageProvider.
Related
I am using dhiwise to convert my figma prototype into flutter but the header is not appearing and is somehow above the screen. I tried moving it elsewhere but it just puts it outside the header.
current look
what it is supposed to look like
return SafeArea(
child: Scaffold(
backgroundColor: ColorConstant.whiteA700,
body: Container(
height: size.height,
width: size.width,
child: Stack(
children: [
Align(
alignment: Alignment.center,
child: SingleChildScrollView(
child: Container(
height: size.height,
width: size.width,
child: Stack(
alignment: Alignment.topRight,
children: [
Align(
alignment: Alignment.center,
child: Container(
height: size.height,
width: size.width,
decoration: BoxDecoration(
color: ColorConstant.whiteA700,
),
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
width: size.width,
padding: getPadding(
left: 11,
top: 7,
right: 11,
bottom: 7,
),
decoration: BoxDecoration(
color: ColorConstant.blue200,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
CustomIconButton(
height: 53,
width: 53,
margin: getMargin(
bottom: 276,
),
child: CustomImageView(
svgPath: ImageConstant.imgUser,
the custom image view code, this code was directly from the website itself and I have no idea what any of the code means to do
// ignore_for_file: must_be_immutable
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class CustomImageView extends StatelessWidget {
///[url] is required parameter for fetching network image
String? url;
///[imagePath] is required parameter for showing png,jpg,etc image
String? imagePath;
///[svgPath] is required parameter for showing svg image
String? svgPath;
///[file] is required parameter for fetching image file
File? file;
double? height;
double? width;
Color? color;
BoxFit? fit;
final String placeHolder;
Alignment? alignment;
VoidCallback? onTap;
EdgeInsetsGeometry? margin;
BorderRadius? radius;
BoxBorder? border;
///a [CustomImageView] it can be used for showing any type of images
/// it will shows the placeholder image if image is not found on network image
CustomImageView({
this.url,
this.imagePath,
this.svgPath,
this.file,
this.height,
this.width,
this.color,
this.fit,
this.alignment,
this.onTap,
this.radius,
this.margin,
this.border,
this.placeHolder = 'assets/images/image_not_found.png',
});
#override
Widget build(BuildContext context) {
return alignment != null
? Align(
alignment: alignment!,
child: _buildWidget(),
)
: _buildWidget();
}
Widget _buildWidget() {
return Padding(
padding: margin ?? EdgeInsets.zero,
child: InkWell(
onTap: onTap,
child: _buildCircleImage(),
),
);
}
///build the image with border radius
_buildCircleImage() {
if(radius!=null) {
return ClipRRect(
borderRadius: radius,
child: _buildImageWithBorder(),
);
}
else{
return _buildImageWithBorder();
}
}
///build the image with border and border radius style
_buildImageWithBorder(){
if(border!=null) {
return Container(
decoration: BoxDecoration(
border: border,
borderRadius: radius,
),
child: _buildImageView(),
);
}else{
return _buildImageView();
}
}
Widget _buildImageView() {
if (svgPath != null && svgPath!.isNotEmpty) {
return Container(
height: height,
width: width,
child: SvgPicture.asset(
svgPath!,
height: height,
width: width,
fit: fit ?? BoxFit.contain,
color: color,
),
);
} else if (file != null && file!.path.isNotEmpty) {
return Image.file(
file!,
height: height,
width: width,
fit: fit ?? BoxFit.cover,
color: color,
);
} else if (url != null && url!.isNotEmpty) {
return CachedNetworkImage(
height: height,
width: width,
fit: fit,
imageUrl: url!,
color: color,
placeholder: (context, url) => Container(
height: 30,
width: 30,
child: LinearProgressIndicator(
color: Colors.grey.shade200,
backgroundColor: Colors.grey.shade100,
),
),
errorWidget: (context, url, error) => Image.asset(
placeHolder,
height: height,
width: width,
fit: fit ?? BoxFit.cover,
),
);
} else if (imagePath != null && imagePath!.isNotEmpty) {
return Image.asset(
imagePath!,
height: height,
width: width,
fit: fit ?? BoxFit.cover,
color: color,
);
}
return SizedBox();
}
}
1- flutter clean
2- flutter pub get
And if it doesn't work with these instructions, I suggest you look at the link below
enter link description here
I hope you specified that image as an asset in the pubspec.yaml.
I mean...
Flutter uses the pubspec.yaml file, located at the root of your project, to identify assets required by an app.
flutter:
assets:
- assets/my_icon.png
- assets/background.png
To include all assets under a directory, specify the directory name with the / character at the end:
flutter:
assets:
- directory/
- directory/subdirectory/
If you already specify path of your image (sometimes you need to hot restart if you add new asset)
please check that svg image was fine by opening it via web. I face the same issues before with svg using SvgPicture I can open svg file on web but not readble in mobile apps. As I remember my error was not valid svg.
This library only supports <defs> and xlink:href references that are defined ahead of their references.
If do so, maybe you can try this one.
save image as PNG from Figma
Import image to Adobe XD
Export to SVG.
Replace current asset with this Adobe XD svg format.
I am struggling to set up the correct layout for my composite widgets.
It is the Stack widget containing two images each wrapped in the corresponding widget which are applying some visual effects on the images.
The images are supposed to change every couple of seconds and then I am using the AnimatedSwitcher to animate the fading transition between them.
This is how it looks now:
The result I want to achieve should look like this:
Here is the source code of the corresponding widget:
import 'dart:ui';
import 'package:demo_flutter_fading_images/themes/style.dart';
import 'package:flutter/material.dart';
class ImagesStack extends StatefulWidget {
final String imagePath;
const ImagesStack({required Key key, required this.imagePath}) : super(key: key);
#override
State<ImagesStack> createState() => _ImagesStackState();
}
class _ImagesStackState extends State<ImagesStack> {
#override
Widget build(BuildContext context) {
return Center(
child: Stack(children: <Widget>[
ImageFiltered(
imageFilter: ImageFilter.blur(
sigmaX: 6,
sigmaY: 6,
),
child: Container(
// constraints: const BoxConstraints.expand(),
constraints: BoxConstraints.tight(const Size(360, 500)),
decoration: BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: AssetImage(widget.imagePath),
fit: BoxFit.fill,
),
),
),
),
Container(
margin: const EdgeInsets.fromLTRB(8, 4, 8, 4),
decoration: frontImageBoxDecoration,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
widget.imagePath,
fit: BoxFit.fill,
),
),
),
]),
);
}
}
And the full source code of demo project:
github - demo project
I tried it quickly on dartpad.
https://dartpad.dev/?id=3c24c716a9844b706662cb495675f56d
You can refer to the code to follow the structure and make changes. I have left some comments to help understand the code.
Try resizing the window after running the app in dart to see how the image gets positioned for different sizes.
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
Now I am using CircleAvatar to show a background image:
CircleAvatar(
radius: 20,
backgroundColor: Colors.transparent,
backgroundImage: foregroundImage.image,
),
but now it always show this error:
======== Exception caught by image resource service ================================================
The following _Exception was thrown resolving an image codec:
Exception: Invalid image data
When the exception was thrown, this was the stack:
#0 _futurize (dart:ui/painting.dart:5275:5)
#1 ImageDescriptor.encoded (dart:ui/painting.dart:5143:12)
#2 instantiateImageCodec (dart:ui/painting.dart:1999:60)
<asynchronous suspension>
Image provider: NetworkImage("https://static.poemhub.top/2021/5/18/laitn.github.io-favicon.ico", scale: 1.0)
Image key: NetworkImage("https://static.poemhub.top/2021/5/18/laitn.github.io-favicon.ico", scale: 1.0)
====================================================================================================
To fix this problem, I tried this way:
final Image defaultImage = Image.asset('images/Icon-App-83.5x83.5#3x.png');
var foregroundImage = counter.value.favIconUrl == "" ? defaultImage : Image.network(
global.staticResourceUrl + "/" + counter.value.localIconUrl,
loadingBuilder: (context,child,loadingProgress)=>(loadingProgress == null) ? child : CircularProgressIndicator(),
errorBuilder: (context, error, stackTrace) => defaultImage,
);
if image fetch failed or other error, set a default image. But still not fix this problem. I figure out maybe the image think this url icon correct, but CircleAvatar render failed. Is it possible to check the image valid before render using CircleAvatar? what should I do to avoid this problem? How to know the image could render successful? I also have tried to set both foreground picture and background picture like this:
CircleAvatar(
radius: 20,
backgroundColor: Colors.transparent,
foregroundImage: foregroundImage.image,
backgroundImage: defaultImage.image,
),
but it looks like this:
both have background and foreground picture.
bool isErrorOccured = false;
CircleAvatar(
radius: 36,
child: isErrorOccured?Image.asset(""):Image.network(""),
onBackgroundImageError: (_, __) {
setState(() {
isErrorOccured = true;
});
},
),
or
SizedBox(
height: height,
width: height,
child: ClipRRect(
child: networkImageUrl == ''
? Image.asset(assetImageUrl, fit: BoxFit.cover)
: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: networkImageUrl,
placeholder: (context, url) => const Center(
child: CupertinoActivityIndicator(),
),
errorWidget: (context, url, error) => Container(
color: Colors.grey[300],
child: const Icon(
Icons.error,
color: Colors.red,
),
),
),
borderRadius: BorderRadius.circular(height / 2),
))
I'm trying to create a widget that is a container and takes two arguments, the path to the image and the title of the Image. The widget code so far is:
class CharacterBox extends StatelessWidget {
final String imagePath;
final String characterName;
CharacterBox(this.imagePath, this.characterName);
#override
Widget build(BuildContext context) {
final CharacterBox args = ModalRoute.of(context).settings.arguments;
return Container(
margin: EdgeInsets.all(20.0),
height: 200,
width: 100,
child: Column(
children: [
Expanded(
child: Image(
image: AssetImage(args.imagePath),
alignment: Alignment.center,
fit: BoxFit.contain,
),
),
Container(
margin: EdgeInsets.all(5.0),
child: Text(
args.characterName,
style: TextStyle(color: Colors.white),
),
)
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Color.fromARGB(255, 252, 70, 82)),
);
}
}
And I'm using the following to pass the arguments:
body: SafeArea(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CharacterBox("assets/artwork.png", "Character"),
],
),
),
However I get the error saying:
The getter 'imagePath' was called on null.
Receiver: null
Tried calling: imagePath
I guess it is related to the ModalRoute declaration since I was doing it with this Documentation. However, I still didn't quiet get it.
You're using args.imagePath should only be imagePath
Remove final CharacterBox args = ModalRoute.of(context).settings.arguments; since you're already passing arguments via the constructor.
To improve the code readability and also perfomance I'd advice to following:
You can append const on the constructor.
I'd change to this and use name parameters for clarity:
class CharacterBox extends StatelessWidget {
final String imagePath;
final String characterName;
const CharacterBox({
Key key,
this.imagePath,
this.characterName
}) : super(key: key);
no need to write args.imagePath and args.characterName
u can directly call it as imagePath and characterName
Image(
image: AssetImage(imagePath),
alignment: Alignment.center,
fit: BoxFit.contain,
),
this is for using route name navigation in flutter
since you are passing the argument in constructor and not Navigator
you can directly use imagePath and characterName like
Image(
image: AssetImage(imagePath),
alignment: Alignment.center,
fit: BoxFit.contain,
),
also you can remove this line from your build function its unnecessary
final CharacterBox args = ModalRoute.of(context).settings.arguments;
it is used to get the arguments passed during Navigation like
Navigator.of(context).pushNamed('/characterBoxPage',arguments:);
Here you can read more about Navigate with arguments
but in your case its similar to a function call and argument passing that happens normally with constructor.
Let me know in comments if you need any more help