Skip to content

Instantly share code, notes, and snippets.

@rydmike
Last active August 31, 2021 12:44
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rydmike/691628e75729aa75d3143740f87e91b3 to your computer and use it in GitHub Desktop.
Save rydmike/691628e75729aa75d3143740f87e91b3 to your computer and use it in GitHub Desktop.
Flutter Rounded Border Solutions to Tweet: https://twitter.com/RydMike/status/1431999590547476483
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Border Fun',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
const double width = 220;
const double height = 150;
const double borderWidth = 20;
const double borderRadius = 40;
return Scaffold(
appBar: AppBar(title: const Text('Rounded Border Fun')),
body: Center(
child: ConstrainedBox(
//
// RydMike:
// Constrain the parent width to width of rounded box, this will also
// reveal if the solutions stays inside width constraints.
//
// When possible or obvious how to do it (did not put a lot of effort
// in to, further teaks probably possible if needed), the Tweeted
// solutions were slightly revised so they stay within parent
// constraints and use a corner radius with the actual provided
// radius and so that the border width remains uniform in the corners.
//
constraints: const BoxConstraints(maxWidth: width),
child: ListView(
children: const <Widget>[
SizedBox(height: 20),
Divider(),
Text('1) Alois Version'),
AloisVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@aloisdeniel')),
),
Divider(),
Text('2) Chinmay Version'),
ChinmayVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@ChinuKabi')),
),
Divider(),
Text('3) Mike Version'),
MikeVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@RydMike')),
),
Divider(),
Text('4) Pankaj Version'),
PankajVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@koiralapankaj7')),
),
Divider(),
Text('5) Paras Version'),
ParasVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@theretroportal')),
),
Divider(),
Text('6) Romain Version'),
RomainVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@lets4r')),
),
Divider(),
Text('7) Vladimir Version'),
SizedBox(height: 20),
VladimirVersion(
width: width,
height: height,
borderWidth: borderWidth,
borderRadius: borderRadius,
child: Center(child: Text('@romashkin_dev')),
),
SizedBox(height: 20),
Divider(),
],
),
),
),
);
}
}
// *****************************************************************************
// Alois version. Twitter @aloisdeniel
// https://twitter.com/aloisdeniel/status/1432210349546221568?s=20
//
// Same concept, but a bit different from Mike version tweeted before this.
class AloisVersion extends StatelessWidget {
const AloisVersion({
Key? key,
required this.width,
required this.height,
required this.borderWidth,
required this.borderRadius,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderWidth;
final double borderRadius;
final Widget? child;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
),
padding: EdgeInsets.only(
top: borderWidth,
right: borderWidth,
bottom: borderWidth,
),
child: Container(
width: width - borderWidth,
height: height - 2 * borderWidth,
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius - borderWidth),
bottomRight: Radius.circular(borderRadius - borderWidth),
),
),
child: child,
),
);
}
}
// *****************************************************************************
// Chinmay version. Twitter @ChinuKabi
// https://twitter.com/ChinuKabi/status/1432032337601052677?s=20
class ChinmayVersion extends StatelessWidget {
const ChinmayVersion({
Key? key,
required this.width,
required this.height,
required this.borderWidth,
required this.borderRadius,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderWidth;
final double borderRadius;
final Widget? child;
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
child: Container(
width: width,
height: height,
color: Colors.black,
child: Padding(
padding: EdgeInsets.only(
top: borderWidth,
bottom: borderWidth,
right: borderWidth,
),
child: ClipRRect(
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius - borderWidth),
bottomRight: Radius.circular(borderRadius - borderWidth),
),
child: Container(
color: Colors.blue[100],
child: child,
),
),
),
),
);
}
}
// *****************************************************************************
// Mike version. Twitter @RydMike
// https://twitter.com/RydMike/status/1432028503843745802?s=20
class MikeVersion extends StatelessWidget {
const MikeVersion({
Key? key,
required this.width,
required this.height,
required this.borderWidth,
required this.borderRadius,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderWidth;
final double borderRadius;
final Widget? child;
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
),
child: Container(
margin: EdgeInsets.only(
top: borderWidth,
right: borderWidth,
bottom: borderWidth,
),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius - borderWidth),
bottomRight: Radius.circular(borderRadius - borderWidth),
),
),
child: child,
),
);
}
}
// *****************************************************************************
// Pankaj version. Twitter @koiralapankaj7
// https://twitter.com/koiralapankaj7/status/1432354767347470342?s=20
//
// This is a variation of Mike version, tweeted before this version.
class PankajVersion extends StatelessWidget {
const PankajVersion({
Key? key,
required this.width,
required this.height,
required this.borderRadius,
required this.borderWidth,
this.borderColor,
this.backgroundColor,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderRadius;
final double borderWidth;
final Color? borderColor;
final Color? backgroundColor;
final Widget? child;
Widget _container({
required Color color,
required double radius,
double borderWidth = 0.0,
Widget? child,
}) {
return Container(
width: width,
height: height,
margin: EdgeInsets.only(
top: borderWidth,
right: borderWidth,
bottom: borderWidth,
),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.only(
topRight: Radius.circular(radius),
bottomRight: Radius.circular(radius),
),
),
child: child,
);
}
@override
Widget build(BuildContext context) {
return _container(
color: borderColor ?? Colors.black,
radius: borderRadius,
child: _container(
borderWidth: borderWidth,
radius: borderRadius - borderWidth,
color: backgroundColor ?? Colors.blue[100]!,
child: child,
),
);
}
}
// *****************************************************************************
// Paras version. Twitter: @theretroportal
// https://twitter.com/theretroportal/status/1432025705836003331?s=20
class ParasVersion extends StatelessWidget {
const ParasVersion({
Key? key,
required this.width,
required this.height,
required this.borderWidth,
required this.borderRadius,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderWidth;
final double borderRadius;
final Widget? child;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: Stack(
children: [
Positioned(
left: -borderWidth, // Border Width
top: 0,
child: Container(
width: width + borderWidth, // + border width
height: height,
decoration: BoxDecoration(
color: Colors.blue[100],
border: Border.all(
color: Colors.black,
width: borderWidth,
),
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
),
child: child,
),
),
],
),
);
}
}
// *****************************************************************************
// Romain version. Twitter: @lets4r
// https://twitter.com/lets4r/status/1432051560645857289?s=20
class RomainVersion extends StatelessWidget {
const RomainVersion({
Key? key,
required this.width,
required this.height,
required this.borderWidth,
required this.borderRadius,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderWidth;
final double borderRadius;
final Widget? child;
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
decoration: ShapeDecoration(
color: Colors.blue[100],
shape: ThreeRoundSidesShape(
side: BorderSide(color: Colors.black, width: borderWidth),
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
),
),
child: child,
);
}
}
class ThreeRoundSidesShape extends OutlinedBorder {
const ThreeRoundSidesShape({
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : super(side: side);
/// The radii for each corner.
final BorderRadiusGeometry borderRadius;
@override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(side.width);
}
@override
ShapeBorder scale(double t) {
return ThreeRoundSidesShape(
side: side.scale(t),
borderRadius: borderRadius * t,
);
}
@override
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
// Not implemented.
return super.lerpFrom(a, t);
}
@override
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
// Not implemented.
return super.lerpTo(b, t);
}
@override
ThreeRoundSidesShape copyWith(
{BorderSide? side, BorderRadius? borderRadius}) {
return ThreeRoundSidesShape(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
);
}
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
final double width = side.width;
final BorderRadius radius = borderRadius.resolve(textDirection);
final RRect outer = radius.toRRect(rect);
final RRect inner = RRect.fromLTRBAndCorners(
outer.left,
outer.top + width,
outer.right - width,
outer.bottom - width,
topLeft: radius.topLeft.deflate(width),
topRight: radius.topRight.deflate(width),
bottomRight: radius.bottomRight.deflate(width),
bottomLeft: radius.bottomLeft.deflate(width),
);
return Path()..addRRect(inner);
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
final double width = side.width;
if (width == 0.0) {
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect),
side.toPaint());
} else {
final BorderRadius radius = borderRadius.resolve(textDirection);
final RRect outer = radius.toRRect(rect);
final RRect inner = RRect.fromLTRBAndCorners(
outer.left,
outer.top + width,
outer.right - width,
outer.bottom - width,
topLeft: radius.topLeft.deflate(width),
topRight: radius.topRight.deflate(width),
bottomRight: radius.bottomRight.deflate(width),
bottomLeft: radius.bottomLeft.deflate(width),
);
final Paint paint = Paint()..color = side.color;
canvas.drawDRRect(outer, inner, paint);
}
}
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ThreeRoundSidesShape &&
other.side == side &&
other.borderRadius == borderRadius;
}
@override
int get hashCode => hashValues(side, borderRadius);
@override
String toString() {
return '${objectRuntimeType(this, 'MyRoundedRectangleBorder')}($side, $borderRadius)';
}
}
extension on Radius {
Radius deflate(double delta) {
return Radius.elliptical(x - delta, y - delta);
}
}
// *****************************************************************************
// Vladimir version. Twitter: @romashkin_dev
// https://twitter.com/romashkin_dev/status/1432027676055908353?s=20
class VladimirVersion extends StatelessWidget {
const VladimirVersion({
Key? key,
required this.width,
required this.height,
required this.borderWidth,
required this.borderRadius,
this.child,
}) : super(key: key);
final double width;
final double height;
final double borderWidth;
final double borderRadius;
final Widget? child;
@override
Widget build(BuildContext context) {
return Container(
width: width - borderWidth,
height: height - 2 * borderWidth,
decoration: BoxDecoration(
color: Colors.blue[100],
boxShadow: [
BoxShadow(
color: Colors.black,
offset: Offset(borderWidth, 0),
blurRadius: 0,
spreadRadius: 0,
),
BoxShadow(
color: Colors.black,
offset: Offset(0, borderWidth),
blurRadius: 0,
spreadRadius: 0,
),
BoxShadow(
color: Colors.black,
offset: Offset(0, -borderWidth),
blurRadius: 0,
spreadRadius: 0,
),
BoxShadow(
color: Colors.black,
offset: Offset(borderWidth, -borderWidth),
blurRadius: 0,
spreadRadius: 0,
),
BoxShadow(
color: Colors.black,
offset: Offset(borderWidth, borderWidth),
blurRadius: 0,
spreadRadius: 0,
),
],
borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
),
child: child,
);
}
}
@rydmike
Copy link
Author

rydmike commented Aug 30, 2021

Responses to Flutter Rounded Border Challenge

In this Tweet: https://twitter.com/RydMike/status/1431999590547476483 a question to draw the shape shown in Flutter was raised.

image

This public Gist contains a collection of received solution.

They all have their own pros and cons. Which one do you like best?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment