diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c
index 027221f1b..bec7ab068 100644
--- a/clutter/clutter-flow-layout.c
+++ b/clutter/clutter-flow-layout.c
@@ -69,6 +69,12 @@ struct _ClutterFlowLayoutPrivate
   gfloat max_row_height;
   gfloat row_height;
 
+  /* per-line size */
+  GArray *line_min;
+  GArray *line_natural;
+
+  guint line_count;
+
   guint is_homogeneous : 1;
 };
 
@@ -93,6 +99,63 @@ G_DEFINE_TYPE (ClutterFlowLayout,
                clutter_flow_layout,
                CLUTTER_TYPE_LAYOUT_MANAGER);
 
+static gint
+get_columns (ClutterFlowLayout *self,
+             gfloat             for_width)
+{
+  ClutterFlowLayoutPrivate *priv = self->priv;
+  gint n_columns;
+
+  if (for_width < 0)
+    return 1;
+
+  if (priv->col_width == 0)
+    return 1;
+
+  n_columns = (gint) (for_width + priv->col_spacing)
+            / (priv->col_width + priv->col_spacing);
+
+  if (n_columns == 0)
+    return 1;
+
+  return n_columns;
+}
+
+static gint
+get_rows (ClutterFlowLayout *self,
+          gfloat             for_height)
+{
+  ClutterFlowLayoutPrivate *priv = self->priv;
+  gint n_rows;
+
+  if (for_height < 0)
+    return 1;
+
+  if (priv->row_height == 0)
+    return 1;
+
+  n_rows = (gint) (for_height + priv->row_spacing)
+         / (priv->row_height + priv->row_spacing);
+
+  if (n_rows == 0)
+    return 1;
+
+  return n_rows;
+}
+
+static gint
+compute_lines (ClutterFlowLayout *self,
+               gfloat             avail_width,
+               gfloat             avail_height)
+{
+  ClutterFlowLayoutPrivate *priv = self->priv;
+
+  if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+    return get_columns (self, avail_width);
+  else
+    return get_rows (self, avail_height);
+}
+
 static void
 clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager,
                                          ClutterContainer     *container,
@@ -102,52 +165,141 @@ clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager,
 {
   ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
   GList *l, *children = clutter_container_get_children (container);
-  gfloat max_child_min_width, max_child_natural_width;
-  gfloat row_natural_width;
+  gint n_rows, line_item_count, line_count;
+  gfloat total_min_width, total_natural_width;
+  gfloat line_min_width, line_natural_width;
+  gfloat max_min_width, max_natural_width;
+  gfloat item_y;
 
-  max_child_min_width = max_child_natural_width = 0;
-  row_natural_width = 0;
+  n_rows = get_rows (CLUTTER_FLOW_LAYOUT (manager), for_height);
+
+  total_min_width = 0;
+  total_natural_width = 0;
+
+  line_min_width = 0;
+  line_natural_width = 0;
+
+  line_item_count = 0;
+  line_count = 0;
+
+  item_y = 0;
+
+  /* clear the line width arrays */
+  if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
+    {
+      if (priv->line_min != NULL)
+        g_array_free (priv->line_min, TRUE);
+
+      if (priv->line_natural != NULL)
+        g_array_free (priv->line_natural, TRUE);
+
+      priv->line_min = g_array_sized_new (FALSE, FALSE,
+                                          sizeof (gfloat),
+                                          16);
+      priv->line_natural = g_array_sized_new (FALSE, FALSE,
+                                              sizeof (gfloat),
+                                              16);
+    }
 
   for (l = children; l != NULL; l = l->next)
     {
       ClutterActor *child = l->data;
       gfloat child_min, child_natural;
+      gfloat new_y, item_height;
 
       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
         continue;
 
-      clutter_actor_get_preferred_width (child, for_height,
-                                         &child_min,
-                                         &child_natural);
+      if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
+        {
+          if (line_item_count == n_rows)
+            {
+              total_min_width += line_min_width;
+              total_natural_width += line_natural_width;
 
-      max_child_min_width = MAX (max_child_min_width, child_min);
-      max_child_natural_width = MAX (max_child_natural_width, child_natural);
+              g_array_append_val (priv->line_min,
+                                  line_min_width);
+              g_array_append_val (priv->line_natural,
+                                  line_natural_width);
 
-      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
-        row_natural_width += (child_natural + priv->col_spacing);
+              line_min_width = line_natural_width = 0;
+
+              line_item_count = 0;
+              line_count += 1;
+              item_y = 0;
+            }
+
+          new_y = ((line_item_count + 1) * (for_height + priv->row_spacing))
+                / n_rows;
+          item_height = new_y - item_y - priv->row_spacing;
+
+          clutter_actor_get_preferred_width (child, item_height,
+                                             &child_min,
+                                             &child_natural);
+
+          line_min_width = MAX (line_min_width, child_min);
+          line_natural_width = MAX (line_natural_width, child_natural);
+
+          item_y = new_y;
+          line_item_count += 1;
+
+          max_min_width = MAX (max_min_width, line_min_width);
+          max_natural_width = MAX (max_natural_width, line_natural_width);
+        }
       else
-        row_natural_width = MAX (row_natural_width, max_child_natural_width);
+        {
+          clutter_actor_get_preferred_width (child, for_height,
+                                             &child_min,
+                                             &child_natural);
+
+          max_min_width = MAX (max_min_width, child_min);
+          max_natural_width = MAX (max_natural_width, child_natural);
+
+          line_count += 1;
+        }
     }
 
   g_list_free (children);
 
-  priv->col_width = max_child_natural_width;
+  priv->col_width = max_natural_width;
 
-  /* if we get a maximum value for the column width we only apply
-   * it if there isn't a child whose minimum width is bigger than
-   * the requested maximum width
-   */
   if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width)
