Flutter add shadow in Custom painter - flutter

I am following some tutorial and fine a very good navbar but issue is when i replace it in my app i need to add shadow.
Here is the code
class BottomNavBarV2 extends StatefulWidget {
#override
_BottomNavBarV2State createState() => _BottomNavBarV2State();
}
class _BottomNavBarV2State extends State<BottomNavBarV2> {
int currentIndex = 0;
setBottomBarIndex(index) {
setState(() {
currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white.withAlpha(55),
body: Stack(
children: [
Positioned(
bottom: 0,
left: 0,
child: Container(
width: size.width,
height: 80,
child: Stack(
overflow: Overflow.visible,
children: [
CustomPaint(
size: Size(size.width, 80),
painter: BNBCustomPainter(),
),
Center(
heightFactor: 0.6,
child: FloatingActionButton(backgroundColor: Colors.orange, child: Icon(Icons.shopping_basket), elevation: 0.1, onPressed: () {}),
),
Container(
width: size.width,
height: 80,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(
Icons.home,
color: currentIndex == 0 ? Colors.orange : Colors.grey.shade400,
),
onPressed: () {
setBottomBarIndex(0);
},
splashColor: Colors.white,
),
IconButton(
icon: Icon(
Icons.restaurant_menu,
color: currentIndex == 1 ? Colors.orange : Colors.grey.shade400,
),
onPressed: () {
setBottomBarIndex(1);
}),
Container(
width: size.width * 0.20,
),
IconButton(
icon: Icon(
Icons.bookmark,
color: currentIndex == 2 ? Colors.orange : Colors.grey.shade400,
),
onPressed: () {
setBottomBarIndex(2);
}),
IconButton(
icon: Icon(
Icons.notifications,
color: currentIndex == 3 ? Colors.orange : Colors.grey.shade400,
),
onPressed: () {
setBottomBarIndex(3);
}),
],
),
)
],
),
),
)
],
),
);
}
}
class BNBCustomPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = new Paint()
..color = Colors.white
..style = PaintingStyle.fill;
Path path = Path();
path.moveTo(0, 20); // Start
path.quadraticBezierTo(size.width * 0.20, 0, size.width * 0.35, 0);
path.quadraticBezierTo(size.width * 0.40, 0, size.width * 0.40, 20);
path.arcToPoint(Offset(size.width * 0.60, 20), radius: Radius.circular(20.0), clockwise: false);
path.quadraticBezierTo(size.width * 0.60, 0, size.width * 0.65, 0);
path.quadraticBezierTo(size.width * 0.80, 0, size.width, 20);
path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
path.lineTo(0, 20);
canvas.drawShadow(path, Colors.black, 5, true);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Screen shot
[
I need to add shadow in this curved navbar. Because body and navbar both is white and navbar curved is not much visible. Need to add little shadow so can see curves easily

You can use the below code to gain the custom shadow for your widget. Just wrap your widget with the below container.
return Container(
margin: EdgeInsets.only(left: 20, top: 50, right: 20, bottom: 50),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.4),
spreadRadius: 8,
blurRadius: 10,
offset: Offset(0, 5), // changes position of shadow
),
],
),
)

Related

Is there a way I can create a bottom navigation menu like what is shown n the attachment using flutter

