How to insert custom clip-type containers in Flutter

Viewed 35

I want to insert some Containers in my Flutter App. Can someone tell me, how can I make the 1,2 numbering as done in containers in the image? If there is a widget/package, please share its link with me.

enter image description here

1 Answers

The following code should do the trick. It uses a CustomPaint widget to draw the clip.

This is the result (Also, the live demo on DartPad):

Screenshot

import 'dart:math';

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(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color.fromARGB(255, 245, 245, 245),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const [
            Padding(
              padding: EdgeInsets.fromLTRB(30.0, 30.0, 30.0, 0),
              child: ClipCard(
                clipText: '1',
                title: 'Refer',
                description:
                    'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla risus sapien, luctus id dapibus eu, eleifend in turpis.',
              ),
            ),
            Padding(
              padding: EdgeInsets.fromLTRB(30.0, 15, 30.0, 0),
              child: ClipCard(
                clipText: '2',
                title: 'Earn',
                description:
                    'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla risus sapien, luctus id dapibus eu, eleifend in turpis.',
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class ClipCard extends StatelessWidget {
  final String clipText;
  final String title;
  final String description;
  const ClipCard({
    Key? key,
    required this.clipText,
    required this.title,
    required this.description,
  }) : super(key: key);

  static const clipSize = 45.0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          width: double.infinity,
          height: 170,
          color: Colors.white,
        ),
        Clip(
          size: clipSize,
          text: clipText,
        ),
        Positioned(
          top: 28,
          left: clipSize,
          child: Text(
            title,
            style: const TextStyle(fontSize: 28, height: 1),
          ),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(28.0, 80.0, 28.0, 28.0),
          child: Text(
            description,
            style: const TextStyle(fontSize: 24),
            overflow: TextOverflow.clip,
          ),
        ),
      ],
    );
  }
}

class Clip extends StatelessWidget {
  final double size;
  final double topOffset;
  final String text;
  const Clip({
    Key? key,
    required this.size,
    this.topOffset = 20,
    required this.text,
  }) : super(key: key);

  static const clipArcRadius = 4.0;

  @override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(-clipArcRadius, topOffset),
      child: SizedBox(
        width: size,
        height: size,
        child: CustomPaint(
          painter: const ClipPainter(clipArcRadius: clipArcRadius),
          child: Center(
              child: Transform.translate(
            offset: const Offset(-clipArcRadius, -clipArcRadius / 2),
            child: Text(
              text,
              style: TextStyle(
                  color: Colors.white, fontSize: size * 0.6, height: 1),
            ),
          )),
        ),
      ),
    );
  }
}

class ClipPainter extends CustomPainter {
  final double clipArcRadius;

  const ClipPainter({this.clipArcRadius = 4});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = const Color.fromARGB(255, 79, 79, 79)
      ..style = PaintingStyle.fill;

    final path = Path()
      ..lineTo(0, size.height - 8)
      ..arcTo(
          Rect.fromLTWH(
            0,
            size.height - clipArcRadius * 2,
            clipArcRadius * 2,
            clipArcRadius * 2,
          ),
          pi,
          -pi / 2,
          false)
      ..lineTo(clipArcRadius, size.height - clipArcRadius)
      ..lineTo(size.width - 8, size.height - clipArcRadius)
      ..lineTo(size.width - 8, (size.height - clipArcRadius) / 2 - 6)
      ..lineTo(size.width, (size.height - clipArcRadius) / 2)
      ..lineTo(size.width - 8, (size.height - clipArcRadius) / 2 + 6)
      ..lineTo(size.width - 8, 0)
      ..close();

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}
Related