How to make a button with rounded edges in flutter? - 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

Related

Custom Shape for Container

Idea is to make a custom shaped container by giving it ShapeDecoration().
I used this thread as a reference.
Eventialy I achieved the shape I wanted, but once I wrap this custom shaped container with margin\padding\SizedBoxes around it - it's content is being pushed out of the bounds of the container. Check out screenshots.
Normaly it should be smth like this:
but with paddings between elements.
So the problem is that my customized container behavers realy wierd once it has any margin\padding arround it.
return Padding(
padding: const EdgeInsets.symmetric(vertical: 40),
child: Container(
padding: const EdgeInsets.all(8),
height: 88,
alignment: Alignment.center,
decoration: ShapeDecoration(
shape: UnitListItemBorderShape(
color: _isCurrent ? theme.listItemBorderColor : theme.listItemBackgroundColor,
),
color: theme.listItemBackgroundColor,
),
child: Stack(
children: [
//custom container's ineer content
] ,
),
);
UnitListItemBorderShape class:
class UnitListItemBorderShape extends ShapeBorder {
const UnitListItemBorderShape({color}) : _color = color;
final Color _color;
#override
EdgeInsetsGeometry get dimensions => const EdgeInsets.all(0);
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) => UnitListItemShape.getShape(
rect.width,
rect.height,
const Radius.circular(16),
const Radius.circular(20),
);
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
final Paint borderPaint = Paint()
..color = _color
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawPath(getOuterPath(rect), borderPaint);
}
#override
ShapeBorder scale(double t) => null;
}
UnitListItemShape class:
class UnitListItemShape {
static Path getShape(double width, double height, Radius borderRadius, Radius circleRadius) {
final double rightOffset = circleRadius.x;
final Rect rect = Rect.fromCenter(center: Offset(width - rightOffset - 4, height / 2), width: 48, height: 48);
return Path()
..moveTo(borderRadius.x, 0)
..lineTo(width - borderRadius.x - rightOffset, 0)
..arcToPoint(Offset(width - rightOffset, borderRadius.x), radius: borderRadius)
..lineTo(width - rightOffset, (height / 2) - rightOffset - 4)
// ..addRect(rect)
..arcTo(rect, -70 * math.pi / 180, 150 * math.pi / 180, false)
..lineTo(width - rightOffset, height - borderRadius.x)
..arcToPoint(Offset(width - borderRadius.x - rightOffset, height), radius: borderRadius)
..lineTo(borderRadius.x, height)
..arcToPoint(Offset(0, height - borderRadius.x), radius: borderRadius)
..lineTo(0, borderRadius.x)
..arcToPoint(Offset(borderRadius.x, 0), radius: borderRadius)
..fillType = PathFillType.evenOdd
..close();
}
}

Flutter How to create box container with arrow on top?

Hi how do i create box container with arrow on top without using any pub packages.
Like below.
You can customize ShapeBorder similar to this post. Then instead of using rect.bottomCenter for Path(), change it to rect.TopCenter to place the drawn Rect at the top.
Path()dart
..addRRect(RRect.fromRectAndRadius(rect, Radius.circular(rect.height / 2)))
..moveTo(rect.topCenter.dx - 10, rect.topCenter.dy)
..relativeLineTo(10, 20)
..relativeLineTo(20, -20)
..close();
For my case, I customized a shapeborder for flutter tooltip.
class TooltipShippingFeeBorder extends ShapeBorder{
#override
EdgeInsetsGeometry get dimensions => const EdgeInsets.only(top:20);
#override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect), Offset.zero);
}
#override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
rect = Rect.fromPoints(rect.topLeft, rect.bottomRight - Offset(0, 20));
return Path()
..addRRect(RRect.fromRectAndRadius(rect, const Radius.circular(6)))
..moveTo(rect.topCenter.dx - 10, rect.topCenter.dy)
..relativeLineTo(10, -20)
..relativeLineTo(20, 20)
..close();
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
}
#override
ShapeBorder scale(double t) => this;
}
And use it on tooltip like this:
Tooltip(
triggerMode: TooltipTriggerMode.tap,
padding: const EdgeInsets.only(bottom: 16, left: 10, right: 10),
decoration: ShapeDecoration(
shape: TooltipShippingFeeBorder(),
color: WalletTheme.primaryColor),
message: '$shippingFeeMessage\n',
child: const Icon(
Icons.info_outline,
size: 16,
color: WalletTheme.grey2,
),
)
So in your case, you also create the same border and use ShapreDecoration for Container.
ShapeDecoration(
shape: TooltipShippingFeeBorder(),
color: WalletTheme.primaryColor)

Unable to set gradient color to my custom shape border in flutter app

