How to add drop shadow to TextFormField In Flutter - flutter

I have a text form field in flutter I want to add a drop shadow to it. how should I do that?
final password = TextFormField(
obscureText: true,
autofocus: false,
decoration: InputDecoration(
icon: new Icon(Icons.lock, color: Color(0xff224597)),
hintText: 'Password',
fillColor: Colors.white,
filled: true,
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
enabledBorder: OutlineInputBorder(borderRadius:BorderRadius.circular(5.0),
borderSide: BorderSide(color: Colors.white, width: 3.0))
),
);

You can Wrap your TextFormField with a Material widget and edit its properties such as elevation and shadowColor.
Example:
Material(
elevation: 20.0,
shadowColor: Colors.blue,
child: TextFormField(
obscureText: true,
autofocus: false,
decoration: InputDecoration(
icon: new Icon(Icons.lock, color: Color(0xff224597)),
hintText: 'Password',
fillColor: Colors.white,
filled: true,
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
enabledBorder: OutlineInputBorder(borderRadius:BorderRadius.circular(5.0),
borderSide: BorderSide(color: Colors.white, width: 3.0))
),
),
)
You will Get something similar to the image below.

You can Use PhysicalModel to add shadow on each widget like this:
PhysicalModel(
borderRadius: BorderRadius.circular(25),
color: Colors.white,
elevation: 5.0,
shadowColor: Color(0xff44BD32),
child: CustomTextField(...

You can use this class as a wrapper for an element's border. It takes the border of a control and draws a shadow to that border above the control. To create an illusion that the shadow is behind the control, the shadow's area above the control gets cut off.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class DecoratedInputBorder extends InputBorder {
DecoratedInputBorder({
required this.child,
required this.shadow,
}) : super(borderSide: child.borderSide);
final InputBorder child;
final BoxShadow shadow;
#override
bool get isOutline => child.isOutline;
#override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) => child.getInnerPath(rect, textDirection: textDirection);
#override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) => child.getOuterPath(rect, textDirection: textDirection);
#override
EdgeInsetsGeometry get dimensions => child.dimensions;
#override
InputBorder copyWith({BorderSide? borderSide, InputBorder? child, BoxShadow? shadow, bool? isOutline}) {
return DecoratedInputBorder(
child: (child ?? this.child).copyWith(borderSide: borderSide),
shadow: shadow ?? this.shadow,
);
}
#override
ShapeBorder scale(double t) {
final scalledChild = child.scale(t);
return DecoratedInputBorder(
child: scalledChild is InputBorder ? scalledChild : child,
shadow: BoxShadow.lerp(null, shadow, t)!,
);
}
#override
void paint(Canvas canvas, Rect rect, {double? gapStart, double gapExtent = 0.0, double gapPercentage = 0.0, TextDirection? textDirection}) {
final clipPath = Path()
..addRect(const Rect.fromLTWH(-5000, -5000, 10000, 10000))
..addPath(getInnerPath(rect), Offset.zero)
..fillType = PathFillType.evenOdd;
canvas.clipPath(clipPath);
final Paint paint = shadow.toPaint();
final Rect bounds = rect.shift(shadow.offset).inflate(shadow.spreadRadius);
canvas.drawPath(getOuterPath(bounds), paint);
child.paint(canvas, rect, gapStart: gapStart, gapExtent: gapExtent, gapPercentage: gapPercentage, textDirection: textDirection);
}
#override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is DecoratedInputBorder && other.borderSide == borderSide && other.child == child && other.shadow == shadow;
}
#override
int get hashCode => hashValues(borderSide, child, shadow);
#override
String toString() {
return '${objectRuntimeType(this, 'DecoratedInputBorder')}($borderSide, $shadow, $child)';
}
}
MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
inputDecorationTheme: InputDecorationTheme(
border: DecoratedInputBorder(
child: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(16.0)),
),
shadow: const BoxShadow(
color: Colors.blue,
blurRadius: 15,
),
),
),
),
It should looks this way:
Interactive example: https://dartpad.dev/?id=35f1249b52d177d47bc91c87d0a8c08c
Alternatively, you can use my package control_style. It implements a deeper realisation of this approach.

