androidiosflutterdartdismissible

Flutter: Dismissable with Card and Margin


I'm building a simple listView in Flutter where the "cells" are simple Cards with a set margin. When dismissing these cards, the "margin" covers the dismissible-background resulting in ugly designs. I've created a sample-app to showcase this problem:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

// MyApp is a StatefulWidget. This allows updating the state of the
// widget when an item is removed.
class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  @override
  MyAppState createState() {
    return MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  final items = List<String>.generate(20, (i) => "Item ${i + 1}");

  @override
  Widget build(BuildContext context) {
    final title = 'Dismissing Items';

    return MaterialApp(
      title: title,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            final item = items[index];

            return Dismissible(
              // Each Dismissible must contain a Key. Keys allow Flutter to
              // uniquely identify widgets.
              key: Key(item),
              // Provide a function that tells the app
              // what to do after an item has been swiped away.
              onDismissed: (direction) {
                // Remove the item from the data source.
                setState(() {
                  items.removeAt(index);
                });

                // Then show a snackbar.
                Scaffold.of(context)
                    .showSnackBar(SnackBar(content: Text("$item dismissed")));
              },
              // Show a red background as the item is swiped away.
              background: Container(color: Colors.red),
              child: Card(color: Colors.blue, margin: EdgeInsets.all(9), child: ListTile(title: Text('$item'))),
            );
          },
        ),
      ),
    );
  }
}

This results in the following design when dismissing: Image of a Card covering up the background of the dismissible widget

It's also not possible to put the dismissible into the card, since you don't swipe the card away then. Is this a bug in Flutter or is there an easier solution?


Solution

  • first try to wrap Dismissible with another widget so that the space around them remains for example with Padding

      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: const EdgeInsets.only(
            left: 16,
            right: 16,
          ),
          child: Dismissible(...),
        );
      }
    }
    

    then we implement the internal part of our Dismissible widget, add a container that will be rendered when we swipe our element, and don’t forget to render the element itself that we will swipe

      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: const EdgeInsets.only(
            left: 16,
            right: 16,
          ),
          child: Dismissible(
            key: ValueKey(id),
            background: Container(
    
            // most importantly, do not forget to give the inner container a 
            // padding to the right so that our icon does not stick to the 
            // wall of the container when swiping
    
              padding: const EdgeInsets.only(
                right: 16,
              ),
              color: Theme.of(context).colorScheme.error,
              alignment: Alignment.centerRight,
              child: const Icon(
                Icons.delete,
                color: Colors.white,
              ),
            ),
            child: Card(
    
              // also if you use a card as an element that will swipe then 
              // you need to remove its default space
    
              margin: const EdgeInsets.all(0),
              child: ListTile(...),
          ),
        );
      }
    }
    

    hope this helps!)