Flutter使用flutter_chat_ui点击消息报错:RenderSliverPadding子类型不匹配
问题:flutter_chat_ui点击消息触发RenderSliverPadding类型不匹配错误
使用flutter_chat_ui开发聊天机器人,初始消息能正常获取展示,但点击任意初始消息时触发报错:
Error: A RenderSliverPadding expected a child of type RenderSliver but received a child of type RenderErrorBox
点击操作本应向消息列表新增消息并展示,却出现上述错误。RenderObjects需要特定类型子组件配合布局绘制,比如RenderSliver不能作为RenderBox的子组件,两者布局协议不兼容。
ChatDemo类代码
class _ChatScreenDemo2 extends State<ChatScreenDemo2> { final GlobalKey<SliverAnimatedListState> _sliverAnimatedListKey = GlobalKey<SliverAnimatedListState>(); ChatScreenViewModel chatScreenVM = ChatScreenViewModel(); ChatController? _chatController; List<types.TextMessage> messages = []; final String User = 'user_1'; // Example user ID final String Admin = 'user_2'; @override void initState() { super.initState(); chatScreenVM.fetchInitialMessage(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Chat'), ), // body: Column( // mainAxisSize: MainAxisSize.min, // children: [ // Flexible( // child: Observer(builder: (_) { // return Chat( // messages: chatScreenVM.initialMessageList.reversed.toList(), // onSendPressed: _handleSendPressed, // user: types.User(id: User), // onMessageTap: _handleMessageTap, // ); // }), // ), // ], // ), body: Container( height: MediaQuery.of(context).size.height, // Set a fixed height child: Observer(builder: (_) { return Chat( messages: chatScreenVM.initialMessageList.reversed.toList(), onSendPressed: _handleSendPressed, user: types.User(id: User), onMessageTap: _handleMessageTap, ); }), ), ); } void _handleSendPressed(types.PartialText message) { final newMessage = types.TextMessage( author: types.User(id: User), createdAt: DateTime.now().millisecondsSinceEpoch, id: DateTime.now().millisecondsSinceEpoch.toString(), text: message.text, ); setState(() { messages.add(newMessage); }); } void _handleMessageTap(BuildContext context, types.Message message) { if (message is types.TextMessage) { final newMessage = types.TextMessage( author: types.User(id: "user_1"), id: message.id, createdAt: DateTime.now().millisecondsSinceEpoch - 10000, text: message.text, ); setState(() { chatScreenVM.updateMessageList(newMessage); print("${message.id}"); print("Cliked"); chatScreenVM.fetchResponse(message.id); }); } } }
Chat屏幕视图模型代码
import 'dart:convert'; import 'package:echarak/Modal/chat_screen_inital_message_response_model.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:mobx/mobx.dart'; import '../Modal/chat_screen_initial_message_model.dart'; import '../constant/base_url.dart'; import 'package:http/http.dart' as http; import 'package:flutter_chat_types/flutter_chat_types.dart' as types; part 'chat_screen_view_model.g.dart'; class ChatScreenViewModel = _ChatScreenViewModel with _$ChatScreenViewModel; abstract class _ChatScreenViewModel with Store{ final FlutterSecureStorage secureStorage = FlutterSecureStorage(); // Initialize secure storage @observable ObservableList<types.TextMessage> initialMessageList = ObservableList(); @action Future<void> updateMessageList(types.TextMessage value) async{ try{ initialMessageList.add(value); }catch(e){ print(e.toString()); } } @action Future<void> fetchInitialMessage()async{ try { final token = await secureStorage.read(key: 'token'); final response = await http.post( Uri.parse('${AppUrls.BASE_URL}/GetTer'), headers: { "Content-Type": "application/json", 'Authorization': 'Bearer $token', // }, ); if (response.statusCode == 200) { final List<dynamic> responseBody = json.decode(response.body); // Map response to initialMessageList initialMessageList = ObservableList<types.TextMessage>.of( responseBody.map((item) { final chatScreenInitial = ChatScreenInitial.fromJson(item); return types.TextMessage( author: types.User(id: "user_2"), id: chatScreenInitial.id.toString(), createdAt: DateTime.now().millisecondsSinceEpoch - 10000, text: chatScreenInitial.type_of_query ?? '', // Fallback if null ); }).toList(), ); print("Initial Message list:$responseBody"); } else { print("Error: ${response.statusCode}"); } }catch(e){ print(e.toString()); } } @action Future<void> fetchResponse(String id) async{ try { final token = await secureStorage.read(key: 'token'); final response = await http.post( Uri.parse('${AppUrls.BASE_URL}/GetsubTer/$id'), headers: { "Content-Type": "application/json", 'Authorization': 'Bearer $token', // }, ); if (response.statusCode == 200) { final List<dynamic> responseBody = json.decode(response.body); final responseList = ObservableList<types.TextMessage>.of( responseBody.map((item) { final chatScreenInitialResonse = ChatScreenInitialResponse.fromJson(item); return types.TextMessage( author: types.User(id: "user_2"), id: chatScreenInitialResonse.id.toString(), createdAt: DateTime.now().millisecondsSinceEpoch - 10000, text: chatScreenInitialResonse.subTypeOfQuery ?? '', // Fallback if null ); }).toList(), ); initialMessageList.addAll(responseList); print("Initial Message list:$responseBody"); } else { print("Error: ${response.statusCode}"); } }catch(e){ print(e.toString()); } } }
解决方案
1. 修复布局容器冲突
Chat组件内部依赖Sliver布局体系,固定高度的Container会破坏其布局逻辑,改用Column+Flexible结构让组件自适应:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Chat')), body: Column( children: [ Flexible( child: Observer(builder: (_) { return Chat( messages: chatScreenVM.initialMessageList.reversed.toList(), onSendPressed: _handleSendPressed, user: types.User(id: User), onMessageTap: _handleMessageTap, ); }), ), ], ), ); }
2. 解决消息ID重复问题
点击添加的新消息使用了原消息的ID,会导致组件内部渲染异常,需生成唯一ID:
void _handleMessageTap(BuildContext context, types.Message message) { if (message is types.TextMessage) { final newMessage = types.TextMessage( author: types.User(id: "user_1"), id: DateTime.now().millisecondsSinceEpoch.toString(), // 生成唯一ID createdAt: DateTime.now().millisecondsSinceEpoch, text: message.text, ); chatScreenVM.updateMessageList(newMessage); print("${message.id}"); print("Clicked"); chatScreenVM.fetchResponse(message.id); } }
3. 移除不必要的setState
因为使用MobX的ObservableList,列表更新后Observer会自动触发UI重建,无需额外调用setState,包括_handleSendPressed方法也可以去掉:
void _handleSendPressed(types.PartialText message) { final newMessage = types.TextMessage( author: types.User(id: User), createdAt: DateTime.now().millisecondsSinceEpoch, id: DateTime.now().millisecondsSinceEpoch.toString(), text: message.text, ); chatScreenVM.updateMessageList(newMessage); // 直接更新ObservableList }
4. 规范时间戳逻辑
避免使用固定偏移的时间戳,确保新消息时间戳晚于旧消息,保证消息排序正确,推荐使用DateTime.now().millisecondsSinceEpoch作为消息创建时间。
内容的提问来源于stack exchange,提问作者Jaivik
相关产品推荐
相关产品推荐

