flutterborder-layout

How to make container borders as in image in Flutter?


Figma Web Site Design

I want to make this border and I have tried all solutions on stackoverflow.

in this solutions when i add borderRadius or when i gave border transparent color to top border, Borders do not displays.

Some other solutions are using Paint widget as that:

and this is now my code:

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

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final description =
      'Lorem ipsum dolor sit amet consectetur. Sed auctor dignissim sit egestas dolor turpis. Nunc congue vulputate tincidunt purus. Non lacus metus tortor elit mauris proin.';

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return SingleChildScrollView(
      child: Column(
        children: [
          FittedBox(
              child: topSection(header: headerWidget(), body: bodyWidget())),
          const SizedBox(height: 20),
          //midSection(),
          CustomTitleWidget(
              height: 200,
              width: double.infinity,
              title: 'Some of my Projects',
              radius: 20,
          ),
        ],
      ),
    );
  }


}

And those components for drawing my bordered container and its not working like i wanted: (OUTPUT image is under)

class CustomDraw extends CustomPainter {
  late Paint painter;
  late double radius;
  late double textWidth;

  CustomDraw(Color color, this.textWidth, {this.radius = 0}) {
    painter = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2
      ..color = color;
  }

  @override
  void paint(Canvas canvas, Size size) {
    var path = Path();

    path.moveTo(size.width - ((size.width - textWidth) / 2), 0);

    path.lineTo(size.width - radius, 0);
    path.cubicTo(size.width - radius, 0, size.width, 0, size.width, radius);
    path.lineTo(size.width, size.height - radius);
    path.cubicTo(size.width, size.height - radius, size.width, size.height,
        size.width - radius, size.height);

    path.lineTo(radius, size.height);
    path.cubicTo(radius, size.height, 0, size.height, 0, size.height - radius);

    path.lineTo(0, radius);
    path.cubicTo(0, radius, 0, 0, radius, 0);
    path.lineTo(((size.width - textWidth) / 2), 0);

    canvas.drawPath(path, painter);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class CustomTitleWidget extends StatefulWidget {
  final double height;
  final double width;
  final double? radius;
  final String title;
  const CustomTitleWidget(
      {Key? key,
        required this.height,
        required this.width,
        required this.title,
        this.radius})
      : super(key: key);

  @override
  State<CustomTitleWidget> createState() => _CustomTitleWidgetState();
}

class _CustomTitleWidgetState extends State<CustomTitleWidget> {
  GlobalKey textKey = GlobalKey();
  double textHeight = 0.0;
  double textWidth = 0.0;

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        final textKeyContext = textKey.currentContext;
        if (textKeyContext != null) {
          final box = textKeyContext.findRenderObject() as RenderBox;
          textHeight = box.size.height;
          textWidth = box.size.width;
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      clipBehavior: Clip.none,
      alignment: Alignment.topCenter,
      children: [
        CustomPaint(
          painter: CustomDraw(
            Colors.black,
            textWidth,
            radius: widget.radius ?? 0,
          ),
          child: SizedBox(
            height: widget.height,
            width: widget.width,
          ),
        ),
        Positioned(
          top: -textHeight / 2,
          left: 60,
          child: Padding(
            key: textKey,
            padding: EdgeInsets.symmetric(horizontal: 8.0),
            child: Container(
              color: Colors.transparent,
              padding: const EdgeInsets.symmetric(horizontal: 10),
              child: Text(
                widget.title,
                style: AppTextStyles.nameTitle,
              ),
            ),
          ),
        )
      ],
    );
  }
}

OUTPUT image


Solution

  • https://youtu.be/3ZrKV8t5mWUg You can see last view of component

    i solve problem with this code:

    **If title is not center you can change Position widget top and bottom

    Stack(
                children: <Widget>[
                  Container(
                    width: double.infinity,
                    margin: const EdgeInsets.fromLTRB(0, 20, 20, 10),
                    padding: const EdgeInsets.all(30),
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.black, width: 2),
                      borderRadius: const BorderRadius.only(
                        topRight: Radius.circular(20),
                        bottomRight: Radius.circular(20),
                      ),
                      shape: BoxShape.rectangle,
                      color: AppColors.lightGrey4,
                    ),
                    child: SingleChildScrollView(
                      child: GridView.count(
                        crossAxisCount: 2,
                        shrinkWrap: true,
                        physics: NeverScrollableScrollPhysics(),
                        children: List.generate(6, (index) {
                          return Container(
                            margin: EdgeInsets.all(10),
                            color: Colors.blue,
                            child: Center(
                              child: Text(
                                'Item ${index + 1}',
                                style: TextStyle(color: Colors.white, fontSize: 16),
                              ),
                            ),
                          );
                        }),
                      ),
                    ),
                  ),
                  Positioned(
                    left: 50,
                    top: 0,
                    child: Container(
                      padding:
                          const EdgeInsets.only(bottom: 10, left: 10, right: 10),
                      decoration: const BoxDecoration(
                        gradient: LinearGradient(
                          colors: [AppColors.mainBackground, AppColors.lightGrey4],
                          begin: Alignment.topCenter,
                          end: Alignment.bottomCenter,
                          stops: [0.5, 0.5],
                        ),
                      ),
                      child: const Text(
                        'Some of My Projects',
                        style: AppTextStyles.nameTitle,
                      ),
                    ),
                  ),
                ],
              )