Negative Margin in Flutter UI

How to achieve similar behavior as in CSS when margins are negative? Is it possible? How to do it in Flutter UI?

Matatias Situmorang
Easyread

--

Flutter card dashboard

About 2 years ago, I created a sample card widget for a web dashboard with React JS. I was inspired by the source code on Codepen. I want to recreate it with Flutter code. Since it’s made for web UI, the style uses CSS code. In original source, the creator uses a negative margin for the icon box.

original card
 .card
margin: 0 -8px 32px -8px
color: rgba(0,0,0,.87)
box-shadow: 2px 2px 8px rgba(0,0,0,.05)

I’ve recreated a similar card according to my dashboard needs with React JS. I use an absolute position and a negative value in the top property. Look at the icons stacked on top of the cards.

my custom card in reactjs
boxicon: {
position: "absolute",
zIndex: 1,
top: -20,
left: 12,

This is a simple card widget that is quite often found in the dashboard web view. From the first time I uploaded in CodeSandbox, now it has 4.3k views and 49 times forked (last updated on Apr 17th, 2023) 😄. You can find it here: (React) responsive styled Card with material UI — CodeSandbox

How about in Flutter?

In Flutter, we can use margin on some of the widgets. For example, Container, Card, etc. And the margin property is implementing the EdgeInsetsGeometry class. This class is also used to set padding in Flutter.

example how we use padding and margin in Flutter:

Container(
padding: EdgeInsets.all(8),
margin: EdgeInsets.fromLRTB(4,8,4,2))

EdgeInsetsGeometry in Flutter, has an isNonNegative method. So….

Margin values must be positive, we cannot assign negative values to EdgeInsetsGeometry.

We will receive a failed assertion when we provide negative values to the margins or padding properties.

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Card(dirty, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#95d0d]]):
'package:flutter/src/widgets/container.dart': Failed assertion: line 267 pos 15: 'margin == null || margin.isNonNegative': is not true.

To create the card widget, we can make a similar behavior, or create a similar shape. Here are several options to achieve this.

  • Positioned widget | In CSS if you know about position: absolute. In Flutter we can achieve similar behavior with the Positioned widget.
  • Transform widget | This is similar to the transform property in CSS. It applies a transformation before the widget is rendered.
  • CustomPainter | I haven’t tried this, but I think it’s possible to create a custom shape like the card above with the CustomPainter class.

In this article, I will use the Transform class to crate card widgets like the image above. Since a negative margin can’t be applied, we will move the icon widget with matrix translation from the Transform class.

Matrix translation: A type of transformation that occurs when a figure is moved from one location to another on the coordinate plane without changing its size, shape or orientation… [ref]

with matrix translation, we can move widgets without changing anything except their position. We can prove it by comparing the position of the widget’s coordinates. If you wonder how to get the coordinate of a widget, you can try with extension method globalPaintBound. Read my article about it here:

Making cards

before translation
after translation

Before we apply the transformation widget, let's create the initial card. (See the picture before translation).

Now we want to place the icon staked above the card. We will use matrix translation. To add the translation, there are 2 options.

  • If the widget parent is not a Container widget, you can wrap them with Transfrom the widget. For example:
Transform(
transform: Matrix4.translationValues(x, y, z),
child: const SomeWidget(),
)
  • If it's a Container, then we can assign it directly to the transform property:
Container(
transform: Matrix4.translationValues(x, y, z),
.....// other props
)

After adding the translation value to the icon widget, now we have a stacked Icon on the card widget (See the picture after translation).

See the code card widget here: https://gist.github.com/pmatatias/6076a587a0dc5f505bca991f0ecda190

Additional: Neumorphism widget

We have done with the shape of the card widget. But if we compare the current result, we missed the shadow. To add the effect shadow we can add it manually to the BoxDecoration property from Container widget. We also can use an extension method named addNeumorphism . For more details, you can read my article explains more about it through this link:

  • Manual shadow for the icon box
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
offset: Offset(3, 3),
blurRadius: 8,
color: Color(0xFFffa726),
),
BoxShadow(
offset: Offset(-1, -1),
blurRadius: 10,
color: Colors.white,
),
....
  • Use the extension method neumorphism
    
@override
Widget build(BuildContext context) {
return Container(
..../// rest of code
).addNeumorphism(
bottomShadowColor: Colors.black26,
);

and now we have a better card with implemented shadow effect:

Finishing

The final step we need to do is to create a responsive layout on the card widget. I’ve also shared an article about responsive layouts in Flutter. For more details, you can read it here:

Cards will be displayed with the GridView.count() widget. Then we apply the responsive method to determine the crossAxisCount, and the childAspectRatiovalue.

...
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: GridView.count(
crossAxisSpacing: 20,
// mainAxisSpacing: 20,
childAspectRatio: size.width /
size.height /
context.responsive(0.3,
md: 0.6,
lg: 0.59,
xl: 1.2), // this ratio is hard code, you may need to change it base on your need
crossAxisCount: context.responsive(1, md: 2, xl: 4),
children: widge,
),
),
...

and finally, we have a card widget with stacked icons written in Flutter code.

card widget in Flutter

Full code and demo can be found on Dartpad:

https://dartpad.dev/?id=be8f84a08f9e2347f20be108ac030484

Fork and star the gist repository on GitHub through the link below:😎😁 dashboard_card_responsive_flutter.dart (github.com)

🙏Thank you for reading. Clap 👏 if you like this article 😃 😄

Reference:

--

--