Chat – Xamarin.Forms

Olá, neste post irei demonstrar como você pode construir um chat utilizando Xamarin.Forms.

 

Irei partir do princípio que você acabou de criar um projeto Xamarin.Forms do tipo Portable Class Library (PCL), caso possua alguma dúvida sobre isso, recomendo ler o post Criando um projeto Xamarin.Forms.

Estruturando o projeto

Para este projeto, utilize o padrão MVVM (Model View ViewModel), dessa forma crie uma estrutura de pastas no seu projeto portable, como a demonstrada a seguir.

pcl

Crie também uma pasta CustomCells, que servirá para adicionar algumas ViewCell’s.

ADICIONANDO O NUGET PACKAGE

Para auxiliar na utilização do padrão MVVM, adicione o nuget package MvvmHelpers.

Clique com o botão direito em cima de sua Solution e selecione “Manage NuGet Packages for Solution…”.

manage

 

Digite “Refractored.MvvmHelpers” e selecione o plugin como demonstrado na imagem a seguir.

mvvmHelpers install

 

Selecione todos os projetos e clique no botão “Install”.

mvvmHelpers install

Models

Dentro da pasta Models crie uma classe chamada Message.

models

A classe Message terá todas as propriedades necessárias da mensagem, neste caso suas propriedades serão:

  • Text: O texto da mensagem.
  • MessageDateTime: A data da mensagem.
  • TimeDisplay: Receberá a data da mensagem formatada.
  • IsTextIn: Flag para indicar se a mensagem está sendo recebida (True) ou enviada (False).

Observe que a classe Message herda da classe ObservableObject que encontra-se no MvvmHelpers.


using System;
using MvvmHelpers;
namespace DemoChat.Models
{
public class Message : ObservableObject
{
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}
string _text;
public DateTime MessageDateTime
{
get { return _messageDateTime; }
set { SetProperty(ref _messageDateTime, value); }
}
DateTime _messageDateTime;
public string TimeDisplay => MessageDateTime.ToLocalTime().ToString();
public bool IsTextIn
{
get { return _isTextIn; }
set { SetProperty(ref _isTextIn, value); }
}
bool _isTextIn;
}
}

view raw

Message.cs

hosted with ❤ by GitHub

 

ViewModels

Dentro da pasta ViewModels crie uma classe chamada MainPageViewModel.

vm

A classe MainPageViewModel irá conter as seguintes propriedades.

  • ListMessages: Lista contendo todas as mensagens.
  • SendCommand: Comando que será executado quando o usuário clicar no botão enviar.
  • OutText: Texto digitado pelo usuário.

Observe que a classe MainPageViewModel herda de BaseViewModel que encontra-se no MvvmHelpers e as propriedades ListMessages e SendCommand estão sendo instanciadas no método construtor.


using System;
using System.Globalization;
using System.Windows.Input;
using DemoChat.Models;
using MvvmHelpers;
using Xamarin.Forms;
namespace DemoChat.ViewModels
{
public class MainPageViewModel : BaseViewModel
{
public ObservableRangeCollection<Message> ListMessages { get; }
public ICommand SendCommand { get; set; }
public MainPageViewModel()
{
ListMessages = new ObservableRangeCollection<Message>();
SendCommand = new Command(() =>
{
if (!String.IsNullOrWhiteSpace(OutText))
{
var message = new Message
{
Text = OutText,
IsTextIn = false,
MessageDateTime = DateTime.Now
};
ListMessages.Add(message);
OutText = "";
}
});
}
public string OutText
{
get { return _outText; }
set { SetProperty(ref _outText, value); }
}
string _outText = string.Empty;
}
}

 

CustomCells

Dentro da pasta CustomCells, crie uma classe chamada SelectorDataTemplate e duas ViewCell, uma chamada de TextInViewCell e a outra TextOutViewCell.

custom

TextInViewCell

screenshot-1513343960365

A TextInViewCell servira para apresentar a mensagem que chega para o usuário. Ela terá um Frame contendo uma Label com o texto e uma Label para apresentar a Data da mensagem.


<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
x:Class="DemoChat.CustomCells.TextInViewCell">
<Grid ColumnSpacing="2" Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Frame Grid.Row="0" Grid.Column="1" BackgroundColor="#0535f0" CornerRadius="15">
<Frame.HasShadow>
<OnPlatform x:TypeArguments="x:Boolean" iOS="false" Android="true"/>
</Frame.HasShadow>
<StackLayout>
<Label TextColor="White" Text="{Binding Text}" />
</StackLayout>
</Frame>
<Label FontSize="Micro" Grid.Row="1" Grid.Column="1" Text="{Binding MessageDateTime, StringFormat='{0:MM/dd/yyyy hh:mm tt}'}" TextColor="Gray"></Label>
</Grid>
</ViewCell>

TextOutViewCell

screenshot-1513343960365

A TextOutViewCell servira para apresentar a mensagem que é enviada pelo usuário. Ela terá um Frame contendo uma Label com o texto e uma Label para apresentar a Data da mensagem.


<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
x:Class="DemoChat.CustomCells.TextOutViewCell">
<Grid ColumnSpacing="2" Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="2"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Frame Grid.Row="0" Grid.Column="1" CornerRadius="15">
<Frame.HasShadow>
<OnPlatform x:TypeArguments="x:Boolean" Android="true" iOS="false"/>
</Frame.HasShadow>
<Frame.BackgroundColor>
<OnPlatform x:TypeArguments="Color" Android="White" iOS="#F5F5F5"/>
</Frame.BackgroundColor>
<StackLayout>
<Label TextColor="Black" Text="{Binding Text}" />
</StackLayout>
</Frame>
<Label Grid.Row="1" FontSize="Micro" Grid.Column="1" HorizontalTextAlignment="End" Text="{Binding MessageDateTime, StringFormat='{0:MM/dd/yyyy hh:mm tt}'}" TextColor="Gray"></Label>
</Grid>
</ViewCell>

 

 

