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

React Native聊天应用布局问题求助:消息框居中与iOS键盘遮挡

React Native 聊天应用问题解决方案

问题1:消息框无法实现类似WhatsApp的贴边布局

问题根源

Chat组件根容器container设置了alignItems: "center",导致ScrollView被居中压缩,无法占满屏幕宽度;同时消息项未限制最大宽度,短消息会撑满容器,无法实现贴边效果。

修复步骤

  1. 移除container样式中的justifyContent: "center"和alignItems: "center",让容器默认占满全屏
  2. 给messageItem添加maxWidth: '80%',限制消息最大宽度为屏幕的80%,短消息自动收缩并向对应方向贴边

问题2:iOS端输入框被键盘遮挡

问题根源

未适配iOS键盘弹出时的布局偏移,输入框会被键盘覆盖。

修复步骤

  1. 引入KeyboardAvoidingView组件,根据平台设置不同的适配行为(iOS用padding,Android用height)
  2. 添加ScrollView自动滚动到底部的逻辑,确保新消息始终可见
  3. 适配iOS状态栏高度,避免输入框被顶到状态栏下方

修改后的Chat.js代码

import React, { useEffect, useState, useRef } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, KeyboardAvoidingView, Platform, Keyboard } from 'react-native';

export default function Chat({ socket, username }) {
  const [currentMessage, setCurrentMessage] = useState('');
  const [messageList, setMessageList] = useState([]);
  const scrollViewRef = useRef(null);

  const enviarMensagem = async () => {
    if (currentMessage !== '') {
      const messageData = {
        user: username,
        message: currentMessage,
        time: new Date(Date.now()).getHours() + ':' + new Date(Date.now()).getMinutes(),
      };
      await socket.emit('enviar_mensagem', messageData);
      setMessageList((prevMessageList) => [...prevMessageList, messageData]);
      setCurrentMessage('');
      Keyboard.dismiss();
    }
  };

  useEffect(() => {
    socket.on('receber_mensagem', (data) => {
      setMessageList((prevMessageList) => [...prevMessageList, data]);
    });
  }, [socket]);

  useEffect(() => {
    scrollViewRef.current?.scrollToEnd({ animated: true });
  }, [messageList]);

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={Platform.OS === 'ios' ? 64 : 0}
    >
      <ScrollView
        ref={scrollViewRef}
        style={styles.messageContainer}
        contentContainerStyle={{ paddingBottom: 20 }}
        keyboardShouldPersistTaps="handled"
      >
        {messageList.map((message, index) => (
          <View 
            key={index}
            style={[
              styles.messageItem,
              message.user === username ? styles.userMessage : styles.otherMessage,
            ]}
          >
            <Text style={styles.username}>{message.user}</Text>
            <Text style={styles.messageText}>{message.message}</Text>
            <Text style={styles.time}>{message.time}</Text>
          </View>
        ))}
      </ScrollView>
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          placeholder="Digite uma mensagem..."
          value={currentMessage}
          onChangeText={setCurrentMessage}
          textAlign="left"
          returnKeyType="send"
          onSubmitEditing={enviarMensagem}
        />
        <TouchableOpacity style={styles.sendButton} onPress={enviarMensagem}>
          <Text style={styles.sendButtonText}>Enviar</Text>
        </TouchableOpacity>
      </View>
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  messageContainer: {
    flex: 1,
    paddingHorizontal: 16,
    paddingTop: 16,
  },
  messageItem: {
    backgroundColor: '#fff',
    borderRadius: 8,
    padding: 8,
    marginBottom: 8,
    maxWidth: '80%',
  },
  userMessage: {
    alignSelf: 'flex-end',
    backgroundColor: '#dcf8c6',
  },
  otherMessage: {
    alignSelf: 'flex-start',
    backgroundColor: '#fff',
  },
  username: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 4,
  },
  messageText: {
    fontSize: 14,
    marginBottom: 4,
  },
  time: {
    fontSize: 12,
    color: '#888',
    textAlign: 'right',
  },
  inputContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 8,
    borderTopWidth: 1,
    borderTopColor: '#ccc',
    backgroundColor: '#fff',
  },
  input: {
    flex: 1,
    height: 40,
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 20,
    paddingHorizontal: 16,
    marginRight: 8,
  },
  sendButton: {
    backgroundColor: '#4caf50',
    borderRadius: 20,
    paddingHorizontal: 16,
    paddingVertical: 8,
  },
  sendButtonText: {
    color: '#fff',
    fontWeight: 'bold',
    fontSize: 16,
  },
});

修改后的App.js代码

import React, { useState } from "react";
import { View, TextInput, Button, StyleSheet } from "react-native";
import { io } from "socket.io-client";
import Chat from "./Chat";

const socket = io.connect("http://***.**.*.**:3001");

export default function App() {
  const [username, setUsername] = useState("");
  const [showChat, setShowChat] = useState(false);

  const entrarSala = () => {
    if (username !== "") {
      socket.emit("entrar_sala", "sala1");
      setShowChat(true);
    }
  };

  return (
    <View style={styles.container}>
      {!showChat ? (
        <View style={styles.loginBox}>
          <TextInput
            style={styles.usernameInput}
            placeholder="José..."
            onChangeText={(text) => setUsername(text)}
          />
          <Button title="Entrar no Chat" onPress={entrarSala} />
        </View>
      ) : (
        <Chat socket={socket} username={username} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  loginBox: {
    justifyContent: "center",
    alignItems: "center",
    flex: 1,
  },
  usernameInput: {
    height: 40,
    width: 200,
    borderColor: "gray",
    borderWidth: 1,
    paddingHorizontal: 10,
    marginBottom: 16,
  },
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:02:00