Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter stack of cards

Im trying to create a stack of cards, superimposed, and offset of each other to visualize multiple versions of a card.

enter image description here

I have tried putting cards inside cards, but do not find a way to offset them. I also tried using the stack class with no luck.

Anyone know how i can achieve this effect?

like image 561
Håkon Halldal Avatar asked Jul 05 '18 08:07

Håkon Halldal


2 Answers

You were going in the right direction with Stack - you just had to figure out how to offset the widget. The best way to do this for the 'top' of the stack is to use padding, but you don't want to have to specify the size of each of the cards... it's much better if the stack grows/shrinks based on the content which is actually being shown.

To that end, you can use Positioned with all of the sizes specified for the cards. That will make sure that they grow to the appropriate size, without making the stack resize or having to specify each of their sizes.

Here's the code:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SafeArea(
        child: Container(
          color: Colors.grey,
          child: ListView(
            children: [
              StackOfCards(
                child: Container(height: 100.0),
                num: 5,
              ),
              StackOfCards(
                child: Container(height: 100.0),
                num: 4,
              ),
              StackOfCards(
                child: Container(height: 100.0),
                num: 3,
              ),
              StackOfCards(
                child: Container(height: 100.0),
                num: 2,
              ),
              StackOfCards(
                child: Container(height: 100.0),
                num: 1,
              )
            \],
          ),
        ),
      ),
    );
  }
}

class StackOfCards extends StatelessWidget {
  final int num;
  final Widget child;
  final double offset;

  const StackOfCards({Key key, int num = 1, @required this.child, this.offset = 10.0})
      : this.num = num > 0 ? num : 1,
        assert(offset != null),
        super(key: key);

  @override
  Widget build(BuildContext context) => Stack(
        children: List<Widget>.generate(
            num - 1,
            (val) => Positioned(
                bottom: val * offset,
                left: val * offset,
                top: (num - val - 1) * offset,
                right: (num - val - 1) * offset,
                child: Card(child: Container()))).toList()
          ..add(
            Padding(
              child: Card(child: child),
              padding: EdgeInsets.only(bottom: (num - 1) * offset, left: (num - 1) * offset),
            ),
          ),
      );
}

Hmmm... I guess that build function could probably be explained a bit. What I'm doing is using a generated list to iterate from 0..(num cards - 1) and calculating the appropriate offsets for each Positioned widget (each of which contains an essentially empty card).

Then this gets made from an iterable to a list with .toList(), but doesn't have the top card yet... so I do an inline add (I'm sure there's a better word for that, but I don't know it) of the Padding widget that has the appropriate offsets and contains the child. The ..add just makes it so that I can do it in one line - it returns the list instead of void as .add would. Yay for dart =)!

I made it a little bit flexible, but you could go further and define the offset as two parameters, make it so that you can go different directions etc. Anyways, the code results in this:

Screenshot of cards

like image 196
rmtmckenzie Avatar answered Oct 05 '22 06:10

rmtmckenzie


This may not be the best approach but according to the sample you posted in the comment you can align it with a Stack and Padding:

Stack(
  children: <Widget>[
    Padding(
      padding: const EdgeInsets.only(top: 8.0, right: 8.0),
      child: Card(
          child: Center(
        child: Padding(
          padding: const EdgeInsets.all(50.0),
          child: Text("Content"),
        ),
      )),
    ),
    Padding(
      padding: const EdgeInsets.only(left: 8.0, bottom: 8.0),
      child: Card(
          child: Center(
        child: Padding(
          padding: const EdgeInsets.all(50.0),
          child: Text("Content"),
        ),
      )),
    ),
  ],
)

Which will look like this:

enter image description here

You can then customize it with setting different paddings etc.

like image 34
Bostrot Avatar answered Oct 05 '22 06:10

Bostrot