I've spent a lot of time googling on how I can achieve this but failed, maybe I'm googling it wrong, I tried using path, but failed.
I just want all items to change colors only when pressed, and the center docked will retain its shape and everything.
Please anyone who can shade some lights on this will be appreciated.
Thank you
Here what I've tried to put together with google help.
I just want to get exactly like what is shown on the attachment but my knowledge is limited at the moment.
return Scaffold(
backgroundColor: Color(0xFFFFFFFF),
bottomNavigationBar: Material(
color: Colors.transparent,
elevation: 100,
child: ClipPath(
child: SizedBox(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(
Icons.home,
color: Colors.deepPurple,
size: 30,
),
onPressed: () {
setState() {}
}),
IconButton(
icon: Icon(
Icons.branding_watermark,
size: 30,
color: Colors.black54,
),
onPressed: () {}),
SizedBox(
width: 50,
),
IconButton(
icon: Icon(
Icons.cloud_download,
size: 30,
color: Colors.black54,
),
onPressed: () {}),
IconButton(
icon: Icon(
Icons.face,
size: 30,
color: Colors.black54,
),
onPressed: () {}),
],
)),
height: 100,
width: double.infinity,
),
clipper: CurveDraw(),
),
),
floatingActionButton: MaterialButton(
color: Colors.deepPurple,
padding: EdgeInsets.all(20),
onPressed: () {},
shape: CircleBorder(),
child: Icon(
Icons.add,
size: 35,
color: Colors.white,
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
Curve
getClip(Size size) {
double sw = size.width;
double sh = size.height;
double gapConst = 50;
Path path = Path();
path.moveTo(0, sh);
path.lineTo(0, sh / 2);
path.quadraticBezierTo(0, 0, sh / 2, 0); //1st curve
path.lineTo(sw / 2 - sw / 5, 0);
path.cubicTo(sw / 2 - sw / 8, 0, sw / 2 - sw / 8, sh / 2, sw / 2, sh / 2);
path.cubicTo(
sw / 2 + sw / 8, sh / 2, sw / 2 + sw / 8, 0, sw / 2 + sw / 5, 0);
path.lineTo(sw - sh / 2, 0);
path.quadraticBezierTo(sw, 0, size.width, sh / 2);
path.lineTo(sw, sh);
path.close();
// rotate the path around the x-axis (flip it upside down)
path.transform(Matrix4.rotationX(pi));
return path;
}
I have implemented this bottom navigation bar without any 3rd party dependency, you can check this out:
Also don't forget to add vector_math dependecy in your pubspec and this import line at the top of the widget:
import 'package:vector_math/vector_math.dart' show radians;
class DemoWidget extends StatefulWidget {
const DemoWidget({super.key});
#override
State<DemoWidget> createState() => _DemoWidgetState();
}
class _DemoWidgetState extends State<DemoWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 82, 49, 49),
bottomNavigationBar: Container(
height: 100,
width: double.infinity,
decoration: const BoxDecoration(
color: Colors.white,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: const Icon(
Icons.home,
color: Colors.deepPurple,
size: 30,
),
onPressed: () {
setState() {}
}),
IconButton(
icon: const Icon(
Icons.branding_watermark,
size: 30,
color: Colors.black54,
),
onPressed: () {}),
const SizedBox(
width: 50,
),
IconButton(
icon: const Icon(
Icons.cloud_download,
size: 30,
color: Colors.black54,
),
onPressed: () {}),
IconButton(
icon: const Icon(
Icons.face,
size: 30,
color: Colors.black54,
),
onPressed: () {}),
],
)),
floatingActionButton: CustomPaint(
painter: HalfPainter(Colors.white),
child: Padding(
padding: const EdgeInsets.only(left: 18, right: 18, top: 30),
child: MaterialButton(
color: Colors.deepPurple,
padding: const EdgeInsets.all(20),
onPressed: () {},
shape: const CircleBorder(),
child: const Icon(
Icons.add,
size: 35,
color: Colors.white,
),
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
class HalfPainter extends CustomPainter {
HalfPainter(Color paintColor) {
this.arcPaint = Paint()..color = paintColor;
}
late Paint arcPaint;
#override
void paint(Canvas canvas, Size size) {
final Rect beforeRect = Rect.fromLTWH(0, (size.height / 2) - 10, 10, 10);
final Rect largeRect = Rect.fromLTWH(10, 16, size.width - 20, 73);
final Rect afterRect =
Rect.fromLTWH(size.width - 10, (size.height / 2) - 10, 10, 10);
final path = Path();
path.arcTo(beforeRect, radians(0), radians(90), false);
path.lineTo(20, size.height / 2);
path.arcTo(largeRect, radians(0), -radians(180), false);
path.moveTo(size.width - 10, size.height / 2);
path.lineTo(size.width - 10, (size.height / 2) - 20);
path.arcTo(afterRect, radians(180), radians(-100), false);
path.close();
canvas.drawPath(path, arcPaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

How can I create a circle outline with a shadow in Flutter?

I need a circular OutlinedButton with an elevation but that property is not available for that widget. I'm trying to create a circle outline with a shadow and wrap it in a GestureDetector to achieve the same behavior but all options that I have tried have the shadow all the way to the center of the circle or don't have any shadow in the inner part of the circle.
This is the output that I'm looking for:
How can I achieve this?
Try This Code:
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(20),
child: IconButton(
onPressed: () {},
iconSize: 200,
icon: const Icon(
Icons.circle_outlined,
shadows: [Shadow(blurRadius: 70)],
),
color: Colors.blue,
),
),
);
}
you can use a standard OutlinedButton widget with a modified shape property:
class RingBorder extends CircleBorder {
const RingBorder({
this.thickness = 12,
this.color = Colors.blue,
this.shadowColor = Colors.black,
this.shadowRadius = 6,
this.paintShadowCounter = 1,
});
final double thickness;
final Color color;
final Color shadowColor;
final double shadowRadius;
final int paintShadowCounter;
#override
CircleBorder copyWith({BorderSide? side, double? eccentricity}) => this;
#override
void paint(ui.Canvas canvas, ui.Rect rect, {ui.TextDirection? textDirection}) {
final path = Path()
..fillType = PathFillType.evenOdd
..addOval(Rect.fromCircle(center: rect.center, radius: rect.shortestSide / 2 - shadowRadius))
..addOval(Rect.fromCircle(center: rect.center, radius: rect.shortestSide / 2 - shadowRadius - thickness));
final paint = Paint()..color = color;
canvas.drawPath(path, paint);
paint
..color = shadowColor
..maskFilter = MaskFilter.blur(BlurStyle.outer, shadowRadius);
for (int i = 0; i < paintShadowCounter; i++) {
canvas.drawPath(path, paint);
}
}
}
now you can use it as follows:
OutlinedButton(
style: ButtonStyle(
shape: MaterialStatePropertyAll(RingBorder(
color: Colors.orange,
paintShadowCounter: 4,
shadowRadius: 12,
)),
),
onPressed: () => print('pressed'),
child: Container(),
);
notice that all params are optional and contain some default values
you can take IconButton and take circle_outlined as icon and give shadow as per your need:
IconButton(
onPressed: () {},
iconSize: 50,
icon: const Icon(
Icons.circle_outlined,
shadows: [Shadow(blurRadius: 20)],
),
color: Colors.blue,
),
Try below code hope its help to you. I have try same like as your shared image
Container(
alignment: Alignment.topCenter,
padding: const EdgeInsets.all(20),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.9),
spreadRadius: 6, //change on your need
blurRadius: 10,
offset: const Offset(0, 3),
)
],
),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(color: Colors.blue, width: 5),
boxShadow: const [
BoxShadow(
color: Colors.black26,
),
BoxShadow(
color: Colors.white,
spreadRadius: -10.0, //change on your need
blurRadius: 12.0,
),
],
),
),
),
),
Result->
wrap the OutlinedButton in a Container and apply a BoxShadow to the Container.
GestureDetector(
onTap: () {},
child: Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: Offset(0, 10),
),
],
),
child: OutlinedButton(
onPressed: () {},
child: Text("Button"),
),
),
)
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: InkWell(
onTap: () {},
child: Container(
height: 150,
width: 150,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: const BorderRadius.all(
Radius.circular(100),
),
border: Border.all(
color: Color(0xff00AEEF),
width: 3.w),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.9),
spreadRadius: 4,
blurRadius: 10,
offset: Offset(0, 3),
)
]),
child: OutlinedButton(
onPressed: () {},
child: Text("You can add Text and icon"),
),
),
),
);
}

