Organizations today strive to deliver presentable UIs in their mobile apps for end users — a great visual experience brings users back.
When it comes to creating a beautiful UI with ease, Flutter has gained popularity since it’s fundamentally a UI Toolkit that can build beautiful, natively compiled, multi-platform applications from a single codebase.
Visual experiences can be enhanced in many ways, including subtle highlights such as colors, highlights around elements, and border decorations. Borders — in particular around the UI components — can provide intuitiveness as well as hierarchy and structure to the end user.
Borders vary in all shapes and sizes, from normal borders with a single color to ones with a gradient, which require different approaches in Flutter.
In this article, you will learn how to create borders in Flutter. Not just a typical default border, but more interesting ones that make widgets and containers stand out. Aside from the basics, you will also learn how to draw borders with gradients.
Let’s get started.
BoxDecoration
BoxDecoration
BoxDecoration
is an immutable class which allows you to paint a box by specifying various properties. When building elements such as a Container
, it may be supplied as a decorative property. It is used to give decoration to the Widget
where it is utilized, as the name indicates.
Supported decorations include color, gradient, background image, border, and shadow.
You can add borders by passing border
properties to the BoxDecoration
class. There are multiple ways to create an instance of this, such as:
Border()
constructorBorder.all
factory patternBorder.merge
static methodA specified border on the Container
is drawn on top of everything, including; color
, gradient
, and image
.
Let’s begin with drawing a single-colored border around a widget, as demonstrated here:
// dart Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( border: Border.all(color: Colors.green[400], width: 3) ), child: Text("I am inside a border.", style: TextStyle(fontSize: 24),), );
In this example, the Text
widget is wrapped by a container and holds properties such as border width and color using BoxDecoration
. Alternatively, it is also possible to provide separate border properties to each side using the Border
constructor instead of Border.all(...)
.
You can change the border radius using the borderRadius
property inside BoxDecoration
.
// dart Container( width: 100, height:100, decoration:BoxDecoration( color: Colors.blue, // Color of the container borderRadius: BorderRadius.circular(20.0), // Radius of the border border: Border.all( width: 8.0, color: Colors.black, // Color of the border ) ) )
TextField
An instance of the UnderlineInputBorder
class with specific border properties such as color
and width
is supplied to the InputDecoration
class as a parameter, in order to draw the border around TextField
.
When the TextField
is in focus —such as when the user attempts to type text into the field, the border becomes active. The border disappears as soon as the TextField
loses focus.
// dart Container( child: TextField( decoration: InputDecoration( enabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: Colors.blue, width: 2.0 ), ), focusedBorder: InputBorder.none, disabledBorder: InputBorder.none, hintText: "Enter your name" ), ) )
Consider a border wrapped around text — it is actually a stroke that encloses every letter itself instead of the outer Container
.
// dart Stack( children: [ Text( 'I am inside a border.', style: TextStyle( fontSize: 24, foreground: Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3 ..color = Colors.green[400], ), ), Text( 'I am inside a border.', style: TextStyle( fontSize: 24, color: Colors.blue, ), ), ] );
If you only use the first Text
widget in the stack, it will only add a stroke; you will need to choose a Stack
widget that shows the border and fills both of the widgets. Stacking two Text
widgets on top of each other that have different font sizes will not work because the text is scaled from the center.
You’ve learned how to implement borders that are relatively basic. Now, let’s implement a gradient border around a Widget
, rather than simply a single-toned border.
When decorating a border with a gradient, two properties are required:
Gradient
, which contains the necessary information such as colors, etc.width
of the border strokeFor this, you need to implement an instance of the CustomPainter
class as shown in the below code:
// dart class CustomGradient extends CustomPainter { CustomGradient({this.gradient, this.sWidth}); final Gradient gradient; final double sWidth; final Paint p = Paint(); @override void paint(Canvas canvas, Size size) { Rect innerRect = Rect.fromLTRB(sWidth, sWidth, size.width - sWidth, size.height - sWidth); Rect outerRect = Offset.zero & size; p.shader = gradient.createShader(outerRect); Path borderPath = _calculateBorderPath(outerRect, innerRect); canvas.drawPath(borderPath, p); } Path _calculateBorderPath(Rect outerRect, Rect innerRect) { Path outerRectPath = Path()..addRect(outerRect); Path innerRectPath = Path()..addRect(innerRect); return Path.combine(PathOperation.difference, outerRectPath, innerRectPath); } @override bool shouldRepaint(CustomPainter oldDelegate) => true; }
Here, instead of drawing the four separate sides of the border, we have added two rectangles as shown in the example, one of which has the same size as the Widget
— whereas the other rectangle has the stroke width subtracted from its original size. PathOperation.difference
calculates the difference between the two rectangles, and the resulting difference acts as a stroke around the smaller rectangle.
To make the gradient work, we will need to add a shader to the paint object p
. In the example above, createShader
is the method which accepts the object of outerRect
as an argument to make the gradient reach the edges.
Now, to make use of our custom class CustomGradient
, we need an enclosing Widget
that contains a child widget and draws our gradient around it. Have a look at this example:
class CustomGradientContainer extends StatelessWidget { CustomGradientContainer({ @required gradient, @required this.child, this.strokeWidth = 3, }) : this.painter = CustomGradient( gradient: gradient, sWidth: strokeWidth ); final CustomGradient painter; final Widget child; final VoidCallback onPressed; final double strokeWidth; @override Widget build(BuildContext context) { return CustomPaint( painter: painter, child: child ); } }
This will draw a gradient around the text. However, this border will be tightly bound around the text itself, which is not conducive to a presentable UI. To improve our UI, we need to insert some further customizations, such as rounded corners and padding.
Take a look at the example below to see how to provide a rounded border to the CustomGradient
class:
// dart class CustomGradient extends CustomPainter { ... final Gradient gradient; final double sWidth; final Paint p = Paint(); // Existing declarations final double bRadius; // Declare border radius ... @override void paint(Canvas canvas, Size size) { ... // Existing rectangle declarations RRect innerRoundedRect = RRect.fromRectAndRadius(innerRect, Radius.circular(bRadius)); RRect outerRoundedRect = RRect.fromRectAndRadius(outerRect, Radius.circular(bRadius)); ... // Existing PaintObject declarations Path borderPath = _calculateBorderPath(outerRoundedRect, innerRoundedRect); } Path _calculateBorderPath(RRect outerRect, RRect innerRect) { // Update parameters Path outerRectPath = Path()..addRRect(outerRect); Path innerRectPath = Path()..addRRect(innerRect); return Path.combine(PathOperation.difference, outerRectPath, innerRectPath); } ... }
Once the CustomGradient
class is updated to specify the border radius property, you can modify CustomGradientContainer
to draw the border radius as below:
// dart class CustomGradientContainer extends StatelessWidget { CustomGradientContainer({ @required gradient, @required this.child, @required this.onPressed, this.strokeWidth = 3, this.bRadius = 36, // Add border radius declaration this.padding = 12 // Add padding declaration }) : this.painter = GradientPainter( gradient: gradient, strokeWidth: sWidth, borderRadius: bRadius // Add border radius declaration ); final GradientPainter painter; final Widget child; final VoidCallback onPressed; final double sWidth; final double bRadius; // Add border radius final double padding; // Add padding @override Widget build(BuildContext context) { return CustomPaint( painter: painter, child: Container( // Add container declaration padding: EdgeInsets.all(padding), // Add padding declaration child: child ) ); } }
Here’s what we have done: the constructor of the GradientBorderContainer
widget has been modified to declare border the radius and padding. We have then declared the value of the padding to wrap a Container
around the child Widget
with the respective padding.
Using a CustomPainter
class to create a border around a Widget
provides a lot more flexibility for decorations in Flutter.
In addition to the basics, we also looked at how to create a changing gradient border around a Widget
. You may change the gradient, padding, stroke width, and the width of the outline enclosing the border.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
One Reply to "How to create simple and gradient borders in Flutter"
It would be really useful to see an image of what the result looks like before you go in to the code. At present there isn’t an image at all. It’s unlikely many people will try the code if they don’t know what the result will be.