티스토리 뷰

Flutter로 앱을 개발할 때 스와이프하여 목록에서 항목을 삭제하는 기능은 매우 유용한 상호작용 요소입니다. 이메일, 채팅 앱 등 여러 애플리케이션에서 자주 사용되는 이 기능을 Flutter에서는 Dismissible 위젯을 통해 간단하게 구현할 수 있습니다. 이 글에서는 Dismissible 위젯을 활용하여 효과적으로 스와이프 삭제 기능을 구현하는 방법을 자세히 설명하겠습니다.

참고. Implement swipe to dismiss

Dismissible 위젯이란?

Dismissible 위젯은 사용자가 항목을 스와이프하여 목록에서 제거할 수 있게 해주는 Flutter 위젯입니다. Dismissible은 목록 아이템마다 적용할 수 있으며, 사용자가 항목을 왼쪽이나 오른쪽으로 스와이프할 때 삭제되는 방식으로 작동합니다. 이는 특히 리스트 형식의 데이터를 다룰 때 유용합니다.

Flutter에서 스와이프 삭제 기능 구현하기

1. 데이터 리스트 생성하기

먼저 스와이프 삭제를 적용할 데이터 리스트를 생성합니다. 이 예제에서는 간단히 20개의 문자열 아이템을 포함한 리스트를 사용해 보겠습니다.

final items = List<String>.generate(20, (i) => 'Item ${i + 1}');

이 리스트는 화면에 표시될 기본 데이터 소스가 됩니다. ListView.builder를 사용하여 각 아이템을 화면에 표시할 수 있습니다.

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
    );
  },
)

2. 각 아이템을 Dismissible로 감싸기

이제 Dismissible 위젯을 사용해 각 아이템을 스와이프하여 삭제할 수 있도록 변경합니다. Dismissible 위젯은 삭제 시 실행할 함수와 삭제된 후의 UI 변화를 정의할 수 있습니다.

itemBuilder: (context, index) {
  final item = items[index];
  return Dismissible(
    key: Key(item), // 각 아이템을 구분하기 위한 고유한 키 설정
    onDismissed: (direction) {
      // 데이터 소스에서 항목 삭제
      setState(() {
        items.removeAt(index);
      });
      // 삭제 후 스낵바 표시
      ScaffoldMessenger.of(context)
          .showSnackBar(SnackBar(content: Text('$item 삭제됨')));
    },
    child: ListTile(
      title: Text(item),
    ),
  );
},

key 속성

Dismissible 위젯에는 key 속성을 설정해야 합니다. 이는 Flutter가 각 위젯을 고유하게 식별하도록 하기 위한 필수 요소입니다. 예제에서는 리스트 아이템의 이름을 키로 설정하였습니다.

onDismissed 콜백

onDismissed 콜백 함수는 사용자가 아이템을 스와이프하여 삭제했을 때 실행됩니다. 이 함수에서는 해당 아이템을 데이터 소스에서 삭제하고, 삭제가 완료되었음을 사용자에게 알리기 위해 SnackBar를 표시합니다.

3. 삭제 인디케이터 추가하기

스와이프 시 삭제가 진행되고 있음을 사용자에게 알리기 위해 배경 인디케이터를 추가할 수 있습니다. 이를 위해 Dismissiblebackground 속성에 배경색 또는 아이콘을 설정하여 스와이프 시 시각적 힌트를 제공합니다.

Dismissible(
  key: Key(item),
  onDismissed: (direction) {
    setState(() {
      items.removeAt(index);
    });
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text('$item 삭제됨')));
  },
  background: Container(color: Colors.red), // 스와이프 시 빨간색 배경 표시
  child: ListTile(
    title: Text(item),
  ),
);

위 코드에서 background 속성에 빨간색 컨테이너를 설정하여 사용자가 스와이프할 때 삭제 진행 중임을 나타냅니다. 이런 시각적 피드백은 사용자 경험을 향상시키는 데 도움이 됩니다.

Dismissible 사용 시 주의할 점

  1. 고유한 키 설정: 각 Dismissible 아이템에 고유한 key를 설정하여 Flutter가 올바르게 식별할 수 있도록 해야 합니다.
  2. 삭제 확인: onDismissed를 통해 사용자가 실수로 삭제할 가능성을 방지하고, 확인 메시지를 제공할 수도 있습니다.
  3. 복구 기능 추가: 사용자가 삭제를 취소할 수 있도록 SnackBar에서 '복구' 기능을 추가하면 실수를 줄일 수 있습니다.
onDismissed: (direction) {
  final removedItem = items.removeAt(index);
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text('$removedItem 삭제됨'),
      action: SnackBarAction(
        label: '복구',
        onPressed: () {
          setState(() {
            items.insert(index, removedItem);
          });
        },
      ),
    ),
  );
},

위 코드를 통해 삭제된 아이템을 SnackBar의 '복구' 버튼을 눌러 다시 리스트에 추가할 수 있게 됩니다.


Flutter에서 스와이프 삭제 기능의 유용성

Dismissible 위젯은 사용자가 리스트 아이템을 직관적으로 관리할 수 있게 해줍니다. 이메일 앱에서 메시지를 삭제하거나, 쇼핑 목록에서 아이템을 제거하는 등의 다양한 기능을 쉽게 구현할 수 있습니다. 이를 통해 Flutter 앱에서 더욱 직관적인 사용자 경험을 제공할 수 있습니다.

 

import 'package:flutter/material.dart';

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

// MyApp is a StatefulWidget. This allows updating the state of the
// widget when an item is removed.
class MyApp extends StatefulWidget {
  const MyApp({super.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) {
    const title = 'Dismissing Items';

    return MaterialApp(
      title: title,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const 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.
                ScaffoldMessenger.of(context)
                    .showSnackBar(SnackBar(content: Text('$item dismissed')));
              },
              // Show a red background as the item is swiped away.
              background: Container(color: Colors.red),
              child: ListTile(
                title: Text(item),
              ),
            );
          },
        ),
      ),
    );
  }
}