1

I’m new to Flutter and trying to replicate a design that has curved shapes with categories displayed along the curve See image here.

I’ve tried using ClipPath, but I haven’t been able to get the bottom arc to match the top one. Also, the category icons don’t naturally follow the curve of the container — they’re still aligned horizontally. I experimented with Stack, but the positioning is still off.

Below is everything I’ve tried so far.

import 'package:flutter/material.dart';
import 'package:starbucks_project/components/category_tile.dart';
import 'package:starbucks_project/data/category.dart';
import 'package:starbucks_project/themes/colors.dart';
import 'package:google_fonts/google_fonts.dart';

class DoubleCurveClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    // TODO: implement getClip

    var path = Path();
    //   top curve
    path.moveTo(0, 50);
    path.quadraticBezierTo(
      // control point (upward)
      size.width / 2,
      -50,
      // endpoint
      size.width,
      50,
    );
    //   right side
    path.lineTo(size.width, size.height - 50);
    //   bottom curve
    path.quadraticBezierTo(
      // control point (upward)
      size.width / 2,
      -10,
      // endpoint
      0,
      size.height - 50,
    );
    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

class Intro extends StatefulWidget {
  const Intro({super.key});

  @override
  State<Intro> createState() => _IntroState();
}

class _IntroState extends State<Intro> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      // appbar
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        title: Padding(
          padding: const EdgeInsets.all(25),
          child: Image.asset('lib/images/icons/logo.png'),
        ),
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 25),
            child: IconButton(
              onPressed: () {},
              icon: Icon(
                Icons.shopping_bag_outlined,
                color: iconColor,
                size: 32,
              ),
            ),
          ),
        ],
      ),
      body: Container(
        width: double.infinity,
        height: double.infinity,
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage('lib/images/icons/background.png'),
            fit: BoxFit.contain,
            alignment: Alignment(0, -0.8),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // text
            SizedBox(height: 32),
            Container(
              width: 280,

              child: Padding(
                padding: const EdgeInsets.only(left: 48.0),
                child: Text(
                  'Enjoy coffee any time',
                  style: GoogleFonts.sen(
                    fontWeight: FontWeight.bold,
                    fontSize: 36,
                  ),
                ),
              ),
            ),
            SizedBox(height: 38),
            ClipPath(
              clipper: DoubleCurveClipper(),
              child: Container(
                decoration: BoxDecoration(color: primaryColor),

                height: 280,
                // child: ListView.builder(
                //   scrollDirection: Axis.horizontal,
                //   itemCount: allCategories.length,
                //   itemBuilder: (context, index) {
                //     final category = allCategories[index];
                //     return CategoryTile(category: category, onTap: () {});
                //   },
                // ),
                child: Stack(
                  children: List.generate(allCategories.length, (index) {
                    double width = MediaQuery.of(context).size.width;
                    double spacing =
                        width / allCategories.length; // space each tile evenly
                    double x = spacing * index;

                    // This is the "y along the curve" (simplified)
                    double y = 50 - 50 * (4 * (x / width) * (1 - x / width));

                    return Positioned(
                      left: x,
                      top: y,
                      child: CategoryTile(
                        category: allCategories[index],
                        onTap: () {},
                      ),
                    );
                  }),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.