Here is what I am trying to do:

The screenshot is taken from a iPhone 15 Pro:

I’m trying to build a pricing UI in Flutter, but the layout is not matching the design—specifically the top-right badge (“Starter Plan”).
class _PricingScreenState extends State {
final PageController _controller = PageController();
int currentPage = 0;
final List plans = [
PlanModel(
title: "Basic Plan",
price: "\$13 USD",
subtitle: "per user/month",
tag: "Starter Plan",
buttonText: "Get Basic - \$13/month",
features: [
"",
"",
"",
"",
"",
"",
"",
"",
"",
],
isPremium: false,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
currentPage == 2 ? "Customize Plan" : "Choose Your Plan",
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.w800,
),
),
],
),
),
// PAGES
Expanded(
child: PageView.builder(
controller: _controller,
itemCount: plans.length,
onPageChanged: (index) {
setState(() => currentPage = index);
},
itemBuilder: (context, index) {
return _PlanCard(plan: plans[index]);
},
),
),
],
),
),
);
}
}
class _PlanCard extends StatelessWidget {
final PlanModel plan;
const _PlanCard({required this.plan});
@override
Widget build(BuildContext context) {
final isPremium = plan.isPremium;
final isCustom = plan.isCustom;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Expanded(
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width: double.infinity,
decoration: BoxDecoration(
color: isPremium
? const Color(0xFF4A90E2)
: const Color(0xFFF5F5F7),
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.fromLTRB(22, 28, 22, 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
plan.title,
style: TextStyle(
color: isPremium ? Colors.white : Colors.black,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 10),
if (!isCustom)
Text(
plan.price,
style: TextStyle(
fontSize: 34,
fontWeight: FontWeight.bold,
color:
isPremium ? Colors.white : Colors.black,
),
),
const SizedBox(height: 5),
Text(
plan.subtitle,
style: TextStyle(
color: isPremium
? Colors.white70
: Colors.grey,
),
),
const SizedBox(height: 20),
if (!isCustom)
const Divider(),
const SizedBox(height: 10),
Expanded(
child: ListView.builder(
itemCount: plan.features.length,
itemBuilder: (_, i) => _FeatureRow(
text: plan.features[i],
isPremium: isPremium,
isCustom: isCustom,
),
),
),
],
),
),
// TAG BADGE
Positioned(
top: -12,
right: 16,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: Text(plan.tag),
),
)
],
),
),
],
),
);
}
}
class _FeatureRow extends StatelessWidget {
final String text;
final bool isPremium;
final bool isCustom;
const _FeatureRow({
required this.text,
this.isPremium = false,
this.isCustom = false,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
CircleAvatar(
radius: 10,
backgroundColor:
isPremium || isCustom ? Colors.yellow : Colors.green,
child: Icon(
Icons.check,
size: 12,
color: Colors.black,
),
),
const SizedBox(width: 10),
Expanded(
child: Text(
text,
style: TextStyle(
color: isPremium ? Colors.white : Colors.black,
),
),
),
],
),
);
}
}
How can I make the badge look attached to the card corner instead of floating?
Should I use ClipPath / CustomPainter for this?
What is the best approach to achieve this kind of connected UI shape in Flutter?
