Last active
July 22, 2024 09:38
-
-
Save flutter-clutter/66f5978d5056daa8265bc903db8c6c30 to your computer and use it in GitHub Desktop.
A flutter widget that gives the given child a configurable gradient border (used and explained on https://www.flutterclutter.dev/flutter/tutorials/how-to-add-a-border-to-a-widget/2020/587/)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
class GradientBorderContainer extends StatelessWidget { | |
GradientBorderContainer({ | |
@required gradient, | |
@required this.child, | |
@required this.onPressed, | |
this.strokeWidth = 4, | |
this.borderRadius = 64, | |
this.padding = 16, | |
this.outlineWidth = 1, | |
splashColor, | |
}) : | |
this.painter = _GradientPainter( | |
gradient: gradient, strokeWidth: strokeWidth, borderRadius: borderRadius, outlineWidth: outlineWidth | |
), | |
this.splashColor = splashColor ?? gradient.colors.first; | |
final _GradientPainter painter; | |
final Widget child; | |
final VoidCallback onPressed; | |
final double outlineWidth; | |
final double strokeWidth; | |
final double borderRadius; | |
final double padding; | |
final Color splashColor; | |
@override | |
Widget build(BuildContext context) { | |
return CustomPaint( | |
painter: painter, | |
child: InkWell( | |
highlightColor: Colors.transparent, | |
splashColor: splashColor, | |
borderRadius: BorderRadius.circular(borderRadius), | |
onTap: onPressed, | |
child: Container( | |
padding: EdgeInsets.all(padding + strokeWidth + outlineWidth), | |
child: IntrinsicWidth( | |
child: child | |
) | |
), | |
), | |
); | |
} | |
} | |
class _GradientPainter extends CustomPainter { | |
_GradientPainter({this.gradient, this.strokeWidth, this.borderRadius, this.outlineWidth}); | |
final Gradient gradient; | |
final double strokeWidth; | |
final double borderRadius; | |
final double outlineWidth; | |
final Paint paintObject = Paint(); | |
@override | |
void paint(Canvas canvas, Size size) { | |
if (outlineWidth > 0) { | |
_paintOutline(outlineWidth, size, canvas); | |
} | |
Rect innerRect = Rect.fromLTRB( | |
strokeWidth, strokeWidth, size.width - strokeWidth, size.height - strokeWidth | |
); | |
RRect innerRoundedRect = RRect.fromRectAndRadius(innerRect, Radius.circular(borderRadius)); | |
Rect outerRect = Offset.zero & size; | |
RRect outerRoundedRect = RRect.fromRectAndRadius(outerRect, Radius.circular(borderRadius)); | |
paintObject.shader = gradient.createShader(outerRect); | |
Path borderPath = _calculateBorderPath(outerRoundedRect, innerRoundedRect); | |
canvas.drawPath(borderPath, paintObject); | |
} | |
void _paintOutline(double outlineWidth, Size size, Canvas canvas) { | |
Paint paint = Paint(); | |
Rect innerRectB = Rect.fromLTRB( | |
strokeWidth + outlineWidth, | |
strokeWidth + outlineWidth, | |
size.width - strokeWidth - outlineWidth, | |
size.height - strokeWidth - outlineWidth | |
); | |
RRect innerRRectB = RRect.fromRectAndRadius(innerRectB, Radius.circular(borderRadius - outlineWidth)); | |
Rect outerRectB = Rect.fromLTRB(-outlineWidth, -outlineWidth, size.width + outlineWidth, size.height + outlineWidth); | |
RRect outerRRectB = RRect.fromRectAndRadius(outerRectB, Radius.circular(borderRadius + outlineWidth)); | |
Path borderBorderPath = _calculateBorderPath(outerRRectB, innerRRectB); | |
paint.color = Colors.black; | |
canvas.drawPath(borderBorderPath, paint); | |
} | |
Path _calculateBorderPath(RRect outerRRect, RRect innerRRect) { | |
Path outerRectPath = Path()..addRRect(outerRRect); | |
Path innerRectPath = Path()..addRRect(innerRRect); | |
return Path.combine(PathOperation.difference, outerRectPath, innerRectPath); | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) => true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PartialBorderContainer extends StatelessWidget { | |
PartialBorderContainer({ | |
@required gradient, | |
@required this.child, | |
@required this.onPressed, | |
this.strokeWidth = 4, | |
this.padding = 16, | |
splashColor, | |
}) : | |
this.painter = PartialPainter( | |
strokeWidth: strokeWidth, gradient: gradient | |
), | |
this.splashColor = splashColor ?? gradient.colors.first; | |
final PartialPainter painter; | |
final Widget child; | |
final VoidCallback onPressed; | |
final double strokeWidth; | |
final double padding; | |
final Color splashColor; | |
@override | |
Widget build(BuildContext context) { | |
return CustomPaint( | |
painter: painter, | |
child: InkWell( | |
highlightColor: Colors.transparent, | |
splashColor: splashColor, | |
onTap: onPressed, | |
child: Container( | |
padding: EdgeInsets.all(padding + strokeWidth), | |
child: IntrinsicWidth( | |
child: child | |
) | |
), | |
), | |
); | |
} | |
} | |
class PartialPainter extends CustomPainter { | |
PartialPainter({this.strokeWidth, this.gradient}); | |
final Paint paintObject = Paint(); | |
final double strokeWidth; | |
final Gradient gradient; | |
@override | |
void paint(Canvas canvas, Size size) { | |
Rect topLeftTop = Rect.fromLTRB(0, 0, size.height / 4, strokeWidth); | |
Rect topLeftLeft = Rect.fromLTRB(0, 0, strokeWidth, size.height / 4); | |
Rect bottomRightBottom = Rect.fromLTRB(size.width - size.height / 4, size.height - strokeWidth, size.width, size.height); | |
Rect bottomRightRight = Rect.fromLTRB(size.width - strokeWidth, size.height * 3 / 4, size.width, size.height); | |
paintObject.shader = gradient.createShader(Offset.zero & size); | |
Path topLeftPath = Path() | |
..addRect(topLeftTop) | |
..addRect(topLeftLeft); | |
Path bottomRightPath = Path() | |
..addRect(bottomRightBottom) | |
..addRect(bottomRightRight); | |
Path finalPath = Path.combine(PathOperation.union, topLeftPath, bottomRightPath); | |
canvas.drawPath(finalPath, paintObject); | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) => true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'gradient_border_container.dart'; | |
import 'partial_border_container.dart'; | |
class Borders extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: SafeArea( | |
child: Center( | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
children: <Widget>[ | |
SizedBox(width: 0, height: 16), | |
_getButtonWithGradientRect(), | |
SizedBox(width: 0, height: 16), | |
_getButtonWithGradientRRect(), | |
SizedBox(width: 0, height: 16), | |
_getDangerButton(), | |
SizedBox(width: 0, height: 16), | |
_getDangerButton2(), | |
SizedBox(width: 0, height: 16), | |
_getButtonWithPartial() | |
], | |
), | |
) | |
), | |
); | |
} | |
GradientBorderButtonFinal _getButtonWithGradientRect() { | |
return GradientBorderButtonFinal( | |
strokeWidth: 4, | |
radius: 0, | |
padding: 20, | |
gradient: | |
LinearGradient(colors: [Colors.redAccent, Colors.orangeAccent]), | |
child: Text('GRADIENT', style: TextStyle(fontSize: 32)), | |
onPressed: () {}, | |
); | |
} | |
GradientBorderButtonFinal _getButtonWithGradientRRect() { | |
return GradientBorderButtonFinal( | |
strokeWidth: 4, | |
radius: 54, | |
padding: 20, | |
gradient: | |
LinearGradient(colors: [Colors.green, Colors.blue]), | |
child: Text('ROUNDED', style: TextStyle(fontSize: 32)), | |
onPressed: () {}, | |
); | |
} | |
GradientBorderButtonFinal _getDangerButton() { | |
return GradientBorderButtonFinal( | |
strokeWidth: 16, | |
radius: 16, | |
gradient: LinearGradient( | |
begin: Alignment.topLeft, | |
end: Alignment(-0.2, -0.4), | |
stops: [0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1], | |
colors: [ | |
Colors.yellow, | |
Colors.yellow, | |
Colors.black, | |
Colors.black, | |
Colors.yellow, | |
Colors.yellow, | |
Colors.black, | |
Colors.black, | |
], | |
tileMode: TileMode.repeated, | |
), | |
child: Text('DANGER!', | |
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold)), | |
onPressed: () {}, | |
); | |
} | |
GradientBorderButtonFinal _getDangerButton2() { | |
return GradientBorderButtonFinal( | |
strokeWidth: 16, | |
radius: 16, | |
outlineWidth: 1, | |
gradient: LinearGradient( | |
begin: Alignment.topLeft, | |
end: Alignment(-0.2, -0.4), | |
stops: [0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1], | |
colors: [ | |
Colors.pinkAccent, | |
Colors.pinkAccent, | |
Colors.white, | |
Colors.white, | |
Colors.pinkAccent, | |
Colors.pinkAccent, | |
Colors.white, | |
Colors.white, | |
], | |
tileMode: TileMode.repeated, | |
), | |
child: Text('TRAIN CROSSING!', | |
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold)), | |
onPressed: () {}, | |
); | |
} | |
Widget _getButtonWithPartial() { | |
return PartialBorderContainer( | |
strokeWidth: 4, | |
padding: 20, | |
gradient: | |
LinearGradient(colors: [Colors.green, Colors.blue]), | |
child: Text('PARTIAL BORDER', style: TextStyle(fontSize: 32)), | |
onPressed: () {}, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment