You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter技术问题:滑动删除ListTile前弹窗确认+交替图片失效

解决方案

问题1:滑动删除前添加确认弹窗

Dismissible组件的confirmDismiss属性可以实现删除前的确认逻辑——它要求返回Future<bool>,通过弹窗的用户选择结果来决定是否执行删除。具体修改如下:

  • 移除原有onDismissed回调,改用confirmDismiss控制删除流程
  • 完善注释的showDeleteAlertBox方法,让它返回用户的选择结果(确认返回true,取消返回false)
  • 在confirmDismiss中调用弹窗,根据结果执行列表更新

问题2:交替显示方形/圆形头像不生效

当前偶数项用Image.network加载的图片没有做方形裁剪处理,即使设置宽高,也可能因图片自身比例问题无法呈现预期的方形。需要用ClipRRect包裹图片,强制裁剪为正方形,和CircleAvatar的圆形效果对应。


修改后的完整代码

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:sample/signin.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<int> items = List<int>.generate(20, (int index) => index);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: Text(
          "Home Page",
          style: GoogleFonts.ubuntuCondensed(fontWeight: FontWeight.bold),
        ),
        actions: [
          IconButton(
            onPressed: () {
              showAlertDialog(context);
            },
            icon: const Icon(Icons.logout),
          ),
        ],
      ),
      body: SafeArea(
        child: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            return Dismissible(
              direction: DismissDirection.vertical,
              background: Container(
                color: Colors.red,
                alignment: Alignment.center,
                child: const Icon(Icons.delete, color: Colors.white),
              ),
              key: ValueKey<int>(items[index]),
              // 替换onDismissed为confirmDismiss
              confirmDismiss: (direction) async {
                final shouldDelete = await showDeleteAlertBox(context);
                if (shouldDelete == true) {
                  setState(() {
                    items.removeAt(index);
                  });
                }
                return shouldDelete;
              },
              child: ListViewWidget(
                index: index,
              ),
            );
          },
        ),
      ),
    );
  }
}

class ListViewWidget extends StatefulWidget {
  final int index; // 明确参数类型,避免类型歧义
  const ListViewWidget({
    super.key,
    required this.index,
  });

  @override
  State<ListViewWidget> createState() => _ListViewWidgetState();
}

class _ListViewWidgetState extends State<ListViewWidget> {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ListTile(
        leading: (widget.index % 2 == 0)
            ? ClipRRect( // 添加方形裁剪
                borderRadius: BorderRadius.zero,
                child: Image.network(
                  'https://upload.wikimedia.org/wikipedia/commons/9/9e/Placeholder_Person.jpg',
                  height: 50,
                  width: 50,
                  fit: BoxFit.cover, // 确保图片填充容器
                ),
              )
            : CircleAvatar(
                radius: 25,
                backgroundImage: NetworkImage(
                  'https://cdn3.iconfinder.com/data/icons/business-vol-26/100/Artboard_2-512.png',
                ),
              ),
        title: Text(
          " Person ${widget.index + 1}",
          style: GoogleFonts.ubuntu(fontWeight: FontWeight.bold),
        ),
        trailing: IconButton(
          icon: const Icon(Icons.delete),
          onPressed: () async {
            // 给右侧删除按钮添加相同确认逻辑
            final shouldDelete = await showDeleteAlertBox(context);
            if (shouldDelete == true) {
              (context.findAncestorStateOfType<_HomePageState>())?.setState(() {
                (context.findAncestorStateOfType<_HomePageState>())?.items.removeAt(widget.index);
              });
            }
          },
        ),
      ),
    );
  }
}

showAlertDialog(BuildContext ctx) {
  Widget logoutButton = TextButton(
    style: TextButton.styleFrom(backgroundColor: Colors.red),
    child: const Text(
      "Log Out",
      style: TextStyle(color: Colors.white),
    ),
    onPressed: () {
      Navigator.of(ctx).push(MaterialPageRoute(
        builder: (ctx) => const SignInPage(),
      ));
    },
  );
  Widget continueButton = TextButton(
    child: const Text(
      "Cancel",
      style: TextStyle(color: Colors.deepPurple),
    ),
    onPressed: () {
      Navigator.pop(ctx);
    },
  );

  AlertDialog alert = AlertDialog(
    title: Text(
      "Warning",
      style:
          GoogleFonts.openSans(color: Colors.red, fontWeight: FontWeight.bold),
    ),
    content: Text(
      "Would you like to continue or Logout",
      style:
          GoogleFonts.archivo(color: Colors.black, fontStyle: FontStyle.italic),
    ),
    actions: [
      continueButton,
      logoutButton,
    ],
  );

  showDialog(
    context: ctx,
    builder: (BuildContext ctx) {
      return alert;
    },
  );
}

// 修改为返回用户选择结果的异步方法
Future<bool> showDeleteAlertBox(BuildContext ctx) async {
  Widget deleteButton = TextButton(
    style: TextButton.styleFrom(backgroundColor: Colors.red),
    child: const Text(
      "Delete",
      style: TextStyle(color: Colors.white),
    ),
    onPressed: () {
      Navigator.pop(ctx, true); // 返回true表示确认删除
    },
  );
  Widget continueButton = TextButton(
    child: const Text(
      "Cancel",
      style: TextStyle(color: Colors.deepPurple),
    ),
    onPressed: () {
      Navigator.pop(ctx, false); // 返回false表示取消
    },
  );

  AlertDialog alert = AlertDialog(
    title: Text(
      "Warning",
      style:
          GoogleFonts.openSans(color: Colors.red, fontWeight: FontWeight.bold),
    ),
    content: Text(
      "Do you want to delete the item",
      style:
          GoogleFonts.archivo(color: Colors.black, fontStyle: FontStyle.italic),
    ),
    actions: [
      continueButton,
      deleteButton,
    ],
  );

  return await showDialog<bool>(
    context: ctx,
    builder: (BuildContext ctx) {
      return alert;
    },
  ) ?? false; // 默认返回false,避免null
}

关键修改说明

  1. 滑动删除确认:

    • confirmDismiss替代onDismissed,通过异步弹窗获取用户选择,决定是否完成滑动删除
    • showDeleteAlertBox改为返回Future<bool>,用Navigator.pop传递用户选择结果
  2. 交替头像样式:

    • 偶数项图片用ClipRRect包裹并设置BorderRadius.zero实现方形裁剪,fit: BoxFit.cover确保图片填充容器无变形
    • 明确ListViewWidget的index参数类型为int,避免类型推断问题
  3. 额外优化:

    • 给右侧删除按钮添加相同的确认逻辑
    • 优化滑动删除背景的图标对齐,让删除图标居中显示

内容的提问来源于stack exchange,提问作者Aswanth DS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 09:44:53