-    priv->col_width = MAX (priv->max_col_width, max_child_min_width);
+    priv->col_width = MAX (priv->max_col_width, max_min_width);
 
   if (priv->col_width < priv->min_col_width)
     priv->col_width = priv->min_col_width;
 
+  if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
+    {
+      /* if we have a non-full row we need to add it */
+      if (line_item_count > 0)
+        {
+          total_min_width += line_min_width;
+          total_natural_width += line_natural_width;
+
+          g_array_append_val (priv->line_min,
+                              line_min_width);
+          g_array_append_val (priv->line_natural,
+                              line_natural_width);
+        }
+
+      priv->line_count = line_count;
+      if (priv->line_count > 0)
+        {
+          gfloat total_spacing;
+
+          total_spacing = priv->col_spacing * (priv->line_count - 1);
+
+          total_min_width += total_spacing;
+          total_natural_width += total_spacing;
+        }
+    }
+
   if (min_width_p)
-    *min_width_p = ceilf (max_child_min_width);
+    *min_width_p = total_min_width;
 
   if (nat_width_p)
-    *nat_width_p = ceilf (row_natural_width);
+    *nat_width_p = total_natural_width;
 }
 
 static void
@@ -159,74 +311,141 @@ clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager,
 {
   ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
   GList *l, *children = clutter_container_get_children (container);
-  gfloat max_child_min_height, max_child_natural_height;
-  gfloat col_natural_height;
+  gint n_columns, line_item_count, line_count;
+  gfloat total_min_height, total_natural_height;
+  gfloat line_min_height, line_natural_height;
+  gfloat max_min_height, max_natural_height;
+  gfloat item_x;
 
-  max_child_min_height = max_child_natural_height = 0;
-  col_natural_height = 0;
+  n_columns = get_columns (CLUTTER_FLOW_LAYOUT (manager), for_width);
+
+  total_min_height = 0;
+  total_natural_height = 0;
+
+  line_min_height = 0;
+  line_natural_height = 0;
+
+  line_item_count = 0;
+  line_count = 0;
+
+  item_x = 0;
+
+  /* clear the line height arrays */
+  if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
+    {
+      if (priv->line_min != NULL)
+        g_array_free (priv->line_min, TRUE);
+
+      if (priv->line_natural != NULL)
+        g_array_free (priv->line_natural, TRUE);
+
+      priv->line_min = g_array_sized_new (FALSE, FALSE,
+                                          sizeof (gfloat),
+                                          16);
+      priv->line_natural = g_array_sized_new (FALSE, FALSE,
+                                              sizeof (gfloat),
+                                              16);
+    }
 
   for (l = children; l != NULL; l = l->next)
     {
       ClutterActor *child = l->data;
       gfloat child_min, child_natural;
+      gfloat new_x, item_width;
 
       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
         continue;
 
-      clutter_actor_get_preferred_height (child, for_width,
-                                          &child_min,
-                                          &child_natural);
+      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
+        {
+          if (line_item_count == n_columns)
+            {
+              total_min_height += line_min_height;
+              total_natural_height += line_natural_height;
 
-      max_child_min_height = MAX (max_child_min_height, child_min);
-      max_child_natural_height = MAX (max_child_natural_height, child_natural);
+              g_array_append_val (priv->line_min,
+                                  line_min_height);
+              g_array_append_val (priv->line_natural,
+                                  line_natural_height);
 
-      if (priv->orientation == CLUTTER_FLOW_VERTICAL)
-        col_natural_height += (child_natural + priv->row_spacing);
+              line_min_height = line_natural_height = 0;
+
+              line_item_count = 0;
+              line_count += 1;
+              item_x = 0;
+            }
+
+          new_x = ((line_item_count + 1) * (for_width + priv->col_spacing))
+                / n_columns;
+          item_width = new_x - item_x - priv->col_spacing;
+
+          clutter_actor_get_preferred_height (child, item_width,
+                                              &child_min,
+                                              &child_natural);
+
+          line_min_height = MAX (line_min_height, child_min);
+          line_natural_height = MAX (line_natural_height, child_natural);
+
+          item_x = new_x;
+          line_item_count += 1;
+
+          max_min_height = MAX (max_min_height, line_min_height);
+          max_natural_height = MAX (max_natural_height, line_natural_height);
+        }
       else
-        col_natural_height = MAX (col_natural_height, max_child_natural_height);
+        {
+          clutter_actor_get_preferred_height (child, for_width,
+                                              &child_min,
+                                              &child_natural);
+
+          max_min_height = MAX (max_min_height, child_min);
+          max_natural_height = MAX (max_natural_height, child_natural);
+
+          line_count += 1;
+        }
     }
 
   g_list_free (children);
 
-  priv->row_height = max_child_natural_height;
+  priv->row_height = max_natural_height;
 
-  /* similarly to max-col-width, we only apply max-row-height if there
-   * is no child with a bigger minimum height
-   */
   if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height)