Here is a possible solution where the BoxShadow is only displayed behind the TextField, but is not expanding vertically if an error text is displayed.
My solution was to use the Stack widget to create an additional Container behind the actual TextField that is responsible for displaying the shadow.
A TextPainter is used to determine the height of the error text depending on its style:
import 'package:flutter/material.dart';
class TextFieldWithBoxShadow extends StatelessWidget {
final String? errorText;
final String? labelText;
final TextEditingController? controller;
final double height;
const TextFieldWithBoxShadow({
Key? key,
this.errorText,
this.labelText,
this.controller,
this.height = 40,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final errorStyle = const TextStyle(
fontSize: 14,
);
// Wrap everything in LayoutBuilder so that the available maxWidth is taken into account for the height calculation (important if you error text exceeds one line)
return LayoutBuilder(builder: (context, constraints) {
// Use tp to calculate the height of the errorText
final textPainter = TextPainter()
..text = TextSpan(text: errorText, style: errorStyle)
..textDirection = TextDirection.ltr
..layout(maxWidth: constraints.maxWidth);
final heightErrorMessage = textPainter.size.height + 8;
return Stack(
children: [
// Separate container with identical height of text field which is placed behind the actual textfield
Container(
height: height,
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
color: Colors.black,
blurRadius: 3,
offset: Offset(3, 3),
),
],
borderRadius: BorderRadius.circular(
10.0,
),
),
),
Container(
// Add height of error message if it is displayed
height: errorText != null ? height + heightErrorMessage : height,
child: TextField(
decoration: InputDecoration(
fillColor: Colors.black,
filled: true,
errorStyle: errorStyle,
errorText: errorText,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
10.0,
),
),
labelText: labelText,
),
controller: controller,
),
),
],
);
});
}
}

The problem when we use a Container, Material or any other Widget to wrap the input text field in order to apply the shadow is if we use the hint text, error text or any other property that change the size of our textbox, the design would be broken.
Intead of wrapping the input in another widget you could use custom painter extending the InputBorder class.
For example:
class MyInputBorder extends OutlineInputBorder {}
Copy the following methods from the OutlineInputBorder implementation (Used for this example) to your new class:
_gapBorderPath
_cornersAreCircular
paint
Then in the paint method you could add the following lines
Path path = Path();
path.addRRect(center);
canvas.drawShadow(path, Colors.black, 4, true);
The above lines must be included before the canvas.drawRRect line:
Example:
if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) {
// paint the shadow for the outlined shape
Path path = Path();
path.addRRect(center);
canvas.drawShadow(path, shadowColor!, elevation, true);
canvas.drawRRect(center, paint);
} else {... other code omitted to keep simple}
Then, in your Widget, use the new Input border:
TextField(
decoration: InputDecoration(
border: MyInputBorder()
),
)
The result produced looks like the following without any of the issues of wrapper solutions:
text box with shadow
Here is a complete sample implementation, the post is in spanish but it has the idea explained: Full article for reference