flutter Create arrow on the side of TabBar depending on the current index

I'm new to flutter and I'm currently learning about making tabBar. I wanted to achieve something like the image below, where on my current active tab there will be an arrow indented towards the other inactive tab. How can I achieve this ? Should I use stack ? And I also need this to be responsive on different screen.
Here's my current code:
Stack(
alignment: AlignmentDirectional.topCenter,
children: [
TabBar(
controller: collabTabController,
labelColor: Colors.white,
labelStyle: weight500Style,
unselectedLabelColor: defaultProfileColor,
unselectedLabelStyle: weight500Style,
indicator: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius:
selectedTabIndex == 0
? const BorderRadius.only(
bottomLeft: Radius.circular(15),
topLeft: Radius.circular(15),
)
: const BorderRadius.only(
bottomRight: Radius.circular(15),
topRight: Radius.circular(15),
)
),
color: selectedTabIndex == 0 ? personalCardTag : Colors.orange,
),
tabs: [
Tab(text:'text 1'.tr()),
Tab(text:'text 2'.tr()),
],
),
RotatedBox(
quarterTurns: 1,
child: ClipPath(
clipper: Triangle(),
child: Container(
color: formBackgroundColor,
width: 40,
height: 20,
),
),
),
],
),
Try this :
class TabBarPage extends StatefulWidget {
const TabBarPage({super.key});
#override
State<TabBarPage> createState() => _TabBarPageState();
}
class _TabBarPageState extends State<TabBarPage>
with SingleTickerProviderStateMixin {
int selectedTabIndex = 0;
TabController? collabTabController;
#override
void initState() {
super.initState();
collabTabController = TabController(length: 2, vsync: this);
}
#override
Widget build(BuildContext context) {
double ScreenWidth = MediaQuery.of(context).size.width;
return Scaffold(
body: SafeArea(
child: Stack(
alignment: AlignmentDirectional.topCenter,
children: [
TabBar(
controller: collabTabController,
labelColor: Colors.white,
unselectedLabelColor: Colors.blue,
onTap: (value) {
selectedTabIndex = value;
setState(() {});
},
indicator: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: selectedTabIndex == 0
? const BorderRadius.only(
bottomLeft: Radius.circular(15),
topLeft: Radius.circular(15),
)
: const BorderRadius.only(
bottomRight: Radius.circular(15),
topRight: Radius.circular(15),
)),
color: selectedTabIndex == 0 ? Colors.amber : Colors.orange,
),
tabs: [
Tab(text: 'text 1'),
Tab(text: 'text 2'),
],
),
selectedTabIndex == 0
? Positioned(
left: ScreenWidth * 0.5,
child: RotatedBox(
quarterTurns: selectedTabIndex == 0 ? 0 : 2,
child: ClipPath(
clipper: Triangle(),
child: Container(
height: 48,
width: 40,
color: selectedTabIndex == 0
? Colors.amber
: Colors.orange,
),
),
),
)
: Positioned(
right: ScreenWidth * 0.5,
child: RotatedBox(
quarterTurns: 2,
child: ClipPath(
clipper: Triangle(),
child: Container(
height: 48,
width: 40,
color: Colors.orange,
),
),
),
),
],
),
),
);
}
}
class Triangle extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path()
..lineTo(size.width * 0.8, size.height * 0.4)
..cubicTo(size.width * 0.8, size.height * 0.4, size.width,
size.height * 0.5, size.width * 0.8, size.height * 0.6)
..lineTo(0, size.height)
..close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

