How to use clip path with sliver app bar in flutter - flutter

Can anyone explain me how to change clip path of sliver app bar in flutter

class MyClipper extends CustomClipper<Path> {
#override Path getClip(Size size) {
final radius = 32.0;
final r = Offset.zero & size;
final r1 = Rect.fromCircle(center: r.bottomRight - Offset(radius, radius * 2), radius: radius);
final r2 = Rect.fromCircle(center: r.bottomLeft + Offset(radius, 0), radius: radius);
return Path()
..moveTo(r.topLeft.dx, r.topLeft.dy)
..lineTo(r.topRight.dx, r.topRight.dy)
..arcTo(r1, 0, pi / 2, false)
..arcTo(r2, -pi / 2, -pi / 2, false);
}
#override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
Answer owner: pskink

You can use that, my design supporting auto resize clip path and working perfectly
import 'package:flutter/material.dart';
import 'package:unfpa/base/base_stateful_state.dart';
import 'package:unfpa/utils/constants.dart';
import 'package:unfpa/utils/custom_clip_path.dart';
import 'package:unfpa/utils/custom_clip_path_two.dart';
class TestWidget extends StatefulWidget {
TestWidget({Key key}) : super(key: key);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends BaseStatefulState<TestWidget> {
final ScrollController _sliverScrollController = ScrollController();
ValueNotifier<bool> isPinned = ValueNotifier(false);
Size size;
#override
void initState() {
super.initState();
_sliverScrollController.addListener(() {
if (!isPinned.value &&
_sliverScrollController.hasClients &&
_sliverScrollController.offset > kToolbarHeight) {
isPinned.value = true;
} else if (isPinned.value &&
_sliverScrollController.hasClients &&
_sliverScrollController.offset < kToolbarHeight) {
isPinned.value = false;
}
});
}
#override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
body: CustomScrollView(
controller: _sliverScrollController,
slivers: <Widget>[
ValueListenableBuilder(
valueListenable: isPinned,
builder: (context, value, child) {
return SliverAppBar(
elevation: 0,
backgroundColor: Colors.red,
leading: IconButton(
iconSize: 48,
color: Colors.white,
icon: Icon(Icons.close),
onPressed: () => Navigator.of(context).pop()),
primary: true,
pinned: true,
stretch: true,
title: Visibility(
visible: value,
child: Text(
"Sliver AppBar Title",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 22.0,
color: Colors.white,
),
),
),
titleSpacing: 0,
);
}),
SliverList(
delegate: SliverChildListDelegate([
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
transform: Matrix4.translationValues(0.0, -2.0, 0.0),
alignment: Alignment.center,
child: ClipPath(
clipper: ClipPathClass(),
child: Container(
padding: EdgeInsets.only(bottom:20),
width: size.width,
color: Colors.yellow,
child: Container(
margin: EdgeInsets.symmetric(
horizontal: 25),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: kToolbarTopMargin),
Text(
"Expanded Sliver title",
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
SizedBox(
height: 5,
),
Text(
"Expanded sliver description",
style: TextStyle(
fontSize: 17,
color: Colors.white,
height: 1.3,
fontWeight: FontWeight.w700,
),
)
],
),
),
),
),
),
],
)
])),
],
));
}
}
CustomClipPathClass
import 'package:flutter/material.dart';
class ClipPathClass extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0.0, size.height - 30);
var firstControlPoint = Offset(size.width / 4, size.height);
var firstPoint = Offset(size.width / 2, size.height);
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
firstPoint.dx, firstPoint.dy);
var secondControlPoint = Offset(size.width - (size.width / 4), size.height);
var secondPoint = Offset(size.width, size.height - 30);
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
secondPoint.dx, secondPoint.dy);
path.lineTo(size.width, 0.0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

Related

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;
}

how to overlap items on Appbar flutter