-    priv->row_height = MAX (priv->max_row_height, max_child_min_height);
+    priv->row_height = MAX (priv->max_row_height, max_min_height);
 
   if (priv->row_height < priv->min_row_height)
     priv->row_height = priv->min_row_height;
 
+  if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
+    {
+      /* if we have a non-full row we need to add it */
+      if (line_item_count > 0)
+        {
+          total_min_height += line_min_height;
+          total_natural_height += line_natural_height;
+
+          g_array_append_val (priv->line_min,
+                              line_min_height);
+          g_array_append_val (priv->line_natural,
+                              line_natural_height);
+        }
+
+      priv->line_count = line_count;
+      if (priv->line_count > 0)
+        {
+          gfloat total_spacing;
+
+          total_spacing = priv->row_spacing * (priv->line_count - 1);
+
+          total_min_height += total_spacing;
+          total_natural_height += total_spacing;
+        }
+    }
+
   if (min_height_p)
-    *min_height_p = ceilf (max_child_min_height);
+    *min_height_p = total_min_height;
 
   if (nat_height_p)
-    *nat_height_p = ceilf (col_natural_height);
-}
-
-static gint
-compute_lines (ClutterFlowLayout *self,
-               const GList       *children,
-               gfloat             avail_width,
-               gfloat             avail_height)
-{
-  ClutterFlowLayoutPrivate *priv = self->priv;
-  gint items_per_line;
-
-  if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
-    {
-      items_per_line = (avail_width - priv->col_spacing)
-                     / (priv->col_width + priv->col_spacing);
-    }
-  else
-    {
-      items_per_line = (avail_height - priv->row_spacing)
-                     / (priv->row_height + priv->row_spacing);
-    }
-
-  return items_per_line;
+    *nat_height_p = total_natural_height;
 }
 
 static void