#mrramos answer was almost complete but this code wouldn't give the desired result, I read the article suggested and implemented my own class, my use case is only a shadow for the text field for when its selected therefore the naming of it.
Quick explanation of this as its a lot to read and most of it is not necessary to understand just to implement a simple shadow.
paint() method is copied from OutlineInputBorder as _cornersAreCircular() and _gapBorderPath()
There is an addition of these few lines in the paint method for it to give the shadow.
Path path = Path();
path.addRRect(center);
canvas.drawShadow(path, Colors.black, 5, true);
final shadowPaint = Paint();
shadowPaint.strokeWidth = 0;
shadowPaint.color = Colors.white;
shadowPaint.style = PaintingStyle.fill;
canvas.drawRRect(center, shadowPaint);
canvas.drawRRect(center, paint);
Complete file class.
import 'package:flutter/material.dart';
import 'dart:ui' show lerpDouble;
import 'dart:math' as math;
class SelectedInputBorderWithShadow extends OutlineInputBorder {
const SelectedInputBorderWithShadow({
BorderSide borderSide = const BorderSide(),
borderRadius = const BorderRadius.all(Radius.circular(5)),
gapPadding = 4.0,
}) : super(
borderSide: borderSide,
borderRadius: borderRadius,
gapPadding: gapPadding,
);
static bool _cornersAreCircular(BorderRadius borderRadius) {
return borderRadius.topLeft.x == borderRadius.topLeft.y &&
borderRadius.bottomLeft.x == borderRadius.bottomLeft.y &&
borderRadius.topRight.x == borderRadius.topRight.y &&
borderRadius.bottomRight.x == borderRadius.bottomRight.y;
}
Path _gapBorderPath(
Canvas canvas, RRect center, double start, double extent) {
// When the corner radii on any side add up to be greater than the
// given height, each radius has to be scaled to not exceed the
// size of the width/height of the RRect.
final RRect scaledRRect = center.scaleRadii();
final Rect tlCorner = Rect.fromLTWH(
scaledRRect.left,
scaledRRect.top,
scaledRRect.tlRadiusX * 2.0,
scaledRRect.tlRadiusY * 2.0,
);
final Rect trCorner = Rect.fromLTWH(
scaledRRect.right - scaledRRect.trRadiusX * 2.0,
scaledRRect.top,
scaledRRect.trRadiusX * 2.0,
scaledRRect.trRadiusY * 2.0,
);
final Rect brCorner = Rect.fromLTWH(
scaledRRect.right - scaledRRect.brRadiusX * 2.0,
scaledRRect.bottom - scaledRRect.brRadiusY * 2.0,
scaledRRect.brRadiusX * 2.0,
scaledRRect.brRadiusY * 2.0,
);
final Rect blCorner = Rect.fromLTWH(
scaledRRect.left,
scaledRRect.bottom - scaledRRect.blRadiusY * 2.0,
scaledRRect.blRadiusX * 2.0,
scaledRRect.blRadiusX * 2.0,
);
// This assumes that the radius is circular (x and y radius are equal).
// Currently, BorderRadius only supports circular radii.
const double cornerArcSweep = math.pi / 2.0;
final double tlCornerArcSweep = math.acos(
(1 - start / scaledRRect.tlRadiusX).clamp(0.0, 1.0),
);
final Path path = Path()..addArc(tlCorner, math.pi, tlCornerArcSweep);
if (start > scaledRRect.tlRadiusX)
path.lineTo(scaledRRect.left + start, scaledRRect.top);
const double trCornerArcStart = (3 * math.pi) / 2.0;
const double trCornerArcSweep = cornerArcSweep;
if (start + extent < scaledRRect.width - scaledRRect.trRadiusX) {
path.moveTo(scaledRRect.left + start + extent, scaledRRect.top);
path.lineTo(scaledRRect.right - scaledRRect.trRadiusX, scaledRRect.top);
path.addArc(trCorner, trCornerArcStart, trCornerArcSweep);
} else if (start + extent < scaledRRect.width) {
final double dx = scaledRRect.width - (start + extent);
final double sweep = math.asin(
(1 - dx / scaledRRect.trRadiusX).clamp(0.0, 1.0),
);
path.addArc(trCorner, trCornerArcStart + sweep, trCornerArcSweep - sweep);
}
return path
..moveTo(scaledRRect.right, scaledRRect.top + scaledRRect.trRadiusY)
..lineTo(scaledRRect.right, scaledRRect.bottom - scaledRRect.brRadiusY)
..addArc(brCorner, 0.0, cornerArcSweep)
..lineTo(scaledRRect.left + scaledRRect.blRadiusX, scaledRRect.bottom)
..addArc(blCorner, math.pi / 2.0, cornerArcSweep)
..lineTo(scaledRRect.left, scaledRRect.top + scaledRRect.tlRadiusY);
}
#override
void paint(
Canvas canvas,
Rect rect, {
double? gapStart,
double gapExtent = 0.0,
double gapPercentage = 0.0,
TextDirection? textDirection,
}) {
assert(gapExtent != null);
assert(gapPercentage >= 0.0 && gapPercentage <= 1.0);
assert(_cornersAreCircular(borderRadius));
final Paint paint = borderSide.toPaint();
final RRect outer = borderRadius.toRRect(rect);
final RRect center = outer.deflate(borderSide.width / 2.0);
if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) {
Path path = Path();
path.addRRect(center);
canvas.drawShadow(path, Colors.black, 5, true);
final shadowPaint = Paint();
shadowPaint.strokeWidth = 0;
shadowPaint.color = Colors.white;
shadowPaint.style = PaintingStyle.fill;
canvas.drawRRect(center, shadowPaint);
canvas.drawRRect(center, paint);
} else {
final double extent =
lerpDouble(0.0, gapExtent + gapPadding * 2.0, gapPercentage)!;
switch (textDirection!) {
case TextDirection.rtl:
final Path path = _gapBorderPath(canvas, center,
math.max(0.0, gapStart + gapPadding - extent), extent);
canvas.drawPath(path, paint);
break;
case TextDirection.ltr:
final Path path = _gapBorderPath(
canvas, center, math.max(0.0, gapStart - gapPadding), extent);
canvas.drawPath(path, paint);
break;
}
}
}
}
and my result looks like this.