How to cliping container from center top

Hello everyone I want to clip container from center top. I tried with custom clipper but not get success. Anyone please help me how to draw container like below image.
My Code
ClipPath(
clipper: TriangleClipper(),
child: Container(
height: 120,
color: Colors.red,
),
),
class TriangleClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var firstControlPoint = Offset(size.width / 2.5, 50.0);
var firstEndPoint = Offset(size.width / 2, 10.0);
var secondControlPoint = Offset(size.width - (size.width / 2.5), 50);
var secondEndPoint = Offset(size.width, 50);
var path = Path()
..lineTo(0, 50)
..lineTo(0, 70)
..cubicTo(0, 70, 0, 50, 20, 50)
..quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
firstEndPoint.dx, firstEndPoint.dy)
..quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
secondEndPoint.dx, secondEndPoint.dy)
..lineTo(size.width - 20, 50)
..cubicTo(size.width - 20, 50, size.width, 50, size.width, 70)
..lineTo(size.width, size.height)
..lineTo(0.0, size.height)
..close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Output:
i think you were missing path.moveTo(0.0, size.height);
import 'package:flutter/material.dart';
class CardCliping extends StatelessWidget {
const CardCliping({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple.shade300,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipPath(
clipper: TriangleClipper(),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.white)),
width: 130,
height: 40,
),
),
Container(
width: 400,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
border: Border.all(color: Colors.white, width: 0)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GestureDetector(
onTap: () {},
child: Icon(
Icons.home_outlined,
color: Colors.deepPurpleAccent,
),
),
GestureDetector(
onTap: () {},
child: Icon(
Icons.person_outline_outlined,
color: Colors.deepPurpleAccent,
),
)
],
),
),
],
),
),
);
}
}
class TriangleClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
final path = Path();
path.moveTo(0.0, size.height);
path.lineTo(size.width * .5, 0);
//we dont need this
// path.lineTo(size.width, size.height);
path.lineTo(size.width, size.height);
path.close();
return path;
}
#override
bool shouldReclip(TriangleClipper oldClipper) => false;
}