I have created a custom shape using CustomBorder class, and I want to set a gradient color to my custom shape. Please see the below code that I have tried.
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: [Colors.orangeAccent, Colors.orange]
)
),
child: Material(
color: Colors.deepOrangeAccent,
shape: CustomShapeBorder(),
child: IconButton(
icon: FaIcon(FontAwesomeIcons.instagram),
iconSize: 30.0,
padding: const EdgeInsets.only(top: 60.0, left: 30.0),
color: Colors.white,
),
),
)
My CustomShapeBorder() class :
class CustomShapeBorder extends ShapeBorder {
final double distanceFromWall = 12;
final double controlPointDistanceFromWall = 5;
#override
// TODO: implement dimensions
EdgeInsetsGeometry get dimensions => null;
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
// TODO: implement getInnerPath
return null;
}
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
// TODO: implement getOuterPath
return getClip(Size(260.0,180.0));
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
// TODO: implement paint
}
#override
ShapeBorder scale(double t) {
// TODO: implement scale
return null;
}
Path getClip(Size size) {
Path clippedPath = new Path();
clippedPath.lineTo(0, size.height);
clippedPath.quadraticBezierTo(30, size.height + 10, size.width * 0.20, size.height - 50);
clippedPath.quadraticBezierTo(70, size.height - 120, size.width * 0.40, size.height * 0.35);
clippedPath.quadraticBezierTo(180, (size.height - (size.height* 0.6)), size.width - 40 , 32 );
clippedPath.quadraticBezierTo(250, 0, size.width, 0);
clippedPath.lineTo(size.width, 0);
clippedPath.close();
return clippedPath;
}
}
Output:(Just refer to the upper custom shape, forget the bottom one)
If I remove the color: Colors.deepOrangeAccent from child: Material then the output is :
BoxDecoration is optimized for rectangular widgets. Since the widget here that you'd like to apply gradient is similar to a blob. It's best to use ShapeDecoration instead. Also, Material should be used as a parent Widget and not a child of a Widget.
Container(
decoration: ShapeDecoration(...),
child: IconButton(...),
)

Text between 2 containers in flutter

I would like to show a text between 2 containers in flutter. The problem is that the containers adapt to the size of the text. I do not want that behavior. want something like this. (Am very new to flutter).
I want to make a music player. The text can not be split.
Edit: Accordingly to what you've asked, you want to create a custom player that updates its color based on the song current position.
For that, you can create a CustomPaint widget with a CustomPainter player that updates whenever the song state changes.
class MyPlayerBar extends CustomPainter {
MyPlayerBar({this.fullSongTimeInSeconds, this.currentSecond});
final int fullSongTimeInSeconds;
final int currentSecond;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
double cursor = (currentSecond * size.width) / fullSongTimeInSeconds;
Radius cornerRadius = Radius.circular(3.0);
// Already played half color (your darker orange)
paint.color = Color.fromRGBO(206, 69, 0, 1.0);
// Painting already played half
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(0.0, 0.0, cursor, size.height),
topLeft: cornerRadius, bottomLeft: cornerRadius),
paint);
// Yet to play half color (your lighter orange)
paint.color = Color.fromRGBO(227, 113, 18, 1.0);
// Painting the remaining space
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(cursor, 0.0, size.width - cursor, size.height),
bottomRight: cornerRadius, topRight: cornerRadius),
paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
I've created a full example that simulates a 3 minute song (180 seconds) that will result in the following:
Full example code:
class MyPlayer extends StatefulWidget {
_MyPlayerState createState() => _MyPlayerState();
}
class _MyPlayerState extends State<MyPlayer> {
int _songCurrentPosition = 0;
int _fullSongInSeconds = 180; // 3 minutes song
#override
void initState() {
super.initState();
_songPlaying();
}
void _songPlaying() async {
if (_songCurrentPosition >= _fullSongInSeconds) return;
await Future.delayed(Duration(seconds: 1));
setState(() => _songCurrentPosition += 1);
_songPlaying();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My player'),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: CustomPaint(
painter: MyPlayerBar(
currentSecond: _songCurrentPosition, // Your current song position in seconds
fullSongTimeInSeconds: _fullSongInSeconds,
),
child: Container(
alignment: Alignment.center,
height: 30.0,
width: double.infinity,
child: Text(
'Playing: 01 - Hey, this is my life',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
),
),
),
);
}
}
class MyPlayerBar extends CustomPainter {
MyPlayerBar({this.fullSongTimeInSeconds, this.currentSecond});
final int fullSongTimeInSeconds;
final int currentSecond;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
double cursor = (currentSecond * size.width) / fullSongTimeInSeconds;
Radius cornerRadius = Radius.circular(3.0);
// Already played half color (your darker orange)
paint.color = Color.fromRGBO(206, 69, 0, 1.0);
// Painting already played half
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(0.0, 0.0, cursor, size.height),
topLeft: cornerRadius, bottomLeft: cornerRadius),
paint);
// Yet to play half color (your lighter orange)
paint.color = Color.fromRGBO(227, 113, 18, 1.0);
// Painting the remaining space
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(cursor, 0.0, size.width - cursor, size.height),
bottomRight: cornerRadius, topRight: cornerRadius),
paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}

How to add drop shadow to TextFormField In 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