I want to achieve have a round icon for the profile image like in this example:
But is not working for me, this is what I achieved so far:
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
ClipPath(
clipper: _AppBarProfileClipper(childHeight),
child: Container(
padding: const EdgeInsets.only(top: 48.0),
color: TheBaseColors.lightBlue,
height: height,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
leading,
FlatButton(
onPressed: null,
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18.0,
color: Colors.white,
),
),
),
trailing,
],
),
),
),
Positioned(
bottom: childHeight / 2, //50
left: 0,
right: 0,
child: Align(
alignment: Alignment.bottomCenter,
child: child,
),
),
],
);
}
}
All the parameters are represented in the constructor and used in the code. But I cant figure out how to make that nice effect with the rounded icon.
Someone knows why is not working?
You can achieve given example like this:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final double appBarHeight = 100.0;
final double spaceAroundRoundButton = 4.0;
final double roundedButtonSize = 64.0;
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return SafeArea(
child: Stack(
children: <Widget>[
Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize:
Size(MediaQuery.of(context).size.width, appBarHeight),
child: ClipPath(
clipper: BottomClipper(),
child: Container(
alignment: Alignment.topLeft,
height: appBarHeight,
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
Icons.menu,
color: Colors.white,
),
onPressed: () {}),
Text(
'User Profile',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
IconButton(
icon: Icon(
Icons.settings,
color: Colors.white,
),
onPressed: () {})
],
),
),
)),
body: Container(
alignment: Alignment.topCenter,
margin: EdgeInsets.only(
top:
roundedButtonSize * 0.75 + spaceAroundRoundButton + 16.0),
child: Text('Body'),
),
),
Positioned(
child: Container(
padding: EdgeInsets.all(spaceAroundRoundButton),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: RawMaterialButton(
elevation: 0,
constraints: BoxConstraints.tightFor(
width: roundedButtonSize, height: roundedButtonSize),
shape: StadiumBorder(
side: BorderSide(width: 4, color: Colors.grey)),
child: Icon(Icons.person),
onPressed: () {},
fillColor: Colors.white,
),
),
left: (size.width / 2) -
(roundedButtonSize / 2) -
spaceAroundRoundButton,
top: appBarHeight -
(roundedButtonSize * 0.25) -
spaceAroundRoundButton,
)
],
),
);
}
}
class BottomClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0.0, size.height * 0.75);
var firstControlPoint = Offset(size.width * 0.25, size.height);
var firstEndPoint = Offset(size.width * 0.5, size.height);
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
firstEndPoint.dx, firstEndPoint.dy);
var secondControlPoint = Offset(size.width * 0.75, size.height);
var secondEndPoint = Offset(size.width, size.height * 0.75);
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
secondEndPoint.dx, secondEndPoint.dy);
path.lineTo(size.width, 0.0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Screenshot:

How to create one side skew container in flutter

I'm new to Flutter. How do I create the shape marked here? I tried. Below is the code.
Positioned(
top: 30.0,
left: 15.0,
width: 50.0,
height: 20.0,
child: Container(
decoration: BoxDecoration(
color: Colors.pink,
shape: BoxShape.rectangle
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0,horizontal: 8.0),
child: Text('අලුත්ම පුවත',style: TextStyle(color: Colors.white, fontSize: 10.0,fontWeight: FontWeight.bold)),
),
),
complete code
You can use clipPath to achieve desire output.
Following code help you more to understand.
class DeleteWidget extends StatefulWidget {
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ClipPath(
clipper: SkewCut(),
child: Container(
color: Colors.red,
width: 200,
height: 50,
child: Center(child: Text("Hello World")),
),
),
),
);
}
}
class SkewCut extends CustomClipper<Path> {
#override
Path getClip(Size size) {
final path = Path();
path.lineTo(size.width, 0);
path.lineTo(size.width - 20, size.height);
path.lineTo(0, size.height);
path.close();
return path;
}
#override
bool shouldReclip(SkewCut oldClipper) => false;
}

How to create one line and insert text center

