I have been experimenting with flutter for a while and I stumbled a lot of times making custom widgets from designs for apps. Though flutter is incredibly easy to make UI components, you have to go through a lot of experimental process to get what we want. In this blog I am gonna simplify and show how make a custom dialog by break down every part of it. So lets get started!

Our goal of this journey will look like this,

It looks so simple, but it gets tricky when we try to make the circle peek out the card. The obvious widgets used in the image are,

Text for Heading and Description

for Heading and Description RaisedButton for Button

for Button CircularAvatar for the circle in the top.

Let’s start by building our custom dialog,

class CustomDialog extends StatelessWidget {

final String title, description, buttonText;

final Image image;



CustomDialog({

@required this.title,

@required this.description,

@required this.buttonText,

this.image,

});



@override

Widget build(BuildContext context) {

return Dialog(

shape: RoundedRectangleBorder(

borderRadius: BorderRadius.circular(Consts.padding),

),

elevation: 0.0,

backgroundColor: Colors.transparent,

child: dialogContent(context),

);

}

}

We are setting all the attributes of dialog and heading to dialogContent() which contains all our important widgets.

Dialog by default comes with its own background color and elevation. Since we wont be using it , we are setting backgroundColor to Colors.transparent and elevation to 0.0 .

dialogContent(BuildContext context) {

return Stack(

children: <Widget>[

//...bottom card part,

//...top circlular image part,

],

);

}

Since the stack shows last element on the top (duh…) we have circular at the end to overlap on the card. Let’s dig deep into each of the children.

Child — Card:

Container(

padding: EdgeInsets.only(

top: Consts.avatarRadius + Consts.padding,

bottom: Consts.padding,

left: Consts.padding,

right: Consts.padding,

),

margin: EdgeInsets.only(top: Consts.avatarRadius),

decoration: new BoxDecoration(

color: Colors.white,

shape: BoxShape.rectangle,

borderRadius: BorderRadius.circular(Consts.padding),

boxShadow: [

BoxShadow(

color: Colors.black26,

blurRadius: 10.0,

offset: const Offset(0.0, 10.0),

),

],

),

child: Column(

mainAxisSize: MainAxisSize.min, // To make the card compact

children: <Widget>[

Text(

title,

style: TextStyle(

fontSize: 24.0,

fontWeight: FontWeight.w700,

),

),

SizedBox(height: 16.0),

Text(

description,

textAlign: TextAlign.center,

style: TextStyle(

fontSize: 16.0,

),

),

SizedBox(height: 24.0),

Align(

alignment: Alignment.bottomRight,

child: FlatButton(

onPressed: () {

Navigator.of(context).pop(); // To close the dialog

},

child: Text(buttonText),

),

),

],

),

),

We are using a Container and BoxDecoration for making the card and since the half of circular avatar should be on the card, we add padding and margin accordingly with the attribute Consts.avatarRadius .

Child — Circular Image:

Positioned(

left: Consts.padding,

right: Consts.padding,

child: CircleAvatar(

backgroundColor: Colors.blueAccent,

radius: Consts.avatarRadius,

),

),

Here we use Positioned widget as a child in Stack to wrap the CircularAvatar as suggested by flutter dev team. Also the left and right attributes are set to same to same values to place in the center of the Stack.

You might me wondering what is that Consts ? It is just a class to store the few constants as shown down here.

class Consts {

Consts._();



static const double padding = 16.0;

static const double avatarRadius = 66.0;

}

Our journey has come to an end.

Right?

Not yet, I will finally demonstrate the usage of this is CustomDialog,

showDialog(

context: context,

builder: (BuildContext context) => CustomDialog(

title: "Success",

description:

"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",

buttonText: "Okay",

),

);

You can call this inside the onPressed: attribute of widgets like button and you get this…

Our journey ends here now.

See y’all!