I have been scratching my head for a very long time just to get this type of design layout for a TextFormField but failed!
Notice the anti strait lines. My further search for this layout on the internet concludes that I can archive this type of layout using a custom clipper or clip-path. but I am just a beginner to learn flutter and never worked with custom clipper before, I have no idea how to use it. Please help.
I think you can use a CustomPainter :
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: CustomPaint(
painter: MyPainter(),
child: // your TextField with style so we don't see the borders etc...
),
),
),
);
}
}
class MyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// draw your rectangle from points here
}
// no need to repaint it because your shape is static
@override
bool shouldRepaint(Sky oldDelegate) => false;
@override
bool shouldRebuildSemantics(Sky oldDelegate) => false;
}
Here is the link for the basics of CustomPainter : link
Here is the written code with the widget displaying the TextField and of course your custom shape :
class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
// implementation of your custom active borders
bool emailActive = false;
FocusNode? emailNode;
@override
void initState() {
super.initState();
emailNode = FocusScopeNode()
..addListener(() {
setState(() {
emailActive = emailNode!.hasFocus;
});
print(emailActive);
});
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
final double width = size.width * 0.8;
final double height = width * 0.15;
return Scaffold(
appBar: AppBar(
title: Text('First screen'),
),
body: Center(
child: Container(
height: height,
width: width,
child: Stack(
children: [
Center(
// your shape
child: CustomPaint(
painter: _TextFieldPainter(width, height, emailActive),
),
),
Container(
height: height,
width: width,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
children: [
Container(
width: 35,
padding: const EdgeInsets.only(
top: 5.0), // so it's aligned with the textfield
child: Icon(Icons.email_rounded),
),
Container(
width: width - 35 - 20, // width - iconSize - padding
child: TextField(
decoration: InputDecoration(
hintText: 'Email id',
focusedBorder: InputBorder
.none, // so there is no border below the text field
),
focusNode: emailNode,
),
),
],
),
),
],
),
),
),
);
}
}
class _TextFieldPainter extends CustomPainter {
final double width;
final double height;
final bool active;
_TextFieldPainter(this.width, this.height, this.active);
@override
void paint(Canvas canvas, Size size) {
// The starting values so it's centered properly
final double startX = width / -2;
final double startY = height / -2;
// Where you set the color etc...
final Paint paint = Paint()
..color = active
? Colors.green
: Colors.black // where you change the color wether it's active or not
..strokeWidth = 1.0;
// Drawing the shape
canvas.drawLine(
Offset(startX, startY + 5.0), Offset(startX + width, startY), paint);
canvas.drawLine(Offset(startX + width, startY),
Offset(startX + width, startY + height), paint);
canvas.drawLine(Offset(startX + width, startY + height),
Offset(startX - 5.0, startY + height), paint);
canvas.drawLine(Offset(startX - 5.0, startY + height),
Offset(startX, startY + 5.0), paint);
}
// no need to repaint it here since there is no dynamic values
@override
bool shouldRepaint(_TextFieldPainter oldDelegate) => false;
@override
bool shouldRebuildSemantics(_TextFieldPainter oldDelegate) => false;
}
You just have to do the same for the password TextField.
And here is how it looks :