I want to ask about design in flutter. I don't know how to do it.
I insert the image for your see my draw. Hope your guys can help me.
For Creating Horizontal line(Divider) around Text Widget
Row(children: <Widget>[
Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 10.0, right: 20.0),
child: Divider(
color: Colors.black,
height: 36,
)),
),
Text("OR"),
Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 20.0, right: 10.0),
child: Divider(
color: Colors.black,
height: 36,
)),
),
]),
Try this class,
class DividerWithText extends StatelessWidget {
final String text;
final TextStyle textStyle;
final Color color;
const DividerWithText({Key key, this.text, this.color, this.textStyle})
: assert(text != null),
super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(child: Divider(color: color)),
const SizedBox(width: 5.0),
Text(
text,
style: textStyle?.copyWith(color: textStyle.color ?? color) ??
TextStyle(color: color),
),
const SizedBox(width: 5.0),
Expanded(child: Divider(color: color)),
],
);
}
}
Usage:
Text("Login"),
DividerWithText(
text: "Or",
textStyle: TextStyle(fontSize: 20.0),
color: Colors.red,
),
Text("Google"),
there is lots of approach to this
1) using something like this
Row(
children: <Widget>[
Flexible(
child: Divider(
color: Colors.black,
height: 4,
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Text(
"Or",
style: TextStyle(backgroundColor: Colors.white),
),
),
Flexible(
child: Divider(
color: Colors.black,
height: 4,
),
),
],
)
2) this code has better performance
class TextWithLine extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(MediaQuery.of(context).size.width, 100),
painter: TextWithLinePainter(
color: Colors.red,
text: "Or",
textStyle: TextStyle(
color: Colors.black,
fontSize: 15,
),
lineColor: Colors.black54,
lineSize: 1,
margin: 8,
),
);
}
}
class TextWithLinePainter extends CustomPainter {
String text;
TextStyle textStyle;
Color color;
Color lineColor;
double lineSize;
double margin;
TextWithLinePainter({this.text, this.textStyle, this.color, this.lineColor, this.lineSize, this.margin});
#override
void paint(Canvas canvas, Size size) {
final textSpan = TextSpan(
text: text,
style: textStyle,
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.textAlign = TextAlign.center;
textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
var textW = textPainter.width;
var textH = textPainter.height;
textPainter.paint(
canvas,
Offset(
size.width / 2 - textW / 2,
size.height / 2 - textH / 2,
),
);
Paint linePainter = Paint()
..color = lineColor
..isAntiAlias = true
..strokeWidth = lineSize;
var firstLineEndX = size.width / 2 - (textW / 2) - margin;
canvas.drawLine(
Offset(0, size.height / 2),
Offset(firstLineEndX, size.height / 2),
linePainter,
);
var secondLineStartX = (size.width / 2) + (textW / 2) + margin;
canvas.drawLine(
Offset(secondLineStartX, size.height / 2),
Offset(size.width, size.height / 2),
linePainter,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}

Flutter: How to make telegram type bubble in chat message

Current output:
Expected output:
Code:
Align(
alignment: Alignment.topRight,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
margin: EdgeInsets.only(right: 12, top: 8),
decoration: BoxDecoration(
color: Color(0xFF486993),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Text("This is my message"),
),
)
Here is what exactly needed to create neat speech bubble in flutter.
first create a custom painter class that extends Painter class
class CustomChatBubble extends CustomPainter {
CustomChatBubble({this.color, #required this.isOwn});
final Color color;
final bool isOwn;
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color ?? Colors.blue;
Path paintBubbleTail() {
Path path;
if (!isOwn) {
path = Path()
..moveTo(5, size.height - 5)
..quadraticBezierTo(-5, size.height, -16, size.height - 4)
..quadraticBezierTo(-5, size.height - 5, 0, size.height - 17);
}
if (isOwn) {
path = Path()
..moveTo(size.width - 6, size.height - 4)
..quadraticBezierTo(
size.width + 5, size.height, size.width + 16, size.height - 4)
..quadraticBezierTo(
size.width + 5, size.height - 5, size.width, size.height - 17);
}
return path;
}
final RRect bubbleBody = RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(16));
final Path bubbleTail = paintBubbleTail();
canvas.drawRRect(bubbleBody, paint);
canvas.drawPath(bubbleTail, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
then use it in your chat screen. please note that this screen for only demo purpose so you can modify it according to your needs.
class ChatSampleWidget extends StatefulWidget {
#override
_ChatSampleWidgetState createState() => _ChatSampleWidgetState();
}
class _ChatSampleWidgetState extends State<ChatSampleWidget> {
TextEditingController _editingController = TextEditingController();
FocusNode _focusNode = FocusNode();
final TextStyle textStyle = TextStyle(color: Colors.white);
#override
void initState() {
super.initState();
}
#override
dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chatter'),
),
body: SafeArea(
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CustomPaint(
painter: CustomChatBubble(isOwn: false),
child: Container(
padding: EdgeInsets.all(8),
child: Text(
'Message from someone else \n Says sometihngs',
style: textStyle,
))),
],
),
SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CustomPaint(
painter:
CustomChatBubble(color: Colors.grey, isOwn: false),
child: Container(
padding: EdgeInsets.all(10),
child: FlutterLogo())),
],
),
SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
CustomPaint(
painter:
CustomChatBubble(color: Colors.green, isOwn: true),
child: Container(
padding: EdgeInsets.all(8),
child: Text(
'Message from me',
style: textStyle,
))),
],
)
],
),
),
Positioned(
bottom: 0,
child: Container(
padding: EdgeInsets.all(8),
width: MediaQuery.of(context).size.width,
color: Colors.grey.withOpacity(0.1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Flexible(
child: TextField(
controller: _editingController,
focusNode: _focusNode,
decoration:
InputDecoration(hintText: 'Say something...'),
),
),
IconButton(
icon: Icon(
Icons.send,
size: 30,
),
onPressed: () {
print(_editingController.text);
})
],
),
))
],
),
),
);
}
}
this is how it looks;
Here you are:
import 'package:flutter/material.dart';
void main() => runApp(
MyApp()
);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Padding(
padding: EdgeInsets.all(7),
child: Align(
alignment: Alignment.centerRight,
child: Stack(
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
decoration: BoxDecoration(
color: Color(0xFF486993),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: 'This is telegram message ',
style: TextStyle(
color: Colors.white,
fontSize: 14.0
),
),
TextSpan(
text: '3:16 PM',
style: TextStyle(
color: Colors.grey,
fontSize: 12.0,
fontStyle: FontStyle.italic
),
),
],
),
),
Icon(Icons.check, color: Color(0xFF7ABAF4), size: 16,)
]
),
),
Positioned(
bottom: 0,
right: 0,
child: CustomPaint(
painter: ChatBubbleTriangle(),
)
)
]
)
),
),
);
}
}
class ChatBubbleTriangle extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = Color(0xFF486993);
var path = Path();
path.lineTo(-15, 0);
path.lineTo(0, -15);
path.lineTo(0, 0);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
You will have to change paint method, because now it's just a triangle.
Otherwise, it looks as you requested.
Trying to recreate Telegram layout myself I came up with this to make the shape;
enum _TailDirection { right, left }
class ChatBubblePainter extends CustomPainter {
ChatBubblePainter({this.color, this.isSentByMe});
final Color color;
final bool isSentByMe;
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
Path paintBubbleTail(_TailDirection direction) {
double startingPoint;
double point;
double endPoint;
double curvePoint;
if (direction == _TailDirection.right) {
startingPoint = size.width - 5;
point = size.width + 10;
endPoint = size.width + 3;
curvePoint = size.width;
}
if (direction == _TailDirection.left) {
startingPoint = 5;
point = -10;
endPoint = -3;
curvePoint = 0;
}
return Path()
..moveTo(startingPoint, size.height)
..lineTo(point, size.height)
..quadraticBezierTo(endPoint, size.height, curvePoint, size.height - 10);
}
final RRect bubbleBody = RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(5.0));
final Path bubbleTail = isSentByMe
? paintBubbleTail(_TailDirection.right)
: paintBubbleTail(_TailDirection.left);
canvas.drawRRect(bubbleBody, paint);
canvas.drawPath(bubbleTail, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}