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

调用NotifyListeners后Consumer Widget未重建的Flutter聊天应用问题

问题:聊天列表数据更新后Widget未重建

我正在开发一款聊天应用,调用更新聊天列表的方法后,数据处理一切正常,但对应的Widget却未重建。试过context.read()、context.watch()、Provider.of(context, listen: true)、ChangeNotifierProxyProvider都没用,只有重启应用才能让UI重建,求解决办法。


相关代码

主入口代码

void main() {
  runApp(MultiProvider(providers: [
    ChangeNotifierProvider(create: (context) => UsersProvider()),
    ChangeNotifierProvider(create: (context) => ChatProvider()),
  ], child: const MyApp()));
}

UsersProvider代码

class UsersProvider extends ChangeNotifier {

  UsersProvider(){
    getChats();
  }

  List<Chats> get chats=> _chats;

  List<Chats> _chats = [];


  Future getChats() async {
    final json = await UsersRepo().getChatLists();
    if (json == null) {
      return;
    }
    if (json['status']) {
      ChatListModel _model = ChatListModel.fromJson(json);
      _chats = _model.chats ?? [];
      notifyListeners();
    }
  }
}

界面代码(ChatList)

class ChatList extends StatelessWidget {
  const ChatList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.appBg,
      appBar: AppBar(
        title: Text('Users'),
        backgroundColor: AppColors.appBgLight,
      ),
      body: chatListWidget(context),
    );
  }

  Widget chatListWidget(BuildContext context) {
    return Consumer<UsersProvider>(
      builder: (BuildContext context, provider, Widget? child) {
        return ListView.separated(
          padding: EdgeInsets.symmetric(vertical: 20),
          itemCount: provider.chats.length,
          separatorBuilder: (c, i) => sbh(15),
          itemBuilder: (c, i) {
            Chats chat = provider.chats[i];
            return ListTile(
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (c) {
                      return ChatScreen(
                        user: Users(
                            name: chat.receiver?.name, sId: chat.receiver?.id),
                      );
                    },
                  ),
                );
                context.read<ChatProvider>().connectToUser(chat.receiver?.id);
              },
              leading:
              userImageWidget((chat.receiver?.name ?? "")[0].toUpperCase()),
              subtitle: Text(
                chat.lastMessage ?? "",
                style: TextStyle(
                  color: Colors.white70,
                  fontSize: 12,
                ),
              ),
              trailing: CircleAvatar(
                radius: 15,
                backgroundColor: Colors.green,
                child: Center(
                    child: Text(
                      '${chat.unReadMessageCount}',
                      style: TextStyle(fontSize: 12, color: Colors.white),
                    )),
              ),
              title: nameWidget(chat.receiver?.name ?? ''),
            );
          },
        );
      },
    );
  }

}

Chats模型类代码

class Chats {
  String? lastMessage;
  String? messageTime;
  int? unReadMessageCount;

  Chats(
      {this.lastMessage,
        this.messageTime,
        this.unReadMessageCount});

  Chats.fromJson(Map<String, dynamic> json) {
    lastMessage = json['lastMessage'];
    messageTime = json['messageTime'];
    unReadMessageCount = json['unReadMessageCount'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['lastMessage'] = this.lastMessage;
    data['messageTime'] = this.messageTime;
    data['unReadMessageCount'] = this.unReadMessageCount;
    return data;
  }
}

解决方向

1. 确保notifyListeners()在主线程调用

异步操作完成后可能处于子线程,需切换到主线程通知UI更新:

Future getChats() async {
  final json = await UsersRepo().getChatLists();
  if (json == null) {
    return;
  }
  if (json['status']) {
    ChatListModel _model = ChatListModel.fromJson(json);
    _chats = _model.chats ?? [];
    // 切换到主线程执行通知
    WidgetsBinding.instance.addPostFrameCallback((_) {
      notifyListeners();
    });
  }
}

2. 排查Provider实例重复创建问题

检查ChatList或其父Widget是否重复嵌套了ChangeNotifierProvider<UsersProvider>,导致界面监听的Provider和更新数据的Provider不是同一个实例。

3. 验证数据解析正确性

在notifyListeners()前添加打印,确认_chats确实更新为最新数据:

print("更新后聊天列表长度:${_chats.length}");

4. 用Selector替代Consumer缩小监听范围

Selector可只监听chats字段的变化,避免不必要的重建,同时确保监听数据源正确:

Widget chatListWidget(BuildContext context) {
  return Selector<UsersProvider, List<Chats>>(
    selector: (context, provider) => provider.chats,
    builder: (context, chats, child) {
      return ListView.separated(
        padding: EdgeInsets.symmetric(vertical: 20),
        itemCount: chats.length,
        separatorBuilder: (c, i) => sbh(15),
        itemBuilder: (c, i) {
          Chats chat = chats[i];
          // 剩余代码不变
        },
      );
    },
  );
}

5. 尝试改为StatefulWidget

若ChatList被缓存(如PageView的keepAlive机制),StatelessWidget可能无法触发重建,改为StatefulWidget并手动监听依赖变化:

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

  @override
  State<ChatList> createState() => _ChatListState();
}

class _ChatListState extends State<ChatList> {
  late UsersProvider _provider;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _provider = Provider.of<UsersProvider>(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.appBg,
      appBar: AppBar(
        title: const Text('Users'),
        backgroundColor: AppColors.appBgLight,
      ),
      body: ListView.separated(
        padding: const EdgeInsets.symmetric(vertical: 20),
        itemCount: _provider.chats.length,
        separatorBuilder: (c, i) => sbh(15),
        itemBuilder: (c, i) {
          Chats chat = _provider.chats[i];
          // 剩余代码不变
        },
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 17:57:20