SelectorDataTemplate

A classe SelectorDataTemplate servirá para identificar quando a mensagem está chegando ou sendo enviada, e no método OnSelectTemplate irá determinar qual ViewCell será usada.

Observe que a classe SelectorDataTemplate herda de DataTemplateSelector que encontra-se no Xamarin.Forms.


using DemoChat.CustomCells;
using DemoChat.Models;
using Xamarin.Forms;
namespace DemoChat
{
public class SelectorDataTemplate : DataTemplateSelector
{
private readonly DataTemplate textInDataTemplate;
private readonly DataTemplate textOutDataTemplate;
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var messageVm = item as Message;
if (messageVm == null)
return null;
return messageVm.IsTextIn ? this.textInDataTemplate : this.textOutDataTemplate;
}
public SelectorDataTemplate()
{
this.textInDataTemplate = new DataTemplate(typeof(TextInViewCell));
this.textOutDataTemplate = new DataTemplate(typeof(TextOutViewCell));
}
}
}

 

 

Views

Dentro da pasta Views crie uma ContentPage

views

 

Observação: Não se esqueça de alterar o arquivo App, para que a MainPage seja  instanciada corretamente.

app

 

MainPage Xaml

A MainPage deverá conter:

  • SelectorDataTemplate no ResourceDictionary.
  • ListView para apresentar as mensagens.
  • Entry para o usuário escrever um texto.
  • Button/Image para o usuário clicar quando quiser enviar.


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
xmlns:local="clr-namespace:DemoChat;assembly=DemoChat"
x:Class="DemoChat.Views.MainPage" Title="Chat">
<ContentPage.Resources>
<ResourceDictionary>
<local:SelectorDataTemplate x:Key="MessageTemplateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
<ScrollView>
<Grid RowSpacing="0" ColumnSpacing="0" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView
x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding ListMessages}"
HasUnevenRows="True" SeparatorVisibility="None" IsEnabled="True" Grid.Row="0"/>
<StackLayout Orientation="Horizontal" Grid.Row="1" BackgroundColor="White" VerticalOptions="EndAndExpand">
<Entry
HorizontalOptions="FillAndExpand"
Placeholder="Message"
Text="{Binding OutText}" Keyboard="Chat" Margin="4"/>
<Image Source="sendButton.png" WidthRequest="40" HeightRequest="40" Margin="4">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding SendCommand}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</Grid>
</ScrollView>
</ContentPage>

view raw

MainPage.xaml

hosted with ❤ by GitHub

 

MainPage Code-Behind

No método construtor instancie uma MainPageViewModel, atribua ao BindingContext e em seguida defina o Scroll para o final da lista no evento CollectionChanged, como demonstrado a seguir.


using DemoChat.ViewModels;
using Xamarin.Forms;
namespace DemoChat.Views
{
public partial class MainPage
{
MainPageViewModel vm;
public MainPage()
{
InitializeComponent();
BindingContext = vm = new MainPageViewModel();
vm.ListMessages.CollectionChanged += (sender, e) =>
{
var target = vm.ListMessages[vm.ListMessages.Count – 1];
MessagesListView.ScrollTo(target, ScrollToPosition.End, true);
};
}
}
}

 

 

Resultado

ezgif.com-gif-maker (3)

 

Esse e todos os exemplos deste blog encontram-se disponíveis no GitHub.

icongithub

4 comentários em “Chat – Xamarin.Forms

  1. Bom dia! Tenho uma aplicação que envia e recebe cadastros para um banco de dados em um servidor online. O banco de dados usado é firebird 2.5 e o processo de envio e recepção dos dados é via Json. Gostaria de saber se existe a possibilidade de enviar essas mensagens para ele e baixar dele quando o usuário estiver conectado. Neste caso, não preciso que a conversa seja no stilo realtime, pode até ser, mas para o meu caso, não tem necessidade. Só preciso que quando o usuário se conectar no aplicativo, o server consiga listar as mensagens enviadas por outro usuário chegue pra ele, respeitando a ordem das conversas, no mesmo esquema do exemplo citado acima. Aceito sugestões e idéias.

    Curtir

    1. Olá Eduardo,
      Como você disse que não precisa ser realtime e você já possui uma comunicação com o banco de dados, acredito que fique fácil para você.
      O que você precisa fazer é quando o usuário se conectar com a internet, baixar as mensagens para ele. E no app você adiciona em uma list e ordena pela data. Para ficar mais preciso utilize a Data, hora, minuto e segundo.

      Espero ter ajudado 🙂

      Curtir

  2. Boa tarde, muito bom o post !!
    Uma dúvida…pra uma estrutura de chat mesmo, tipo whatsapp, é recomendavel que criar um serviço com SignalR ou acha que só uma Api + Database serve ? Quando penso sobre, acho que se a aplicação não precisar ser tempo real (bem fidedigno tipo Whatsapp) serve uma Api + Db…mas se precisar ser tempo real, é aconselhavel um SignalR ne…
    Um cliente meu ta interessado em um chat e estou dando uma estudada.

    Curtir

    1. Olá Marcelo, obrigado !!
      Bom… acho que você mesmo acabou respondendo a sua pergunta kkk
      Se esse chat for mais algo como para demonstrar algumas observações, uma Api + DB acredito ser o suficiente. Caso precise de algo mais complexo, acho que só isso não resolva o seu problema.
      Espero ter ajudado.

      Curtir

Deixe um comentário