@@ -239,7 +458,7 @@ clutter_flow_layout_allocate (ClutterLayoutManager   *manager,
   GList *l, *children = clutter_container_get_children (container);
   gfloat avail_width, avail_height;
   gfloat item_x, item_y;
-  gint line_items_count;
+  gint line_item_count;
   gint items_per_line;
   gint line_index;
 
@@ -249,12 +468,11 @@ clutter_flow_layout_allocate (ClutterLayoutManager   *manager,
   clutter_actor_box_get_size (allocation, &avail_width, &avail_height);
 
   items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager),
-                                  children,
                                   avail_width, avail_height);
 
   item_x = item_y = 0;
 
-  line_items_count = 0;
+  line_item_count = 0;
   line_index = 0;
 
   for (l = children; l != NULL; l = l->next)
@@ -262,49 +480,88 @@ clutter_flow_layout_allocate (ClutterLayoutManager   *manager,
       ClutterActor *child = l->data;
       ClutterActorBox child_alloc;
       gfloat item_width, item_height;
-      gfloat child_min, child_natural;
+      gfloat new_x, new_y;
 
       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
         continue;
 
-      if (line_items_count == items_per_line)
+      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
         {
-          if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+          if (line_item_count == items_per_line && line_item_count > 0)
             {
+              item_y += g_array_index (priv->line_natural,
+                                       gfloat,
+                                       line_index);
+
+              line_item_count = 0;
+              line_index += 1;
+
               item_x = 0;
-              item_y += priv->row_height + priv->row_spacing;
             }
-          else
+
+          new_x = ((line_item_count + 1) * (avail_width + priv->col_spacing))
+                / items_per_line;
+          item_width = new_x - item_x - priv->col_spacing;
+          item_height = g_array_index (priv->line_natural,
+                                       gfloat,
+                                       line_index);
+
+          if (!priv->is_homogeneous)
             {
-              item_x += priv->col_width + priv->col_spacing;
-              item_y = 0;
+              gfloat child_min, child_natural;
+
+              clutter_actor_get_preferred_width (child, item_height,
+                                                 &child_min,
+                                                 &child_natural);
+              item_width = MIN (item_width, child_min);
+
+              clutter_actor_get_preferred_height (child, item_width,
+                                                  &child_min,
+                                                  &child_natural);
+              item_height = MIN (item_height, child_natural);
             }
-
-          line_items_count = 0;
-          line_index += 1;
-        }
-
-      if (priv->is_homogeneous)
-        {
-          item_width = priv->col_width;
-          item_height = priv->row_height;
         }
       else
         {
-          clutter_actor_get_preferred_width (child, priv->row_height,
-                                             &child_min,
-                                             &child_natural);
-          item_width = MIN (child_natural, priv->col_width);
+          if (line_item_count == items_per_line && line_item_count > 0)
+            {
+              item_x += g_array_index (priv->line_natural,
+                                       gfloat,
+                                       line_index);
 
-          clutter_actor_get_preferred_height (child, item_width,
-                                              &child_min,
-                                              &child_natural);
-          item_height = MIN (child_natural, priv->row_height);
+              line_item_count = 0;
+              line_index += 1;
+
+              item_y = 0;
+            }
+
+          new_y = ((line_item_count + 1) * (avail_height + priv->row_spacing))
+                / items_per_line;
+          item_height = new_y - item_y - priv->row_spacing;
+          item_width = g_array_index (priv->line_natural,
+                                      gfloat,
+                                      line_index);
+
+          if (!priv->is_homogeneous)
+            {
+              gfloat child_min, child_natural;
+
+              clutter_actor_get_preferred_width (child, item_height,
+                                                 &child_min,
+                                                 &child_natural);
+              item_width = MIN (item_width, child_min);
+
+              clutter_actor_get_preferred_height (child, item_width,
+                                                  &child_min,
+                                                  &child_natural);
+              item_height = MIN (item_height, child_natural);
+            }
         }
 
       CLUTTER_NOTE (LAYOUT,
-                    "flow[line:%d, item:%d/%d] = { %.2f, %.2f, %.2f, %.2f }",
-                    line_index, line_items_count + 1, items_per_line,
+                    "flow[line:%d, item:%d/%d] ="
+                    "{ %.2f, %.2f, %.2f, %.2f }",
+                    line_index, line_item_count + 1, items_per_line,
                     item_x, item_y, item_width, item_height);
 
       child_alloc.x1 = ceil (item_x);
@@ -314,11 +571,11 @@ clutter_flow_layout_allocate (ClutterLayoutManager   *manager,
       clutter_actor_allocate (child, &child_alloc, flags);
 
       if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
-        item_x += (priv->col_width + priv->col_spacing);
+        item_x = new_x;
       else
-        item_y += (priv->row_height + priv->row_spacing);
+        item_y = new_y;
 
-      line_items_count += 1;
+      line_item_count += 1;
     }
 
   g_list_free (children);
@@ -451,6 +708,20 @@ clutter_flow_layout_get_property (GObject    *gobject,
     }
 }
 
