I'm trying to achieve something like this in flutter:
This is my attempt to do that
class ContainerWidget extends StatelessWidget {
final Widget child;
final double width, height;
late List<Color>? gradiantColors;
ContainerWidget({
Key? key,
required this.child,
required this.height,
required this.width,
this.gradiantColors,
}) : super(key: key);
#override
Widget build(BuildContext context) {
var border = BorderRadius.only(
topRight: Radius.circular(70),
topLeft: Radius.circular(8),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
);
return Container(
decoration: BoxDecoration(
color: gradiantColors == null ? Colors.white : null,
borderRadius: border,
boxShadow: [
BoxShadow(
color: Colors.grey.shade300,
offset: Offset(0, 1),
blurRadius: 30,
spreadRadius: 5,
),
],
gradient: gradiantColors != null
? LinearGradient(
colors: gradiantColors!,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
),
width: width,
height: height,
child: Stack(
children: [
Positioned(
top: -40,
left: -20,
child: Container(
decoration: BoxDecoration(
color: Colors.red.shade100.withOpacity(0.2),
shape: BoxShape.circle,
),
width: 100,
height: 100,
),
),
Positioned(
child: Icon(
Icons.abc_rounded,
size: 40,
),
),
child
],
),
);
}
}
this is the output
However, I've two problems:
The circle on top leak out over the rounded corner, I tried to solve that with the ClipRRect, but it clipped also the shadow of the container.
Icon (or the image) cannot be placed above the top portion of the container.
Any ideas ?
Like this (put this inside your widget class):
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(blurRadius: 20,
color: Colors.orange.withOpacity(0.5),
offset: const Offset(0.0, 8.0)
)
],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(100),
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20)
)
),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(100),
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20)
),
child: Stack(
children: [
Container(
width: 200,
height: 300,
color: Colors.orange
),
Positioned(
top: -50,
left: -40,
child: ClipOval(
child: Container(
width: 150,
height: 150,
color: Colors.white.withOpacity(0.25)
)
)
)
]
)
)
),
Positioned(
top: -40,
left: 20,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(blurRadius: 10, color: Colors.black.withOpacity(0.2), offset: const Offset(0.0, 5.0))
]
)
)
)
]
)
)
);
}
which then ends up looking like this:
The top white Container is just a placeholder for what your image could look like. I'd suggest then using a PNG already with the shadow integrated if you'll be using pictures like that.
Let me know if that's what you're trying to accomplish and adjust to your needs.
Related
Long story short I have a stack, in which I have an image that is position out of the stack.
body: Column(
children: [
Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
_positionedImage(),
],
),
_bodyContainer(),
],
container:
Widget _bodyContainer() {
return Container(
width: MediaQuery.of(context).size.width,
height: 200,
decoration: BoxDecoration(
color: itsyBackground,
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
boxShadow: [BoxShadow(offset: Offset(0, 0), color: Colors.green, spreadRadius: 1, blurRadius: 1),],
),
);
}
How can I make so that picture is on top of the container?
the approach that you used is wrong here.. You don't need a column here. You can simply render a stack an the bottom most widget should be placed first in the stack children.
https://medium.com/flutter-community/a-deep-dive-into-stack-in-flutter-3264619b3a77
An Example:
Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
height: 200,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20)),
boxShadow: [
BoxShadow(
offset: Offset(0, 0),
color: Colors.green,
spreadRadius: 1,
blurRadius: 1),
],
),
),
Positioned(
top: -10,
child: Image.network(
'https://static.toiimg.com/thumb/msid-31346158,width-748,height-
499,resizemode=4,imgsize-114461/.jpg',
width: 100,
),
),
],
),
I would like to shows a container elements with expansion and collapse effect.
It is not combined with AppBar.
Here is my container code:
InkWell(
onTap: () {
if (_height == 200) {
setState(() {
_height = 100.0;
});
} else {
_updateState();
}
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 550),
height: _height,
width: Get.width,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 10,
offset: const Offset(0, 3),
),
],
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("0.0"),
Text("0.0"),
//Widget1(),
//Widget2(),
//Widget3(),
],
),
),
);
I want it to show my other Widgets when I click it.
Also I tried ExpansionPanelList and ExpansionTile but they are not working like what I exactly want. Also I don't want to use SliverAppBar .
This example might help you:
class _MyHomePageState extends State<MyHomePage> {
double _height = 100;
void onClick() {
if (_height == 300) {
setState(() {
_height = 100.0;
});
} else {
setState(() {
_height = 300;
});
}
}
#override
Widget build(BuildContext context) {
final List<Widget> _widgets = [
Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 10,
offset: const Offset(0, 3),
),
],
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
),
Container(
width: MediaQuery.of(context).size.width,
height: 100,
color: Colors.grey,
),
Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.red,
),
],
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
)
];
return Scaffold(
backgroundColor: Colors.blueAccent,
body: Column(
children: [
InkWell(
onTap: () {
onClick();
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 550),
height: _height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 10,
offset: const Offset(0, 3),
),
],
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column(
children: _widgets,
),
),
),
),
],
),
);
}
}
result:
you can add condition in you column like this
children: [
Text("0.0"),
Text("0.0"),
if(_height == 200)
Widget1(),
if(_height == 200)
Widget2(),
if(_height == 200)
Widget3(),
],
also you can use AnimatedSwitcher
https://api.flutter.dev/flutter/widgets/AnimatedSwitcher-class.html
I try to make my first app with Flutter.
I have a question i make this custom bottom bar:
Are there better method for make this effect or similar effect?
Is it correct this method with stack and row?
Thanks u and good coding!
Container bottom(Size size) {
double totalElement = size.width / 3;
return Container(
height: size.width * 0.15,
//color: GS.mainColor,
child: Stack(
alignment: Alignment.bottomLeft,
children: [
//LONG ELEMENT
Positioned(
child: Container(
width: totalElement*3,
height: GS.heightBottomElement,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
)
),
)
),
//FIRST ELEMENT
Positioned(
child: Container(
height: GS.heightBottomElement,
width: totalElement*2,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
)
),
)
),
//FIRST ELEMENT
Positioned(
child: Container(
height: GS.heightBottomElement,
width: totalElement,
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
)
),
)
),
],
),
);
}
You could extract the Positioned elements into a separate stateless widget, in that way you use the DRY method, and your code will look nicer and cleaner, with less repeated code.
It would look like this:
Container bottom(Size size) {
double totalElement = size.width / 3;
return Container(
height: size.width * 0.15,
//color: GS.mainColor,
child: Stack(
alignment: Alignment.bottomLeft,
children: [
//LONG ELEMENT
MyCutomPositioned(height: blabla, width: blabla, color: blabla),
//FIRST ELEMENT
MyCutomPositioned(height: blabla, width: blabla, color: blabla),
//SECOND ELEMENT
MyCutomPositioned(height: blabla, width: blabla, color: blabla),
],
),
);
}
I am trying to do this card. But I don't know how to the green shape part. Any suggestions?
Inside a row create a container and give it DecorationBox and some radius to upper and bottom left corners and a second child as text. You can modify the following example code according to your needs.
Container(
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
bottomLeft: Radius.circular(5))),
height: 20,
width: 20,
),
Text('data'),
],
)),
Exactly how I want it.
class NotificationBanner extends StatelessWidget {
final Radius radius = Radius.circular(10.0);
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Container(
height: 100,
width: 8,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
topLeft: radius,
bottomLeft: radius,
),
),
),
Expanded(
child: Container(
height: 100,
decoration: BoxDecoration(
color: kLightBlack,
borderRadius: BorderRadius.only(
topRight: radius,
bottomRight: radius,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Some pretty message',
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 20.0),
),
),
)),
),
],
),
);
}
}
I'm following the picture below but got some difficulties when I tried to make that white bubble.
I have tried a method using OverFlowBox from another post Flutter mask a circle into a container but I got the circle stuck in the middle of the Container and I don't know why alignment won't help moving it. Here is what I've tried:
return Container(
alignment: Alignment.topCenter,
height: screenHeight/3.5,
width: screenWidth/3.5,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(60),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
gradient: LinearGradient(
begin: FractionalOffset.topLeft,
end: FractionalOffset.bottomRight,
colors: [boxColorBegin, boxColorEnd]
),
),
child: ClipRect(
clipBehavior: Clip.hardEdge,
child: OverflowBox(
maxHeight: screenHeight/3.5 +20,
maxWidth: screenWidth/3.5 + 20,
child:Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
)
),
),
);
And the result was
Is there any ways to overflow something inside a widget so that it looks like clipped?
Thanks in advance!
I found the way to achieve what I want but am still confused why OverFlowBox can't be aligned. I thought it is because the size of the OverFlowBox is larger than its parent but it still don't work when I changed it to a smaller size.
I used Stack and Positioned widget and set the overflow parameter of the Stack as overflow.clip
Here is the code:
return Container(
height: screenHeight/3.5,
width: screenWidth/3.2,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(60),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
gradient: LinearGradient(
begin: FractionalOffset.topLeft,
end: FractionalOffset.bottomRight,
colors: [boxColorBegin, boxColorEnd]
),
),
child: Stack(
overflow: Overflow.clip,
alignment: Alignment.topCenter ,
children: <Widget>[
Positioned(
bottom: screenHeight / 8,
right: screenWidth / 12,
child: Container(
width: screenWidth / 3.5,
height: screenHeight / 3.5,
decoration: BoxDecoration(
color: Colors.white38,
shape: BoxShape.circle,
),
)
)
],
)
)
And the result is
EDIT
Turns out you can just use Container as a clipper with the clipBehavior parameter and use FractionalTranslation widget as child to manipulate the position of the white circle. Thanks to pskink for the simple answer.
Here is the new code
return Container(
alignment: Alignment.topLeft,
clipBehavior: Clip.antiAlias,
height: screenHeight/3.5,
width: screenWidth/3.2,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(60),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
gradient: LinearGradient(
begin: FractionalOffset.topLeft,
end: FractionalOffset.bottomRight,
colors: [boxColorBegin, boxColorEnd]
),
),
child: FractionalTranslation(
translation: Offset(-0.25, -0.5),
child: Container(
width: screenWidth / 3.5,
height: screenHeight / 3.5,
decoration: BoxDecoration(
color: Colors.white38,
shape: BoxShape.circle,
),
)
)
);
You can easily accomplish this using ClipRRect as your root container for this widget. Provide it a border radius and it will clip all children and prevent them from painting outside the bounds. You can then use Transform.translate to render a circle and offset it outside its parent.
I created a sample pen for you to try
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
topRight: Radius.circular(125),
),
child: Container(
height: 400,
width: 250,
decoration: BoxDecoration(
color: Colors.red,
),
child: Stack(
children: [
Align(
alignment: Alignment.topLeft,
child: Transform.translate(
offset: Offset(-40, -100),
child: Container(
height: 220,
width: 220,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(110)),
color: Colors.white.withOpacity(0.4),
),
),
),
),
],
),
),
);
For more information on ClipRRect and Transform.translate visit the API docs.
you can use padding to move the OverflowBox. you also need to clip the parent Container with ClipRRect
here is your fix (tested and works):
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(60),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
child: Container(
alignment: Alignment.topCenter,
height: screenHeight / 3.5,
width: screenWidth / 3.5,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(60),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
gradient: LinearGradient(
begin: FractionalOffset.topLeft, end: FractionalOffset.bottomRight, colors: [boxColorBegin, boxColorEnd]),
),
child: ClipRect(
clipBehavior: Clip.hardEdge,
child: Padding(
padding: const EdgeInsets.only(right: 130, bottom: 150),
child: OverflowBox(
maxHeight: screenHeight / 3.5 + 20,
maxWidth: screenWidth / 3.5 + 20,
child: Container(
decoration: BoxDecoration(
color: Colors.white.withAlpha(80),
shape: BoxShape.circle,
),
)),
),
),
),
);