-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Description
By default, Flutter squares the inner border of RoundedRectangleBorder (and BoxDecoration) on larger values. I don't think we can change that because it would be a bad breaking change. But we could add a property that improves it.
ORRR, we could have a breaking change. What if I made a PR and we checked how many goldens it breaks at Google? 🌚 Maybe there are not many people impacted because larger box is not that common. We could still have the old behavior in BoxDecoration that does some things differently.
Figma doesn't do this, and with the new StrokeAlign, I think things would be more consistent with this feel:

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(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: const Scaffold(body: MyHomePage()),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
List<Widget> spaceRow(double gap, Iterable<Widget> children) => children
.expand((item) sync* {
yield SizedBox(width: gap);
yield item;
})
.skip(1)
.toList();
List<Widget> spaceColumn(double gap, Iterable<Widget> children) => children
.expand((item) sync* {
yield SizedBox(height: gap);
yield item;
})
.skip(1)
.toList();
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
const double width = 40;
return ListView(
padding: const EdgeInsets.all(50),
children: [
const Text(
"Container",
textAlign: TextAlign.center,
),
const SizedBox(height: 40),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: spaceRow(
width * 2,
[
for (StrokeAlign align in StrokeAlign.values)
Container(
width: 100,
height: 100,
decoration: ShapeDecoration(
color: Colors.green,
shape: RoundedRectangleBorder(
isStrokeInsideRound: false,
side: BorderSide(
color: Colors.red.withOpacity(0.5),
width: 20,
strokeAlign: align,
),
borderRadius: BorderRadius.circular(20),
),
),
),
],
),
),
const SizedBox(height: 40),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: spaceRow(
width * 2,
[
for (StrokeAlign align in StrokeAlign.values)
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(0),
border: Border.all(
color: Colors.black.withOpacity(0.75),
width: width,
strokeAlign: align,
),
),
// child: Center(
// child: Container(
// width: 100, height: 100, color: Colors.blue)),
),
],
),
),
const SizedBox(height: width * 2),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: spaceRow(
width * 2,
[
for (StrokeAlign align in StrokeAlign.values)
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.green,
border: Border.all(
color: Colors.black.withOpacity(0.75),
width: 20,
strokeAlign: align,
),
borderRadius: BorderRadius.circular(8)),
),
],
),
),
const SizedBox(height: width * 2),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: spaceRow(
width * 2,
[
for (StrokeAlign align in StrokeAlign.values)
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
border: Border.all(
color: Colors.black.withOpacity(0.75),
width: width,
strokeAlign: align,
),
),
),
],
),
),
],
);
}
}Proposed fix:
Add isStrokeInsideRound to RoundedRectangleBorder and update paint to the following:
@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 Paint paint = Paint()
..color = side.color;
if (side.strokeAlign == StrokeAlign.inside && !isStrokeInsideRound) {
final RRect outer = borderRadius.resolve(textDirection).toRRect(rect);
final RRect inner = outer.deflate(width);
canvas.drawDRRect(outer, inner, paint);
} else {
final Rect inner;
final Rect outer;
switch (side.strokeAlign) {
case StrokeAlign.inside:
inner = rect.deflate(width);
outer = rect;
break;
case StrokeAlign.center:
inner = rect.deflate(width / 2);
outer = rect.inflate(width / 2);
break;
case StrokeAlign.outside:
inner = rect;
outer = rect.inflate(width);
break;
}
final BorderRadius borderRadiusResolved = borderRadius.resolve(textDirection);
canvas.drawDRRect(borderRadiusResolved.toRRect(outer), borderRadiusResolved.toRRect(inner), paint);
}
}
}
}Challenge: lerp is kind of impossible between two booleans.