You can wrap TextFormField into Container add shadow too Container this will add shadow to you TextFormFieldbut it also adds color to TextFormField.
To remove Color from TextFormField use fillColor and filled property on TextFormField.
You Can control the opacity of a color line Colors.black.withOpacity(0.3).
Checkout Code Below:
final Widget password = Container(
decoration: BoxDecoration(
boxShadow: [
const BoxShadow(
blurRadius: 8,
),
],
borderRadius: BorderRadius.circular(5.0),
),
child: TextFormField(
obscureText: true,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
prefixIcon: const Icon(
Icons.lock,
color: Color(0xff224597),
),
hintText: 'Password',
contentPadding: const EdgeInsets.fromLTRB(
20.0,
10.0,
20.0,
10.0,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
borderSide: const BorderSide(
color: Colors.white,
width: 3.0,
),
),
),
),
);
YOU CAN CHECKOUT THE OUTPUT HERE

Related

how to add gradiant color to textformfield border without wrap with container in flutter?

TextFormField(
keyboardType: keybordType,
obscureText: obscureText == true ? true : false,
controller: controller,
validator: validator,
autofocus: autofocus == true ? true : false,
focusNode: focusNode,
onFieldSubmitted: onFieldSubmitted,
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration(
hintStyle: KMTextStyleGrey_13,
hintText: hintText,
border: tFFNonBorder,
contentPadding:
EdgeInsets.only(bottom: 12, left: 12, right: 12),
focusedBorder: tFFNonBorder,
enabledBorder: tFFNonBorder,
errorBorder: tFFNonBorder,
suffixIcon: suffixIcon),
)
You could write your own InputBorder class like this:
class GradientOutlineInputBorder extends InputBorder {
const GradientOutlineInputBorder({
BorderSide borderSide = const BorderSide(),
this.borderRadius = const BorderRadius.all(Radius.circular(4.0)),
this.gapPadding = 4.0,
required this.focusedGradient,
required this.unfocusedGradient,
}) : assert(borderRadius != null),
assert(gapPadding != null && gapPadding >= 0.0),
super(borderSide: borderSide);
final Gradient focusedGradient;
final Gradient unfocusedGradient;
static bool _cornersAreCircular(BorderRadius borderRadius) {
return borderRadius.topLeft.x == borderRadius.topLeft.y &&
borderRadius.bottomLeft.x == borderRadius.bottomLeft.y &&
borderRadius.topRight.x == borderRadius.topRight.y &&
borderRadius.bottomRight.x == borderRadius.bottomRight.y;
}
final double gapPadding;
final BorderRadius borderRadius;
#override
bool get isOutline => true;
#override
GradientOutlineInputBorder copyWith({
BorderSide? borderSide,
BorderRadius? borderRadius,
double? gapPadding,
}) {
return GradientOutlineInputBorder(
borderSide: borderSide ?? this.borderSide,
borderRadius: borderRadius ?? this.borderRadius,
gapPadding: gapPadding ?? this.gapPadding,
focusedGradient: focusedGradient,
unfocusedGradient: unfocusedGradient);
}
#override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(borderSide.width);
}
#override
GradientOutlineInputBorder scale(double t) {
return GradientOutlineInputBorder(
borderSide: borderSide.scale(t),
borderRadius: borderRadius * t,
gapPadding: gapPadding * t,
focusedGradient: focusedGradient,
unfocusedGradient: unfocusedGradient);
}
#override
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
if (a is GradientOutlineInputBorder) {
final GradientOutlineInputBorder outline = a;
return GradientOutlineInputBorder(
borderRadius:
BorderRadius.lerp(outline.borderRadius, borderRadius, t)!,
borderSide: BorderSide.lerp(outline.borderSide, borderSide, t),
gapPadding: outline.gapPadding,
focusedGradient: focusedGradient,
unfocusedGradient: unfocusedGradient);
}
return super.lerpFrom(a, t);
}
#override
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
if (b is GradientOutlineInputBorder) {
final GradientOutlineInputBorder outline = b;
return GradientOutlineInputBorder(
borderRadius:
BorderRadius.lerp(borderRadius, outline.borderRadius, t)!,
borderSide: BorderSide.lerp(borderSide, outline.borderSide, t),
gapPadding: outline.gapPadding,
focusedGradient: focusedGradient,
unfocusedGradient: unfocusedGradient);
}
return super.lerpTo(b, t);
}
#override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..addRRect(borderRadius
.resolve(textDirection)
.toRRect(rect)
.deflate(borderSide.width));
}
#override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
}
Path _gapBorderPath(
Canvas canvas, RRect center, double start, double extent) {
final Rect tlCorner = Rect.fromLTWH(
center.left,
center.top,
center.tlRadiusX * 2.0,
center.tlRadiusY * 2.0,
);
final Rect trCorner = Rect.fromLTWH(
center.right - center.trRadiusX * 2.0,
center.top,
center.trRadiusX * 2.0,
center.trRadiusY * 2.0,
);
final Rect brCorner = Rect.fromLTWH(
center.right - center.brRadiusX * 2.0,
center.bottom - center.brRadiusY * 2.0,
center.brRadiusX * 2.0,
center.brRadiusY * 2.0,
);
final Rect blCorner = Rect.fromLTWH(
center.left,
center.bottom - center.brRadiusY * 2.0,
center.blRadiusX * 2.0,
center.blRadiusY * 2.0,
);
const double cornerArcSweep = math.pi / 2.0;
final double tlCornerArcSweep = start < center.tlRadiusX
? math.asin(start / center.tlRadiusX)
: math.pi / 2.0;
final Path path = Path()
..addArc(tlCorner, math.pi, tlCornerArcSweep)
..moveTo(center.left + center.tlRadiusX, center.top);
if (start > center.tlRadiusX) {
path.lineTo(center.left + start, center.top);
}
const double trCornerArcStart = (3 * math.pi) / 2.0;
const double trCornerArcSweep = cornerArcSweep;
if (start + extent < center.width - center.trRadiusX) {
path
..relativeMoveTo(extent, 0.0)
..lineTo(center.right - center.trRadiusX, center.top)
..addArc(trCorner, trCornerArcStart, trCornerArcSweep);
} else if (start + extent < center.width) {
final double dx = center.width - (start + extent);
final double sweep = math.acos(dx / center.trRadiusX);
path.addArc(trCorner, trCornerArcStart + sweep, trCornerArcSweep - sweep);
}
return path
..moveTo(center.right, center.top + center.trRadiusY)
..lineTo(center.right, center.bottom - center.brRadiusY)
..addArc(brCorner, 0.0, cornerArcSweep)
..lineTo(center.left + center.blRadiusX, center.bottom)
..addArc(blCorner, math.pi / 2.0, cornerArcSweep)
..lineTo(center.left, center.top + center.trRadiusY);
}
#override
void paint(
Canvas canvas,
Rect rect, {
double? gapStart,
double gapExtent = 0.0,
double gapPercentage = 0.0,
TextDirection? textDirection,
}) {
assert(gapExtent != null);
assert(gapPercentage >= 0.0 && gapPercentage <= 1.0);
assert(_cornersAreCircular(borderRadius));
final RRect outer = borderRadius.toRRect(rect);
final Paint paint = borderSide.toPaint();
final bool isFocused = borderSide.width == 2.0;
paint.shader = isFocused
? focusedGradient.createShader(outer.outerRect)
: unfocusedGradient.createShader(outer.outerRect);
final RRect center = outer.deflate(borderSide.width / 2.0);
if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) {
canvas.drawRRect(center, paint);
} else {
final double extent =
ui.lerpDouble(0.0, gapExtent + gapPadding * 2.0, gapPercentage)!;
switch (textDirection) {
case TextDirection.rtl:
{
final Path path = _gapBorderPath(
canvas, center, gapStart + gapPadding - extent, extent);
canvas.drawPath(path, paint);
break;
}
case TextDirection.ltr:
{
final Path path =
_gapBorderPath(canvas, center, gapStart - gapPadding, extent);
canvas.drawPath(path, paint);
break;
}
default:
break;
}
}
}
#override
bool operator ==(dynamic other) {
if (identical(this, other)) {
return true;
}
if (runtimeType != other.runtimeType) {
return false;
}
final GradientOutlineInputBorder typedOther =
other as GradientOutlineInputBorder;
return typedOther.borderSide == borderSide &&
typedOther.borderRadius == borderRadius &&
typedOther.gapPadding == gapPadding;
}
#override
int get hashCode => hashValues(borderSide, borderRadius, gapPadding);
}
To be used like this:
var gradient = LinearGradient(
colors: <Color>[
Colors.orange,
Colors.purple,
],
);
TextFormField(
decoration: const InputDecoration(
focusedBorder: GradientOutlineInputBorder(
borderSide: BorderSide(width: 5),
gapPadding: 12.0,
focusedGradient: gradient,
unfocusedGradient: gradient,
),
enabledBorder: GradientOutlineInputBorder(
borderSide: BorderSide(width: 3),
focusedGradient: gradient,
unfocusedGradient: gradient,
),
),
),

