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

解决Flutter中‘已移除的Dismissible仍在组件树中’报错问题

问题解决:Dismissible删除后红屏报错

报错A dismissed Dismissible widget is still part of the tree的核心原因是:删除通知后,ListView的数据源未及时更新,导致已被删除的Dismissible组件仍保留在组件树中。以下是针对性的解决方案:


1. 修复状态管理逻辑(推荐用Provider)

将通知数据的读取、删除逻辑统一放到CommonProvider中,确保数据变更能自动通知UI刷新:

class CommonProvider extends ChangeNotifier {
  List<Notifications>? _notifications;

  List<Notifications>? get notifications => _notifications;

  // 初始化加载通知
  Future<void> loadNotifications() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String notificationsString = prefs.getString('notifications') ?? '[]';
    _notifications = (json.decode(notificationsString) as List)
        .map((item) => Notifications.fromJson(item))
        .toList();
    notifyListeners(); // 通知UI更新
  }

  // 删除单条通知
  Future<void> deleteNotification(String id) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String notificationsString = prefs.getString('notifications') ?? '[]';
    List<Notifications> notificationsList = 
        (json.decode(notificationsString) as List)
            .map((item) => Notifications.fromJson(item))
            .toList();
    
    int indexToDelete = notificationsList.indexWhere((n) => n.id == id);
    if (indexToDelete != -1) {
      notificationsList.removeAt(indexToDelete);
      String updatedString = json.encode(notificationsList);
      prefs.setString('notifications', updatedString);
      
      _notifications = notificationsList; // 更新Provider内的数据源
      notifyListeners(); // 触发UI刷新
    }
  }
}

2. 修改页面代码,正确绑定状态

移除原FutureBuilder的重复异步请求,改为通过Provider监听数据变化:

class NotificationsPage extends StatefulWidget {
  const NotificationsPage({super.key});

  @override
  _NotificationsPageState createState() => _NotificationsPageState();
}

class _NotificationsPageState extends State<NotificationsPage> {
  @override
  void initState() {
    super.initState();
    // 页面初始化时加载通知数据
    Provider.of<CommonProvider>(context, listen: false).loadNotifications();
  }

  @override
  Widget build(BuildContext context) {
    final cP = Provider.of<CommonProvider>(context);
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(title: const Text('Notifications')),
        body: Padding(
          padding: EdgeInsets.symmetric(vertical: 15.h),
          child: Column(
            children: [
              // 根据Provider状态渲染不同UI
              if (cP.notifications == null)
                const Center(child: CircularProgressIndicator())
              else if (cP.notifications!.isEmpty)
                const Center(child: Text('No Notification Available'))
              else
                SizedBox(
                  height: 500.h,
                  child: ListView.builder(
                    itemCount: cP.notifications!.length,
                    itemBuilder: (context, index) {
                      final notification = cP.notifications![index];
                      return Dismissible(
                        key: Key(notification.id.toString()),
                        onDismissed: (direction) {
                          // 调用Provider的删除方法,自动触发UI更新
                          cP.deleteNotification(notification.id.toString());
                        },
                        child: Card(
                          child: ListTile(
                            title: Text(notification.title!),
                            subtitle: Text(notification.subTitle!),
                            leading: CustomImageFrame(icon: notification.icon!),
                          ),
                        ),
                      );
                    },
                  ),
                ),
              Center(
                child: InkWell(
                  onTap: () {
                    // 批量删除逻辑同理,需更新Provider数据源并调用notifyListeners
                  },
                  child: const Icon(Icons.delete),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

3. 临时替代方案(无需修改Provider)

如果暂时不想调整状态管理,可在原deleteNotification方法末尾添加setState强制刷新页面,确保ListView数据源同步更新:

Future<void> deleteNotification(String id) async {
  // 原删除逻辑不变...
  
  if (indexToDelete != -1) {
    notificationsList.removeAt(indexToDelete);
    String updatedNotificationsString = json.encode(notificationsList);
    prefs.setString('notifications', updatedNotificationsString);
    
    // 强制刷新页面,更新ListView数据源
    if (mounted) setState(() {});
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:53:14