Skip to content

Instantly share code, notes, and snippets.

@rohan20
Last active December 27, 2021 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rohan20/b95177ad365c4c0e2f48625f27996d52 to your computer and use it in GitHub Desktop.
Save rohan20/b95177ad365c4c0e2f48625f27996d52 to your computer and use it in GitHub Desktop.
[Demo] Colored rounded border on some sides of a Flutter widget
// Initially posted here (with screenshots): https://gist.github.com/rohan20/d12a709a27511304c853334dd636ba63
// Demo: https://dartpad.dev/?id=b95177ad365c4c0e2f48625f27996d52
import 'package:flutter/material.dart';
void main() {
runApp(DemoApp());
}
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_ResetPickTeamChangesButton(),
_ApplyPickTeamChangesButton(),
],
),
),
);
}
}
/// Rounded border on some sides of a widget is tricky to achieve since Flutter's [BorderRadius] and [Border] don't
/// work together when the border is "not" on all sides.
///
/// The initial logic was found here: https://stackoverflow.com/a/61613471/5066615.
///
/// The way it's done here is to wrap the [child] in 2 [Container] widgets:
/// The outer [Container] has its background color set to [borderColor] i.e. what we want the border color to be.
/// The inner [Container] has its background color set to the background color of the widget behind the [child] so
/// that it appears to just have a border of [borderColor].
/// The inner [Container] also has a margin that is the same size as the [borderWidth].
class RoundedBorderOnSomeSidesWidget extends StatelessWidget {
/// Color of the content behind this widget
final Color contentBackgroundColor;
final Color borderColor;
final Widget child;
final double borderRadius;
final double borderWidth;
/// The sides where we want the rounded border to be
final bool topLeft;
final bool topRight;
final bool bottomLeft;
final bool bottomRight;
const RoundedBorderOnSomeSidesWidget({
required this.borderColor,
required this.contentBackgroundColor,
required this.child,
required this.borderRadius,
required this.borderWidth,
this.topLeft = false,
this.topRight = false,
this.bottomLeft = false,
this.bottomRight = false,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: borderColor,
borderRadius: BorderRadius.only(
topLeft: topLeft ? Radius.circular(borderRadius) : Radius.zero,
topRight: topRight ? Radius.circular(borderRadius) : Radius.zero,
bottomLeft: bottomLeft ? Radius.circular(borderRadius) : Radius.zero,
bottomRight:
bottomRight ? Radius.circular(borderRadius) : Radius.zero,
),
),
child: Container(
margin: EdgeInsets.only(
top: topLeft || topRight ? borderWidth : 0,
left: topLeft || bottomLeft ? borderWidth : 0,
bottom: bottomLeft || bottomRight ? borderWidth : 0,
right: topRight || bottomRight ? borderWidth : 0,
),
decoration: BoxDecoration(
color: contentBackgroundColor,
borderRadius: BorderRadius.only(
topLeft: topLeft
? Radius.circular(borderRadius - borderWidth)
: Radius.zero,
topRight: topRight
? Radius.circular(borderRadius - borderWidth)
: Radius.zero,
bottomLeft: bottomLeft
? Radius.circular(borderRadius - borderWidth)
: Radius.zero,
bottomRight: bottomRight
? Radius.circular(borderRadius - borderWidth)
: Radius.zero,
),
),
child: child,
),
);
}
}
const double _roundedButtonTextPadding = 12;
const double _roundedButtonBorderRadius = 8;
const double _borderWidth = 1;
class _EdgeOfThePageButton extends StatelessWidget {
final String text;
final VoidCallback? onTap;
/// The sides where we want the rounded border to be
final bool topLeft;
final bool topRight;
final bool bottomLeft;
final bool bottomRight;
const _EdgeOfThePageButton({
required this.text,
this.onTap,
this.topLeft = false,
this.topRight = false,
this.bottomLeft = false,
this.bottomRight = false,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: RoundedBorderOnSomeSidesWidget(
borderColor: Colors.white,
contentBackgroundColor: Colors.teal.shade800,
borderWidth: _borderWidth,
borderRadius: _roundedButtonBorderRadius,
topLeft: topLeft,
topRight: topRight,
bottomLeft: bottomLeft,
bottomRight: bottomRight,
child: Padding(
padding: const EdgeInsets.all(_roundedButtonTextPadding),
child: Text(
text,
style: const TextStyle(color: Colors.white, fontSize: 12),
textAlign: TextAlign.center,
),
),
),
);
}
}
class _ResetPickTeamChangesButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _EdgeOfThePageButton(
text: "Reset\nChanges",
onTap: () => print("Reset Changes tapped"),
bottomRight: true,
);
}
}
class _ApplyPickTeamChangesButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _EdgeOfThePageButton(
text: "Apply\nChanges",
onTap: () => print("Apply Changes tapped"),
bottomLeft: true,
);
}
}
@rohan20
Copy link
Author

rohan20 commented Dec 27, 2021

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