How to make a button with rounded edges in flutter?

Making a rounded corner button is so simple, but I want to make a button that its edges are also rounded.
maybe I should use CustomPaint?
SquircleBorder might help you:
Container(
width: 56.0,
height: 56.0,
child: Material(
color: Colors.blueGrey[400],
shape: SquircleBorder(
side: BorderSide(color: Colors.grey, width: 3.0),
),
child: Icon(Icons.settings),
),
),
class SquircleBorder extends ShapeBorder {
final BorderSide side;
final double superRadius;
const SquircleBorder({
this.side: BorderSide.none,
this.superRadius: 5.0,
})
: assert(side != null),
assert(superRadius != null);
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);
#override
ShapeBorder scale(double t) {
return new SquircleBorder(
side: side.scale(t),
superRadius: superRadius * t,
);
}
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return _squirclePath(rect.deflate(side.width), superRadius);
}
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return _squirclePath(rect, superRadius);
}
static Path _squirclePath(Rect rect, double superRadius) {
final c = rect.center;
final dx = c.dx * (1.0 / superRadius);
final dy = c.dy * (1.0 / superRadius);
return new Path()
..moveTo(c.dx, 0.0)
..relativeCubicTo(c.dx - dx, 0.0, c.dx, dy, c.dx, c.dy)
..relativeCubicTo(0.0, c.dy - dy, -dx, c.dy, -c.dx, c.dy)
..relativeCubicTo(-(c.dx - dx), 0.0, -c.dx, -dy, -c.dx, -c.dy)
..relativeCubicTo(0.0, -(c.dy - dy), dx, -c.dy, c.dx, -c.dy)
..close();
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
var path = getOuterPath(rect.deflate(side.width / 2.0), textDirection: textDirection);
canvas.drawPath(path, side.toPaint());
}
}
}
Use a ContinuousRectangleBorder shape:
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: ContinuousRectangleBorder(
side: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(18)),
),
),
onPressed: () {},
child: Text('Click'),
);
That shape is called a Squircle. See: https://en.wikipedia.org/wiki/Squircle

Flutter rounded TabBar border indicator

I spent way too many hours searching for a similar solution but it seems that nobody on earth is implementing such design. I want to indicate selected TabBar Tab with such indicator:
Currently it looks like this:
Code of current TabBar:
const TabBar(
indicator: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.blue, width: 5),
),
),
labelPadding: EdgeInsets.zero,
tabs: [
_Tab(icon: Icons.home, text: 'Home'),
_Tab(icon: Icons.settings, text: 'Settings'),
_Tab(icon: Icons.cleaning_services, text: 'Clean'),
_Tab(icon: Icons.construction, text: 'Service'),
_Tab(icon: Icons.library_books, text: 'Resources'),
],
),
)
Has anybody got an idea of how this should look like?
You must create your own Decoration. Have a look to this guide : https://medium.com/swlh/flutter-custom-tab-indicator-for-tabbar-d72bbc6c9d0c
It creates a custom point under the tab, so you can copy that to create your kind of indicator
Thanks, BabC.
Here's the final result:
class _TabIndicator extends BoxDecoration {
final BoxPainter _painter;
_TabIndicator() : _painter = _TabIndicatorPainter();
#override
BoxPainter createBoxPainter([onChanged]) => _painter;
}
class _TabIndicatorPainter extends BoxPainter {
final Paint _paint;
_TabIndicatorPainter()
: _paint = Paint()
..color = Colors.blue
..isAntiAlias = true;
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
final double _xPos = offset.dx + cfg.size.width / 2;
canvas.drawRRect(
RRect.fromRectAndCorners(
Rect.fromLTRB(_xPos - 20, 0, _xPos + 20, 5),
bottomLeft: const Radius.circular(5.0),
bottomRight: const Radius.circular(5.0),
),
_paint,
);
}
}

How to implement a Chat bubble shaped widget in flutter

