Related
I want to create a Card that is reusable with Image. Am I on the right track in the new type of Card? I do not know how to put the Image on the card. all the question regarding the reusable widget card type in stackoverflow and youtube seems old and i dont know if it is truly working in the newer version.
Prototype Figma of My vision of Card in the HomePage
Here is the example for the clarifcation of the image on the background
this is the previous code that I want to be scrapped because they are too many.
Container(
padding: const EdgeInsets.all(8),
color: const Color.fromARGB(255, 75, 175, 78),
child: Center(
child: TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
const SecondPage(
plantname: 'Bell Pepper')));
},
child: const Text(
"Bell Pepper",
style: TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)),
)),
)),
This the new type of Card that I want to be the reusable. But I dont know how to put the image and make it better.
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/cli_commands.dart';
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final Image imageOfPlant;
const ListViewCard(
{super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 0,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
width: 150,
height: 200,
child: Text(title,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)),// textstyle
),),//text //SizedBox
], // <widget>[]
), // column
), //inkwell
); // card
}
}
Make a asset folder in your project like this
Add you image(jpeg, png or other) to this folder
Go to pubspec.yaml & add your asset path
To add assets to your application, add an assets section, like this:
assets:
- assets/
Make this changes in your ListViewCard widget:
import 'package:flutter/material.dart';
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final String imageOfPlant; //Change to String
const ListViewCard(
{super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 0,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset(
imageOfPlant,
height: 200,
width: 150,
fit: BoxFit.cover,
),
SizedBox(
width: 150,
height: 50,
child: Center(
child: Text(title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)),// textstyle
),
),),//text //SizedBox
], // <widget>[]
), // column
), //inkwell
); // card
}
}
Use your card
ListViewCard(
title: 'Lotus', onTap: () {}, imageOfPlant: 'assets/image.jpg')
OUTPUT:
You can use Stack widget for this,
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final Image imageOfPlant;
const ListViewCard({
super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 0,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Stack(
children: [
Positioned.fill( // or positioned with top.left,bottom,right
child: imageOfPlant,
),
Align(
alignment: Alignment.bottomCenter,//based on your need
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
width: 150,
height: 200,
child: Text(
title,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
), //text //SizedBox
], // <widget>[]
),
),
],
), // column
), //inkwell
); // card
}
}
Also GridTile has similar look.
Instead of pass image widget inside constructor, pass its path, here I use asset image you can also network image too. Try this:
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final String imageOfPlantPath;
const ListViewCard({
super.key,
required this.title,
required this.onTap,
required this.imageOfPlantPath,
});
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 2,
),
borderRadius: BorderRadius.all(Radius.circular(20))),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
child: Container(
height: 300,
width: 150,
clipBehavior: Clip.antiAliasWithSaveLayer,
alignment: Alignment.bottomCenter,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(imageOfPlantPath),
fit: BoxFit.cover),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: LayoutBuilder(builder: (context, constraints) {
return Container(
alignment: Alignment.center,
width: constraints.maxWidth,
height: constraints.maxHeight * 0.5,
decoration: BoxDecoration(
color: Color.fromARGB(255, 75, 175, 78),
border: Border(
top: BorderSide(
color: Colors.black,
width: 2,
),
),
),
child: Text(
title,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
);
}), //text //S,
),
), //inkwell
); // card
}
}
Note: inside LayoutBuilder you can play with height and do what you want, I set half of the image here.
Instead of using Image as DecorationImage, you can use Stack that #YeasinSheikh said blew.
I'm working on an app and in the begin I extracted a widget to make my code clean and organized..
When I give the constracter the parameters, it force me to add "required" keyword, which it wasn't necessary befor!!
How can I ignore it?
class NewWidget extends StatelessWidget {
NewWidget({this.text, this.onPress});
final String text;
final Function onPress;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Card(
color: Colors.blueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: TextButton(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 100.0),
child: Text(
text,
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
onPressed: onPress(),
),
),
);
}
}
And this is the notification I had for it..
The parameter 'text' can't have a value of 'null' because of its type, but the implicit default value is 'null'. Try adding either an explicit non-'null' default value or the 'required' modifier.
It comes from Dart's nullsafety, your two parameters are defined as non-nullable variables but they are optional inside of your constructor which means the compiler cannot infer if the type should be String or String? for your text variable and cannot determine if onPress is a Function or Function?.
You either need to pass your parameters as required or declare them as nullable and check if they are null or not. Here is a sample for both cases:
Adding required
class NewWidget extends StatelessWidget {
NewWidget({required this.text, required this.onPress});
final String text;
final Function onPress;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Card(
color: Colors.blueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: TextButton(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 100.0),
child: Text(
text,
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
onPressed: onPress,
),
),
);
}
}
Using nullable parameters
class NewWidget extends StatelessWidget {
NewWidget({this.text, this.onPress});
final String? text;
final Function? onPress;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Card(
color: Colors.blueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: TextButton(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 100.0),
child: Text(
text ?? "text String is null",
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
onPressed: () => onPress!(),
),
),
);
}
}
I want to create a list of custom checkboxes(custom shape and color + icon) in flutter which can change their state on tap, till now I have created the list of checkboxes, but when I tap on one of them, all of them change their state.
I want that only the checkbox I'm tapping should change it's state.
Widget checkbox(){
return InkWell( ///CHECKBOX
onTap: () {
setState(() {
this.value = !this.value;
});
},
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white),
child:
value ? Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.green),
child: Icon(
Icons.check,
size: 20.0,
color: Colors.white,
)
)
:
Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black, ),
padding: EdgeInsets.all(0.0),
child: Icon(
Icons.circle,
size: 30.0,
color: Colors.white,
),
),
),
);
Edit(s):
I've used Inkwell class for this purpose
'value' is initialized to false
Thanks in advance
put you checkbox in the separate stateful widget so that it can control it's own state
class MyCheckbox extends StatefulWidget {
MyCheckbox({Key key}) : super(key: key);
#override
_MyCheckboxState createState() => _MyCheckboxState();
}
class _MyCheckboxState extends State<MyCheckbox> {
bool value = false;
#override
Widget build(BuildContext context) {
return InkWell(
///CHECKBOX
onTap: () {
setState(() {
this.value = !this.value;
});
},
child: Container(
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.white),
child: value
? Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.green),
child: Icon(
Icons.check,
size: 20.0,
color: Colors.white,
))
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
),
padding: EdgeInsets.all(0.0),
child: Icon(
Icons.circle,
size: 30.0,
color: Colors.white,
),
),
));
}
}
and when you call your widget add key: UniqueKey(), to make sure everything works as expected
Clipping allows me to draw only the part which I want to show. How do I remove the extra part which is not drawn but still takes up space?
The code
import 'package:flutter/material.dart';
class ClipTut extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
children: <Widget>[
ClipRect(
clipper: CustomRect(),
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 4, color: Colors.black),
color: Colors.blue,
),
width: 200.0,
height: 200.0,
),
),
Container(
height: 200,
width: 100,
decoration: BoxDecoration(
border: Border.all(width: 4, color: Colors.black),
color: Colors.green,
),
)
],
),
),
);
}
}
class CustomRect extends CustomClipper<Rect> {
#override
Rect getClip(Size size) => Rect.fromLTRB(0, 0.0, size.width/2, size.height);
#override
bool shouldReclip(CustomRect oldClipper) => true;
}
My Solution
I had a similar problem recently (I needed to clip the image in half) and found this sample code on the flutter api site (use the other alignment points to change slicing behaviors) - no extra space left over:
ClipRect(
child: Align(
alignment: Alignment.topCenter,
heightFactor: 0.5,
child: Image.network(userAvatarUrl),
),
)
My base widget is a Column. The first element is a Container which has a BoxShadow. The second element is a ListView which builds several Card, depending on the context. If the scroll is 0 the shadow gets displayed. However when start scrolling, the card is "over" the shadow (higher z-index) and hides it.
unscrolled
scrolled
The shadow should stay always on top, over the Cards. How is this done?
EDIT
if you don't want container shadow to disappear on scroll remove the ScrollNotification and NotificationListener
There is a widget for that 😉
You can use ScrollNotification with NotificationListener. Try this;
Happy coding! 🤓
class TestPage extends StatefulWidget {
const TestPage({Key key}) : super(key: key);
#override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
double blurRadius = 10.0;
double spreadRadius = 1.0;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blueGrey,
title: Text('Title'),
),
body: Container(
width: Get.width,
height: Get.height,
child: Stack(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0).copyWith(
top: 62.0,
),
child: NotificationListener<ScrollNotification>(
// ignore: missing_return
onNotification: (scrollNotification) {
if (scrollNotification is ScrollStartNotification) {
setState(() {
blurRadius = 0.0;
spreadRadius = 0.0;
});
} else if (scrollNotification is ScrollEndNotification) {
setState(() {
blurRadius = 10.0;
spreadRadius = 1.0;
});
}
},
child: ListView.builder(
// controller: _controller,
itemCount: 10,
itemBuilder: (context, index) {
return Card(
child: Container(
width: Get.width * .8,
height: 100.0,
child: Center(
child: Text('child # index : $index'),
)),
);
},
),
),
),
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
width: Get.width,
height: 60.0,
margin: EdgeInsets.only(),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20.0),
bottomRight: Radius.circular(20.0),
),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: blurRadius,
spreadRadius: spreadRadius,
),
],
),
child: Center(
child: Text('TOP CONTAINER'),
),
),
),
],
),
),
);
}
}
According to your question, here is the concept of what is going wrong here.
The 1st child of Column is a Container with shadow. Shadow renders outside of the defined size. If you don't provide any spaces after this Container we won't be able to see the shadow. This space can be done by Container margin , SizedBox or wrapping our list with Padding. But now our main question is how we get shadow while index=0. I believe it is coming from ListChildren`. They contain upper spaces, that's why we can see only the 1st time.
On Ui render priority starts from bottom to Top.
How to solve this problem:
We can provide space at the bottom of our container or assign margin(not padding), or use a SizedBox after the container providing the same amount of height as shadow.
providing bottom margin on the container.
adding a SizedBox with height of shadow.
wrapping our list with Padding and providing top:.
In this image,
our shadow: white, background:amber.
Demo code:
import 'package:flutter/material.dart';
class ColumnWithContainerShadow extends StatelessWidget {
const ColumnWithContainerShadow({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: Column(
children: [
Container(
height: 50,
width: double.infinity,
////* we dont need margin if we have padding on ListView
// margin: EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.green,
boxShadow: [
BoxShadow(
offset: Offset(0, 12),
color: Colors.white,
)
],
),
child: Center(child: Text("Container A")),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 12),
child: ListView(
children: [
...List.generate(
333,
(index) => Container(
/// enable this margine and remove other spaces to see 1st child shadow.(shadow depend on children position)
// margin: EdgeInsets.only(top: 12),
height: 60,
color: Colors.deepPurple,
child: Text("$index"),
),
)
],
),
),
),
Container(
height: 50,
width: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.green,
boxShadow: [
BoxShadow(
offset: Offset(0, 12),
color: Colors.white,
)
],
),
child: Text("Bottom Container"),
),
// Comment to close shadow
SizedBox(
height: 20,
)
],
),
);
}
}
Wrap this upper box in Material widget and provide an elevation.