Flutter Curve Bar

I wonder if there is a better solution for making a curved bar like the following image.
Here is my flutter code:
import 'package:flutter_web/material.dart';
class CurvedBar extends StatelessWidget {
const CurvedBar({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(height: 50,
color: Colors.orange,
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20.0),
bottomRight: Radius.circular(20.0)),
child: Container(
height: 20.0,
width: double.infinity,
color: Colors.white,
),
),
Container(
color: Colors.white,
child: Row(
children: <Widget>[
Flexible(
flex: 1,
child: ClipRRect(
borderRadius:
BorderRadius.only(topRight: Radius.circular(20.0)),
child: Container(
height: 20.0,
color: Colors.orange,
),
)),
Flexible(
flex: 1,
child: ClipRRect(
borderRadius:
BorderRadius.only(topLeft: Radius.circular(20.0)),
child: Container(
height: 20.0,
color: Colors.orange,
),
))
],
))
],
));
}
}
make a custom ShapeBorder class like this one (the key method is _getPath that returns your shape's Path):
class CustomShapeBorder extends ShapeBorder {
const CustomShapeBorder();
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => _getPath(rect);
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) => _getPath(rect);
_getPath(Rect rect) {
final r = rect.height / 2;
final radius = Radius.circular(r);
final offset = Rect.fromCircle(center: Offset.zero, radius: r);
return Path()
..moveTo(rect.topLeft.dx, rect.topLeft.dy)
..relativeArcToPoint(offset.bottomRight, clockwise: false, radius: radius)
..lineTo(rect.center.dx - r, rect.center.dy)
..relativeArcToPoint(offset.bottomRight, clockwise: true, radius: radius)
..relativeArcToPoint(offset.topRight, clockwise: true, radius: radius)
..lineTo(rect.centerRight.dx - r, rect.centerRight.dy)
..relativeArcToPoint(offset.topRight, clockwise: false, radius: radius)
..addRect(rect);
}
#override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(0);
}
#override
ShapeBorder scale(double t) {
return CustomShapeBorder();
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
}
}
now you can use it like:
Container(
margin: EdgeInsets.only(top: 80),
height: 50,
width: double.infinity,
decoration: ShapeDecoration(
shape: CustomShapeBorder(),
//color: Colors.orange,
gradient:
LinearGradient(colors: [Colors.blue, Colors.orange]),
shadows: [
BoxShadow(
color: Colors.black, offset: Offset(3, -3), blurRadius: 3),
],
),
),
Result: