Here’s what I’m making an attempt to do:

The screenshot is taken from a iPhone 15 Professional:

I’m making an attempt to construct a pricing UI in Flutter, however the structure isn’t matching the design—particularly the top-right badge (“Starter Plan”).
class _PricingScreenState extends State<PricingScreen> {
closing PageController _controller = PageController();
int currentPage = 0;
closing Record<PlanModel> 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 construct(BuildContext context) {
return Scaffold(
backgroundColor: Colours.white,
physique: SafeArea(
youngster: Column(
kids: [
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(
youngster: PageView.builder(
controller: _controller,
itemCount: plans.size,
onPageChanged: (index) {
setState(() => currentPage = index);
},
itemBuilder: (context, index) {
return _PlanCard(plan: plans[index]);
},
),
),
],
),
),
);
}
}
class _PlanCard extends StatelessWidget {
closing PlanModel plan;
const _PlanCard({required this.plan});
@override
Widget construct(BuildContext context) {
closing isPremium = plan.isPremium;
closing isCustom = plan.isCustom;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
youngster: Column(
kids: [
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(
high: -12,
proper: 16,
youngster: Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 6),
ornament: BoxDecoration(
coloration: Colours.white,
borderRadius: BorderRadius.round(30),
),
youngster: Textual content(plan.tag),
),
)
],
),
),
],
),
);
}
}
class _FeatureRow extends StatelessWidget {
closing String textual content;
closing bool isPremium;
closing bool isCustom;
const _FeatureRow({
required this.textual content,
this.isPremium = false,
this.isCustom = false,
});
@override
Widget construct(BuildContext context) 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 connected to the cardboard nook as a substitute of floating?
Ought to I take advantage of ClipPath / CustomPainter for this?
What’s the finest strategy to attain this type of linked UI form in Flutter?

