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
Related
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.
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'm still new to Flutter Web. I have 3 lines in my flutter web, the first line is the welcome message, the second line is a product and the last is contact, to see those lines user needs to do a scroll on my web. But how can I wrap my code using the Scroll function in flutter web? this is my code.
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Column(
children: [
Container(
// Code Line 1
height: size.height,
width: size.width,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/container.jpg"),
fit: BoxFit.fitWidth),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
CusAppBar(),
Spacer(),
Body(),
Spacer(
flex: 1,
),
],
),
),
Container(
// Code Line 2 Here
),
Container(
// Code Line 3 Here
)
],
),
);
}
}
My background is react, usually we just use tag ScrollView outside the container so the user can scroll the page. But how I can implement it on flutter web?
Thank you.
Try to add your fist Column inside SingleChildScrollView like below hope it help you:
body: SingleChildScrollView(
child:Column(
children:[
//Declare Your Widgets Here
],
),
),
so today i did good with advancing in learning to make an app with flutter , i made a ListView , but the image didnt take the full height and with of the parent element , i tried some solutions but it didnt workout
this is the class
class FlowersItem extends StatelessWidget {
final String imgAssetPath;
FlowersItem({this.imgAssetPath});
#override
Widget build(BuildContext context) {
return Container(
width: 200,
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 30),
margin: EdgeInsets.only(right: 16),
decoration: BoxDecoration(
color: Color(0xff29404E),
borderRadius: BorderRadius.circular(12)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(children: <Widget>[
ClipRect(
child: FittedBox(
child: Image.asset(imgAssetPath),
fit: BoxFit.fill,
),
)
],)
],
),
);
}
}
List<FlowersItem> flowers = [
FlowersItem(
imgAssetPath: 'assets/images/flower1.jpeg',
),
FlowersItem(
imgAssetPath: 'assets/images/flower-pot.png',
),
FlowersItem(
imgAssetPath: 'assets/images/flower-pot.png',
),
FlowersItem(
imgAssetPath: 'assets/images/flower-pot.png',
),
FlowersItem(
imgAssetPath: 'assets/images/flower-pot.png',
),
];
Have you tried BoxFit.cover? I think that's what you are looking for.
It generally works for me when I want to fill out and entire box with an image.
have you tried Expanded class?
So, Expanded class is A widget that expands a child of a Row, Column, or Flex so that the child fills the available space.
you could find the documentation here.
I want to build a widget that displays an image as a background behind some content. I know I can do this with a DecorationImage the problem is that I want the image to fade in as it might not be available right away.
So I want it to look like this after the image has faded in.
class DecorationExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fitWidth,
image: NetworkImage(
'https://images.pexels.com/photos/414612/pexels-photo-414612.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500'),
),
),
child: Column(
// Center the content dead center.
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//Expand the column to take up the availble width
Container(width: double.infinity),
Text('Can be'),
Text('any'),
Text('size'),
Text('Depending on the number of rows')
],
),
);
}
}
My first instinct is to use a stack. The problem is that I need the stack to constrain itself to the height of the column which may vary depending on the content.
import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';
class StackedImageTest extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
width: double.infinity,
child: _fadeInImage(),
),
_content(),
],
);
}
_content() => Column(
// Center the content dead center.
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//Expand the column to take up the availble width
Container(width: double.infinity),
Text('Can be'),
Text('any'),
Text('height'),
Text('Depending on the number of rows')
],
);
_fadeInImage() => FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
fit: BoxFit.fitWidth,
image: 'https://images.pexels.com/photos/414612/pexels-photo-414612.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
);
}
To run the example include this dependency in your pubspec.yaml file:
transparent_image: ^1.0.0
So basically how can I achieve the same effect as with a decoration image(DecorationExample) but make it so that the image fades nicely into view(like in the StackedImageTest widget)?
Pretty simple as it turns out😅
Wrapping the first layer in the stack with a Positioned.fill() seems to do the trick
class FadeInDecorationContainer extends StatelessWidget {
final Widget child;
final String imgUrl;
const FadeInDecorationContainer({Key key, this.child, this.imgUrl}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned.fill(child: _fadeInImage()),
child,
],
);
}
_fadeInImage() => FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
fit: BoxFit.fitWidth,
image: imgUrl,
);
}
To run the example include this dependency in your pubspec.yaml file:
transparent_image: ^1.0.0