I want to design a widget of shape of a chat bubble where one corner is pinned and its height should adjust to the lines of the text? For now I'm using ClipRRect widget with some borderRadius. But I want one corner pinned. Any suggestions ?
UPDATE
I know this can be done using a stack but I'm looking for a better solution since I have to use it many times in a single view and using many stacks might affect the performs. ( correct me here if I'm wrong )
For someone who want this get done with library. You can add bubble: ^1.1.9+1 (Take latest) package from pub.dev and wrap your message with Bubble.
Bubble(
style: right ? styleMe : styleSomebody,
//Your message content child here...
)
Here right is boolean which tells the bubble is at right or left, Write your logic for that and add the style properties styleMe and styleSomebody inside your widget as shown below. Change style according to your theme.
double pixelRatio = MediaQuery.of(context).devicePixelRatio;
double px = 1 / pixelRatio;
BubbleStyle styleSomebody = BubbleStyle(
nip: BubbleNip.leftTop,
color: Colors.white,
elevation: 1 * px,
margin: BubbleEdges.only(top: 8.0, right: 50.0),
alignment: Alignment.topLeft,
);
BubbleStyle styleMe = BubbleStyle(
nip: BubbleNip.rightTop,
color: Colors.grey,
elevation: 1 * px,
margin: BubbleEdges.only(top: 8.0, left: 50.0),
alignment: Alignment.topRight,
);
Hi im also searching for a chat bubble shaped widget finally i made one.
I have made this in custom Painter and i'm not good at it.
import 'package:flutter/material.dart';
class ChatBubble extends CustomPainter {
final Color color;
final Alignment alignment;
ChatBubble({
#required this.color,
this.alignment,
});
var _radius = 10.0;
var _x = 10.0;
#override
void paint(Canvas canvas, Size size) {
if (alignment == Alignment.topRight) {
canvas.drawRRect(
RRect.fromLTRBAndCorners(
0,
0,
size.width - 8,
size.height,
bottomLeft: Radius.circular(_radius),
topRight: Radius.circular(_radius),
topLeft: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
var path = new Path();
path.moveTo(size.width - _x, size.height - 20);
path.lineTo(size.width - _x, size.height);
path.lineTo(size.width, size.height);
canvas.clipPath(path);
canvas.drawRRect(
RRect.fromLTRBAndCorners(
size.width - _x,
0.0,
size.width,
size.height,
topRight: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
} else {
canvas.drawRRect(
RRect.fromLTRBAndCorners(
_x,
0,
size.width,
size.height,
bottomRight: Radius.circular(_radius),
topRight: Radius.circular(_radius),
topLeft: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
var path = new Path();
path.moveTo(0, size.height);
path.lineTo(_x, size.height);
path.lineTo(_x, size.height-20);
canvas.clipPath(path);
canvas.drawRRect(
RRect.fromLTRBAndCorners(
0,
0.0,
_x,
size.height,
topRight: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
copy and paste the above code and paste in your project
Align(
alignment: alignment, //Change this to Alignment.topRight or Alignment.topLeft
child: CustomPaint(
painter: ChatBubble(color: Colors.blue, alignment: alignment),
child: Container(
margin: EdgeInsets.all(10),
child: Stack(
children: <Widget>[
TextView("Hello World"),
],
),
),
),
)
paste this code where you have to show chatBubble Widget.
And i have also upload this code in bitbucket ChatBubble Widget Please be free if you have anything to contribute.
Sorry I am not able to show you the code for it but I can present an idea that might work if you implement it correctly. Suppose the Widget you made with ClipRect is called MyChatBubbleRect. Now, make another widget that draws a triangle using CustomPainter, let's call it MyChatBubbleTriangle, of course fill it with same color as the chat bubble but you can use a different color initially for debugging. Now that we have two widgets we can stack 'em together on top of each other and using Positioned widget over the MyChatBubbleTriangle. Something like this:
Stack(
children : [
MyChatBubbleRect(), // Maybe decrease the width a bit
Positioned(
top: 0,
right: 0,
child: MyChatBubbleTriangle()
)
]
)
This is just an idea I think you can pursue. Sorry couldn't provide the proper source code.
Chat Body
DecoratedBox(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8.0),
),
child: Text("your message goes here"),
);
Make a custom Triangle
class ChatBubbleTriangle extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = Colors.blue;
var path = Path();
path.lineTo(-10, 0);
path.lineTo(0, 10);
path.lineTo(10, 0);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
Use both of them within a stack wrapping ChatBubbleTriangle with Positioned() widget

How to create a dotted border around a box in flutter?

I am building a list of boxes layouts in my app using flutter. I want dotted border around the box. I have used card widget to create the boxes. But, how can I get dotted border around the boxes?
EDIT
I have added this as a package in pub.
Now, all you need to do is
DottedBorder(
color: Colors.black,
gap: 3,
strokeWidth: 1,
child: FlutterLogo(size: 148),
)
Working Solution [Outdated]
Like tomerpacific said in this answer, Flutter does not have a default implementation for dashed border at the moment.
I worked for some time yesterday and was able to come up with a solution using CustomPainter. Hope this helps someone.
Add the DashedRect to your container, like so
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
height: 400,
width: 300,
color: Colors.black12,
child: DashedRect(color: Colors.red, strokeWidth: 2.0, gap: 3.0,),
),
),
);
}
}
DashedRect.dart
import 'package:flutter/material.dart';
import 'dart:math' as math;
class DashedRect extends StatelessWidget {
final Color color;
final double strokeWidth;
final double gap;
DashedRect(
{this.color = Colors.black, this.strokeWidth = 1.0, this.gap = 5.0});
#override
Widget build(BuildContext context) {
return Container(
child: Padding(
padding: EdgeInsets.all(strokeWidth / 2),
child: CustomPaint(
painter:
DashRectPainter(color: color, strokeWidth: strokeWidth, gap: gap),
),
),
);
}
}
class DashRectPainter extends CustomPainter {
double strokeWidth;
Color color;
double gap;
DashRectPainter(
{this.strokeWidth = 5.0, this.color = Colors.red, this.gap = 5.0});
#override
void paint(Canvas canvas, Size size) {
Paint dashedPaint = Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
double x = size.width;
double y = size.height;
Path _topPath = getDashedPath(
a: math.Point(0, 0),
b: math.Point(x, 0),
gap: gap,
);
Path _rightPath = getDashedPath(
a: math.Point(x, 0),
b: math.Point(x, y),
gap: gap,
);
Path _bottomPath = getDashedPath(
a: math.Point(0, y),
b: math.Point(x, y),
gap: gap,
);
Path _leftPath = getDashedPath(
a: math.Point(0, 0),
b: math.Point(0.001, y),
gap: gap,
);
canvas.drawPath(_topPath, dashedPaint);
canvas.drawPath(_rightPath, dashedPaint);
canvas.drawPath(_bottomPath, dashedPaint);
canvas.drawPath(_leftPath, dashedPaint);
}
Path getDashedPath({
#required math.Point<double> a,
#required math.Point<double> b,
#required gap,
}) {
Size size = Size(b.x - a.x, b.y - a.y);
Path path = Path();
path.moveTo(a.x, a.y);
bool shouldDraw = true;
math.Point currentPoint = math.Point(a.x, a.y);
num radians = math.atan(size.height / size.width);
num dx = math.cos(radians) * gap < 0
? math.cos(radians) * gap * -1
: math.cos(radians) * gap;
num dy = math.sin(radians) * gap < 0
? math.sin(radians) * gap * -1
: math.sin(radians) * gap;
while (currentPoint.x <= b.x && currentPoint.y <= b.y) {
shouldDraw
? path.lineTo(currentPoint.x, currentPoint.y)
: path.moveTo(currentPoint.x, currentPoint.y);
shouldDraw = !shouldDraw;
currentPoint = math.Point(
currentPoint.x + dx,
currentPoint.y + dy,
);
}
return path;
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I do not expect this to fit in with all use cases and there is a lot of room for customization and improvement. Comment if you find any bugs.
You can use dotted_border Flutter package
return DottedBorder(
borderType: BorderType.RRect,
radius: Radius.circular(20),
dashPattern: [10, 10],
color: Colors.grey,
strokeWidth: 2,
child: Card(
color: Colors.amber,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Center(child: Text("hi")),
)
There is an one plugin for draw dotted border around widgets
https://pub.dev/packages/dotted_border
Using this plugin you can draw dotted or dashed border
//1. Install the plugin by add dependencies in pubspace.yaml
dotted_border: ^1.0.6
Add below code for show border
DottedBorder(
color: Colors.black,
strokeWidth: 1,
child: FlutterLogo(size: 148),
)
BorderStyle.none can be useful if you wanna apply some animation or remove\add border function onTap(like a lighting border) event or similar.