skip to Main Content

I need some help with building a user interface for my app. While browsing Dribbble, I came across a design that I think would be simple and fast to replicate. However, I’m struggling with creating a Container that has a unique borderRadius in Flutter.

Here’s a link to the design:
Dribble page design

I plan to add a black appBar with a leading and actions for the two icons and then create a Container in the body. However, I can’t seem to figure out how to achieve the non-standard borderRadius on the bottom right corner.

Can you please help me with this? Thank you in advance for your assistance.

4

Answers


  1. There are multiple ways to achieve this. One would be with a CustomClipper but I think the same effect can be obtained using a Stack and widgets for the corners with a simple border radius.

    https://dartpad.dev/?id=51c04550077960bdad72c0b3bf5063c9

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          debugShowCheckedModeBanner: false,
          home: TestPage(),
        );
      }
    }
    
    class TestPage extends StatelessWidget {
      const TestPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        const borderRadius = 48.0;
        return Scaffold(
          backgroundColor: Colors.grey,
          body: Column(
            children: [
              Stack(
                clipBehavior: Clip.none,
                children: [
                  Positioned(
                    bottom: -borderRadius,
                    left: 0,
                    child: Container(
                      height: borderRadius * 2,
                      width: borderRadius * 2,
                      color: Colors.white,
                    ),
                  ),
                  Positioned(
                    bottom: -borderRadius,
                    right: 0,
                    child: Container(
                      height: borderRadius * 2,
                      width: borderRadius * 2,
                      color: Colors.black,
                    ),
                  ),
                  Container(
                    height: 200,
                    decoration: BoxDecoration(
                      color: Colors.black,
                      borderRadius: BorderRadius.circular(borderRadius),
                    ),
                  ),
                ],
              ),
              Expanded(
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(borderRadius),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    Login or Signup to reply.
  2. I have achieved this similar feature while developing ‘message box’ in my chatting application. This is how I created curviness from one side of a container.

    Container(
          decoration: BoxDecoration(
            color: Colors.black, // I put black here for you
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(20),
              topRight: Radius.circular(20),
              bottomLeft: Radius.circular(20),
              bottomRight: Radius.circular(1),
            ),
          ),
          child: Column(
            children: [
              // Add your widgets here
            ],
          ),
        ),
    
    Login or Signup to reply.
  3. There are several approaches to achieve this. Here’s one way to do it:

    CustomClipper is a powerful tool in Flutter for creating shapes. Here in another file, You can define a CustomClipper class that extends CustomClipper<Path> to create a curved shape. Then, apply this CustomClipper to the AppBar to achieve the desired curved effect.

    AppBar:

    appBar: PreferredSize(
        preferredSize: Size.fromHeight(150),
        child: ClipPath(
          clipper: CurveAppBar(),
          child: AppBar(
            backgroundColor: Colors.black,
            leading: Icon(Icons.menu, color: Colors.white),
            actions: [
              Icon(Icons.person, color: Colors.white),
            ],
          ),
        ),
      ),
    

    CustomClipper file:

    import 'package:flutter/material.dart';
    
    class CurveAppBar extends CustomClipper<Path> {
      @override
      Path getClip(Size size) {
        final Path path = Path();
        path.moveTo(0, 0);
        path.lineTo(0, size.height - 50);
        path.quadraticBezierTo(
            size.width / 5, size.height / 2.5, size.width / 2, size.height - 50);//Adjust Your curves here
        path.quadraticBezierTo(
            size.width * 3 / 4, size.height, size.width, size.height - 50);//Adjust your curves here
        path.lineTo(size.width, 0);
        path.close();
        return path;
      }
    
      @override
      bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
        return false;
      }
    }
    

    Feel free to adjust the parameters of the CustomClipper to customize the curves according to your specific design.

    Login or Signup to reply.
  4. Implement a custom BoxBorder

    BoxBorder is used with the Flutter Container‘s BoxDecoration, so writing a custom implementation compliments this API, and lets you neatly control the edge inset dimensions so child Widgets are aligned properly, and overall a custom BoxBorder is flexible for which designs it supports, since it uses a canvas to draw the shape to the screen, only mathematics is the limit.

    class ChatBubbleBorder extends BoxBorder {
      final double borderWidth;
      final Radius radius;
    
      const ChatBubbleBorder(
          {this.borderWidth = 2, this.radius = const Radius.circular(16)});
    
      @override
      void paint(
        Canvas canvas,
        Rect rect, {
        TextDirection? textDirection,
        BoxShape shape = BoxShape.rectangle,
        BorderRadius? borderRadius,
      }) {
        var paint = Paint();
        paint.color = Colors.yellow;
        paint.style = PaintingStyle.fill;
    
        var modifiedRect =
            Rect.fromLTWH(rect.left, rect.top, rect.width, rect.height - radius.y);
    
        RRect rRect = RRect.fromRectAndCorners(
          modifiedRect,
          bottomLeft: radius,
          topLeft: radius,
          topRight: radius,
          bottomRight: Radius.zero,
        );
        canvas.drawRRect(rRect, paint);
    
        var path = Path();
        path.moveTo(rect.width - radius.x, rect.height - radius.y);
        path.quadraticBezierTo(
            rect.width, rect.height - radius.y, rect.width, rect.height);
        path.lineTo(rect.width, rect.height - radius.y);
        path.close();
        // In case of margin move the path.
        path = path.shift(rect.topLeft);
        canvas.drawPath(path, paint);
      }
    
      @override
      BorderSide get top => BorderSide(width: borderWidth);
    
      @override
      BorderSide get bottom => BorderSide(width: borderWidth + radius.y);
    
      @override
      EdgeInsetsGeometry get dimensions => EdgeInsets.fromLTRB(
            borderWidth,
            borderWidth,
            borderWidth,
            borderWidth + radius.y,
          );
    
      @override
      bool get isUniform => false;
    
      @override
      ShapeBorder scale(double t) => ChatBubbleBorder(
            borderWidth: t * borderWidth,
            radius: Radius.circular(t * radius.y),
          );
    }
    

    Usage and result

    Here a scroll view is passed as a child to the container using the custom ChatBubbleBorder, this shows that the content is rendered within the expected dimensions.

    import 'package:flutter/material.dart';
    
    main() => runApp(const MaterialApp(home: BorderTestScreen()));
    
    class BorderTestScreen extends StatelessWidget {
      const BorderTestScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            margin: const EdgeInsets.all(8),
            padding: const EdgeInsets.symmetric(horizontal: 8),
            decoration: const BoxDecoration(
              border: ChatBubbleBorder(),
            ),
            child: const SizedBox(
              width: 300,
              height: 100,
              child: SingleChildScrollView(
                child: Text(
                  "Testing1nTesting2nTesting3nTesting4nTesting5nTesting6",
                ),
              ),
            ),
          ),
        );
      }
    }
    

    Chat Bubble BoxBorder

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search