I am working with the scratcher library and it provides a square shape card which works great. However, I want to create a custom shape of cards such as a circle or a heart. I tried with ClipPath but it still counts the outer area as scratchable.
I wonder, is there any way to do it? Thanks in advance.
`ClipPath(
clipper: CustomClipPath(),
clipBehavior: Clip.hardEdge,
child: Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.red,
child: Scratcher(
brushSize: 30,
threshold: 50,
color: Colors.red,
onChange: (value) => print("Scratch progress: $value%"),
onThreshold: () => print("Threshold reached, you won!"),
child: Container(
height: 300,
width: 300,
color: Colors.blue,
),
),
),
);
class CustomClipPath extends CustomClipper<Path> {
var radius = 5.0;
#override
Path getClip(Size size) {
Path path = Path();
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width, 0.0);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}`
Related
I try to create a border around a container, that is not that difficult ofcourse, but i need also a text IN the border with space around it. Like horizontal divder but i need a complete border.
Attached what i like to achieve.
Any one who can help me how to approach this?
Thanks!
Tried to use the horizontal and vertical divider packages, but then the border is not in full.
You can use CustomPainter like this:
class CustomDraw extends CustomPainter {
late Paint painter;
late double radius;
late double textWidth;
CustomDraw(Color color, this.textWidth, {this.radius = 0}) {
painter = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2
..color = color;
}
#override
void paint(Canvas canvas, Size size) {
var path = Path();
path.moveTo(size.width - ((size.width - textWidth) / 2), 0);
path.lineTo(size.width - radius, 0);
path.cubicTo(size.width - radius, 0, size.width, 0, size.width, radius);
path.lineTo(size.width, size.height - radius);
path.cubicTo(size.width, size.height - radius, size.width, size.height,
size.width - radius, size.height);
path.lineTo(radius, size.height);
path.cubicTo(radius, size.height, 0, size.height, 0, size.height - radius);
path.lineTo(0, radius);
path.cubicTo(0, radius, 0, 0, radius, 0);
path.lineTo(((size.width - textWidth) / 2), 0);
canvas.drawPath(path, painter);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
and this widget:
class CustomTitleWidget extends StatefulWidget {
final double height;
final double width;
final double? radius;
final String title;
const CustomTitleWidget(
{Key? key,
required this.height,
required this.width,
required this.title,
this.radius})
: super(key: key);
#override
State<CustomTitleWidget> createState() => _CustomTitleWidgetState();
}
class _CustomTitleWidgetState extends State<CustomTitleWidget> {
GlobalKey textKey = GlobalKey();
double textHeight = 0.0;
double textWidth = 0.0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
final textKeyContext = textKey.currentContext;
if (textKeyContext != null) {
final box = textKeyContext.findRenderObject() as RenderBox;
textHeight = box.size.height;
textWidth = box.size.width;
}
});
});
}
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.topCenter,
children: [
CustomPaint(
child: Container(
height: widget.height,
width: widget.width,
),
painter: CustomDraw(
Colors.red,
textWidth,
radius: widget.radius ?? 0,
),
),
Positioned(
top: -textHeight / 2,
child: Padding(
key: textKey,
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
widget.title,
),
),
)
],
);
}
}
use like this:
CustomTitleWidget(
height: 200,
width: double.infinity,
title: 'asdasdasdasdasd',
radius: 16),
#Maenda, We can implement such kind of structure using Stack, take one container with a border & put the other container over the first one.
Here is an example:
Stack(
children: [
Positioned(
top: 12,
left: 0,
right: 0,
child: Container(
height: 120,
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green,
border: Border.all(width: 1.2),
borderRadius: BorderRadius.circular(3)),
)),
Container(
margin: EdgeInsets.symmetric(
horizontal: 21,
),
child: Column(
children: [
Container(
width: 100,
margin: EdgeInsets.only(top: 12, bottom: 5),
alignment: Alignment.center,
color: Colors.blue,
child: Text(
"Any Text",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
Text(
"Nature, in the broadest sense, is the physical world or universe. Nature can refer to the phenomena of the physical world, and also to life in general.",
textAlign: TextAlign.center,
),
SizedBox(
height: 8,
)
],
),
),
],
);
Hope this works! You can customize as per the design.
Please help me on how can I create this box
message box to be created in flutter
you ca use these packages to have a bubble shape for a message
bubble: ^1.2.1
flutter_chat_bubble: ^2.0.0
You can use the bubble from material design by importing
import 'package:flutter/material.dart';
or simply :-
Bubble(
margin: BubbleEdges.only(leftBottom: 10),
nip: BubbleNip.rightTop,
color: Color.fromRGBO(225, 255, 199, 1.0),
child: Text('Your Text', textAlign: TextAlign.right)
If u need more reference read this.
you can use customPainter or CustomClipper , in this case, better use clipper because we need all size from its child,
code :
import 'package:flutter/material.dart';
class MessageBox extends StatefulWidget {
const MessageBox({Key? key}) : super(key: key);
#override
_MessageBoxState createState() => _MessageBoxState();
}
class _MessageBoxState extends State<MessageBox> {
#override
Widget build(BuildContext context) {
return Material(
child: Column(
children: [
const SizedBox(height: 200,),
Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(35.0)
),
),
ClipPath(
clipper: MessageClipper(),
child: Container(
height: 20,
width: MediaQuery.of(context).size.width,
color: Colors.purple,
),
)
],
),
);
}
}
class MessageClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var firstOffset = Offset(size.width * 0.1, 0.0);
var secondPoint = Offset(size.width * 0.15, size.height );
var lastPoint = Offset(size.width * 0.2, 0.0);
var path = Path()
..moveTo(firstOffset.dx, firstOffset.dy)
..lineTo(secondPoint.dx, secondPoint.dy)
..lineTo(lastPoint.dx, lastPoint.dy)
..close();
return path;
}
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
return true;
}
}
here result :
How can I add a dotted border for a container but for only one side but also with border radius?
There are packages like dotted_border and fdottedline , but they both do around the whole container.
I'm trying to achieve this
As mentioned in another thread there isn't a default way of implementing this yet, however the method detailed in this answer seems to get you part way there. There are also a few other approaches to this problem in there too which may be of help.
It is not possible with an actual border. especially not since the curved part of the border is still solid in your picture. However, I managed to get the desired result by overlaying a dotted line at the center between two containers with a solid border, using a Stack
class MyWidget extends StatelessWidget {
static const double _height = 500;
static const double _width = 500;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
width: _width + 30,
height: _height + 30,
child: Center(
child: Stack(
alignment: Alignment.center,
children: [
Row(
children: [
_container(),
_container(),
],
mainAxisAlignment: MainAxisAlignment.center,
),
CustomPaint(
foregroundPainter: DashedLinePainter(),
child: Container(
width: 5, color: Colors.white, height: _height - 30)),
],
),
),
);
}
Widget _container() {
return Container(
height: _height,
width: _width / 2,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
width: 2,
),
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
);
}
}
class DashedLinePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
double dashWidth = 9, dashSpace = 5, startX = size.width / 2, startY = 0;
final paint = Paint()
..color = Colors.blueAccent
..strokeWidth = 2;
while (startY < size.height) {
canvas.drawLine(
Offset(startX, startY), Offset(startX, startY + dashWidth), paint);
startY += dashWidth + dashSpace;
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
This code renders this:
You can use the plugin dotted_decoration to achieve the required design.
Code:-
Container(
decoration: DottedDecoration(
color: Colors.white,
strokeWidth: 0.5,
linePosition: LinePosition.right,
),
height:120,
width: 50,
),
I am using multiple curve in my container but that is show on bottom. how to set on top?
Please help me.
I am using multiple curve in my container but that is show on bottom. how to set on top?
Please help me.
I am using multiple curve in my container but that is show on bottom. how to set on top?
Please help me.
This is my code.
import 'package:evillage_app/style/style.dart';
import 'package:flutter/material.dart';
class MenuCard extends StatefulWidget {
const MenuCard({Key? key}) : super(key: key);
#override
_MenuCardState createState() => _MenuCardState();
}
class _MenuCardState extends State<MenuCard> {
List menuService = [
{'name': 'ASAA', 'icon': 'assets/icon/ASAA.png'},
{'name': 'INFO INDIA', 'icon': 'assets/icon/Info India.png'},
{'name': 'BUSINESS', 'icon': 'assets/icon/business.png'},
{'name': 'E-DIARY', 'icon': 'assets/icon/e Diary.png'},
{'name': 'NEWS', 'icon': 'assets/icon/News.png'},
{'name': 'SERVICES', 'icon': 'assets/icon/Service.png'},
];
#override
Widget build(BuildContext context) {
return Stack(
children: [
ClipPath(
clipper:SkewCut() ,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width / 1.58,
decoration: BoxDecoration(gradient: menuGradient),
child: GridView.count(
crossAxisCount: 2,
childAspectRatio: (2 / 0.75),
crossAxisSpacing: 2,
mainAxisSpacing: 5,
physics: ScrollPhysics(),
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 30,
vertical: 13),
children: List.generate(
menuService.length,
(index) {
return Container(
child: Card(
shadowColor: Colors.grey,
elevation: 4,
child: Container(
width: MediaQuery.of(context).size.width / 1,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(25)),
color: Colors.white,
),
child: ListTile(
leading: CircleAvatar(
// backgroundColor: red,
backgroundImage:
AssetImage(menuService[index]['icon']),
),
contentPadding: EdgeInsets.only(left: 15),
title: Text(
menuService[index]['name'].toString(),
style: labelStyle(),
overflow: TextOverflow.ellipsis,
),
)),
));
},
),
),
),
),
],
);
}
}
class SkewCut extends CustomClipper<Path> {
#override
Path getClip(Size size) {
Path path = Path();
path.lineTo(0, size.height);
path.quadraticBezierTo(
size.width / 4, size.height - 40, size.width / 2, size.height - 20);
path.quadraticBezierTo(
3 / 4 * size.width, size.height, size.width, size.height - 30);
path.lineTo(size.width, 0);
return path;
}
#override
bool shouldReclip(SkewCut oldClipper) => false;
}
This is ScreenShot
My suggestion would be using CustomPaint instead of CustomClipper<Path>.
CustomPaint
class SkewCut extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.purpleAccent;
paint.style = PaintingStyle.fill;
var pathUp = Path();
pathUp.moveTo(0, size.height * 0.1033);
pathUp.quadraticBezierTo(size.width * 0.25, size.height * 0.145,
size.width * 0.5, size.height * 0.1033);
pathUp.quadraticBezierTo(size.width * 0.75, size.height * 0.0750,
size.width * 1.0, size.height * 0.1033);
pathUp.lineTo(size.width, 0);
pathUp.lineTo(0, 0);
canvas.drawPath(pathUp, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I have just replaced your CustomClipper with CustomPainter.
Usage
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: SkewCut(),
child: <<YOUR CHILD WIDGET>>,
),
);
}
While there are many ways to do this but personally I use the following visual editor to have some generated code and then tweak it based on requirements.
https://fluttershapemaker.com/
This saves me a lot of time and effort.
I am trying to achieve arc shape in bottom sheet modal?
So far i have tried below code, but the shape is not achieved in modal.
class ArcPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Path path = Path();
path.lineTo(0, size.height);
path.quadraticBezierTo(
size.width / 2, size.height - 100, size.width, size.height);
path.lineTo(size.width, 0);
Paint paint = Paint()
..color = Colors.blue
..strokeWidth = 0.0;
canvas.drawPath(path, paint);
paint.color = Colors.blue[600];
}
#override
bool shouldRepaint(ArcPainter oldDelegate) {
return false;
}
}
void _settingModalBottomSheet(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return CustomPaint(
painter: ArcPainter(),
// height: MediaQuery.of(context).size.height / 2 - 20,
// padding: EdgeInsets.all(30),
child: Wrap(
children: <Widget>[
Center(child: Text('Add a new task')),
TextFormField(
decoration: const InputDecoration(
labelText: 'Task *',
),
onSaved: (String value) {
// This optional block of code can be used to run
// code when the user saves the form.
},
validator: (String value) {
return value.contains('#') ? 'Do not use the # char.' : null;
},
),
Divider(),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text('AB'),
),
label: Text('Aaron Burr'),
)
],
),
);
});
}
What would be the best way to change the shape of a modal?
Expecting results as show in the UI(to do list app)
use shape_of_view package to change the shape of the bottom-sheet and backgroundColor to make the bottom-sheet transparent.
showModalBottomSheet(
elevation: 0,
context: context,
backgroundColor: Colors.transparent,
clipBehavior: Clip.hardEdge,
builder: (BuildContext bc) {
return ShapeOfView(
shape: ArcShape(
direction: ArcDirection.Outside,
height: 20,
position: ArcPosition.Top),
child: Container(
color: Colors.white,
child: Padding(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Container(
color: Colors.pink,
height:50
),
)));
});