+static void
+clutter_flow_layout_finalize (GObject *gobject)
+{
+  ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv;
+
+  if (priv->line_min != NULL)
+    g_array_free (priv->line_min, TRUE);
+
+  if (priv->line_natural != NULL)
+    g_array_free (priv->line_natural, TRUE);
+
+  G_OBJECT_CLASS (clutter_flow_layout_parent_class)->finalize (gobject);
+}
+
 static void
 clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass)
 {
@@ -465,6 +736,7 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass)
 
   gobject_class->set_property = clutter_flow_layout_set_property;
   gobject_class->get_property = clutter_flow_layout_get_property;
+  gobject_class->finalize = clutter_flow_layout_finalize;
 
   layout_class->get_preferred_width =
     clutter_flow_layout_get_preferred_width;
@@ -631,6 +903,9 @@ clutter_flow_layout_init (ClutterFlowLayout *self)
 
   priv->min_col_width = priv->min_row_height = 0;
   priv->max_col_width = priv->max_row_height = -1;
+
+  priv->line_min = NULL;
+  priv->line_natural = NULL;
 }
 
 /**
diff --git a/tests/interactive/test-flow-layout.c b/tests/interactive/test-flow-layout.c
index 51fab1da3..f8131ee3e 100644
--- a/tests/interactive/test-flow-layout.c
+++ b/tests/interactive/test-flow-layout.c
@@ -81,6 +81,7 @@ test_flow_layout_main (int argc, char *argv[])
   ClutterActor *stage, *box;
   ClutterLayoutManager *layout;
   ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff };
+  ClutterColor box_color = { 255, 255, 255, 255 };
   GError *error;
   gint i;
 
@@ -114,6 +115,7 @@ test_flow_layout_main (int argc, char *argv[])
                                        y_spacing);
 
   box = clutter_box_new (layout);
+  clutter_box_set_color (CLUTTER_BOX (box), &box_color);
   clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
   clutter_actor_set_position (box, 0, 0);
 
@@ -126,7 +128,7 @@ test_flow_layout_main (int argc, char *argv[])
 
   for (i = 0; i < n_rects; i++)
     {
-      ClutterColor color = { 255, 255, 255, 255 };
+      ClutterColor color = { 255, 255, 255, 224 };
       ClutterActor *rect;
       gchar *name;
       gfloat width, height;