How to Create a Desktop Skype-like Chat Box in Flutter?

Desktop Skype is a software that enables users to make voice and video calls over the internet. The app also allows for instant messaging, file sharing, and calling landlines and mobiles. Desktop Skype is available for Windows, Mac, and Linux operating systems.

In this snippet, we will try to replicate Skype-like desktop UI in Flutter.

import 'dart:math';
import 'package:flutter/material.dart';
class Theme {
static final Color primaryTextColor = Colors.grey.shade700;
static final Color secondaryTextColor = Colors.grey;
}
class SkypePage extends StatefulWidget {
@override
_SkypePageState createState() => _SkypePageState();
}
class _SkypePageState extends State<SkypePage> {
final double leftWidth = 300.0;
Widget _leftCol() {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
MainAvatarRow(),
],
),
Row(
children: [
Flexible(
flex: 8,
child: TextField(
maxLines: 1,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0),
hintText: 'People, groups & messages',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderSide:
BorderSide(color: Theme.secondaryTextColor))),
),
),
Flexible(
child: IconButton(
icon: Icon(Icons.dialpad_outlined), onPressed: () {}))
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LabelButton(label: 'Chats', icon: Icons.message),
LabelButton(label: 'Calls', icon: Icons.phone),
LabelButton(label: 'Contacts', icon: Icons.contact_page_outlined),
LabelButton(
label: 'Notifications',
icon: Icons.notification_important_outlined),
],
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LabelButtonRow(
icon: Icons.video_call_outlined, label: 'Meet Now'),
SizedBox(width: 4.0),
LabelButtonRow(icon: Icons.note_add_outlined, label: 'New Chat')
],
),
SizedBox(height: 8.0),
Text(
'CHATS',
style: TextStyle(fontSize: 16.0, color: Theme.primaryTextColor),
),
Expanded(
child: ListView(
children: [
...List.generate(15, (index) {
return ListTile(
contentPadding: EdgeInsets.zero,
title: Row(
children: [
AvatarRow(
name: 'Name ${index + 1}',
message: 'hello',
),
],
),
);
})
],
),
),
],
),
);
}
Widget _rightCol() {
return Column(
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name 1',
style: TextStyle(
fontSize: 18.0, color: Theme.primaryTextColor)),
Row(
children: [
Text('Last seen days ago',
style: TextStyle(color: Theme.secondaryTextColor)),
Text(' | ',
style: TextStyle(color: Theme.secondaryTextColor)),
Icon(Icons.image, color: Theme.secondaryTextColor),
Text('Gallery',
style: TextStyle(color: Theme.secondaryTextColor)),
Text(' | ',
style: TextStyle(color: Theme.secondaryTextColor)),
Icon(Icons.search, color: Theme.secondaryTextColor),
Text('Find',
style: TextStyle(color: Theme.secondaryTextColor)),
],
),
],
),
Spacer(),
RoundButton(icon: Icons.video_call_outlined),
RoundButton(icon: Icons.call),
RoundButton(icon: Icons.person_add),
],
),
Divider(
color: Theme.secondaryTextColor,
thickness: 1,
),
SingleChildScrollView(
child: ChatBox(),
),
Row(
children: [
Flexible(
flex: 8,
child: TextField(
maxLines: 1,
decoration: InputDecoration(
fillColor: Colors.grey,
contentPadding: EdgeInsets.all(0),
hintText: 'Type a message',
prefixIcon: Icon(Icons.emoji_emotions_outlined),
border: OutlineInputBorder(
borderSide:
BorderSide(color: Theme.secondaryTextColor))),
),
),
Spacer(),
Flexible(child: RoundButton(icon: Icons.folder_open)),
Flexible(child: RoundButton(icon: Icons.contact_mail_outlined)),
Flexible(child: RoundButton(icon: Icons.mic)),
Flexible(child: RoundButton(icon: Icons.more_horiz_outlined)),
],
)
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: leftWidth,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.grey.shade100,
border: Border(
right: BorderSide(
color: Theme.secondaryTextColor,
width: 1,
))),
child: _leftCol()),
Expanded(
child: Container(
padding: EdgeInsets.all(16.0),
height: double.infinity,
color: Colors.white,
child: _rightCol()),
),
],
),
);
}
}
class Avatar extends StatelessWidget {
final String? image;
final double? size;
const Avatar({Key? key, this.size, this.image}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: size ?? 36,
height: size ?? 36,
child: ClipRRect(
borderRadius: BorderRadius.circular(36.0),
child: Image.network(
image ??
'https://cdn.pixabay.com/photo/2014/11/30/14/11/cat-551554_960_720.jpg',
fit: BoxFit.fill,
),
));
}
}
class AvatarRow extends StatelessWidget {
final Avatar? avatar;
final String? name;
final String? message;
const AvatarRow({Key? key, this.name, this.message, this.avatar})
: super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
//padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
avatar ?? Avatar(),
SizedBox(width: 16),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name ?? 'Name',
style: TextStyle(color: Theme.primaryTextColor)),
Text(message ?? 'hello',
style: TextStyle(color: Theme.secondaryTextColor)),
],
),
Spacer(),
Text('${Random().nextInt(12) + 1}/${Random().nextInt(20) + 1}/2021',
style: TextStyle(color: Theme.secondaryTextColor))
],
),
),
);
}
}
class MainAvatarRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Avatar(
image:
'https://cdn.pixabay.com/photo/2014/04/13/20/49/cat-323262__340.jpg'),
SizedBox(width: 16),
Text('TL Templates',
style: TextStyle(color: Theme.primaryTextColor)),
SizedBox(width: 16),
Text('\$0.00',
style:
TextStyle(color: Theme.primaryTextColor.withOpacity(0.5))),
Spacer(),
IconButton(icon: Icon(Icons.more_horiz_outlined), onPressed: () {})
],
),
),
);
}
}
class LabelButton extends StatelessWidget {
final IconData? icon;
final VoidCallback? onPressed;
final String? label;
const LabelButton({Key? key, @required this.icon, this.onPressed, this.label})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
icon: Icon(icon!, color: Theme.primaryTextColor, size: 18.0),
onPressed: onPressed ?? () {}),
Text(
label!,
style: TextStyle(color: Theme.primaryTextColor),
)
],
);
}
}
class LabelButtonRow extends StatelessWidget {
final IconData? icon;
final VoidCallback? onPressed;
final String? label;
const LabelButtonRow(
{Key? key, @required this.icon, this.onPressed, this.label})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.zero,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.0),
),
child: Row(
children: [
IconButton(
icon: Icon(icon!, color: Theme.primaryTextColor, size: 18.0),
onPressed: onPressed ?? () {}),
Text(
label!,
style: TextStyle(color: Theme.primaryTextColor),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
color: Theme.primaryTextColor, size: 18.0),
onPressed: onPressed ?? () {})
],
),
);
}
}
class RoundButton extends StatelessWidget {
final IconData? icon;
final VoidCallback? onPressed;
const RoundButton({Key? key, @required this.icon, this.onPressed})
: super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 0),
child: Container(
decoration:
BoxDecoration(color: Colors.grey.shade100, shape: BoxShape.circle),
child: IconButton(
icon: Icon(icon!, color: Theme.primaryTextColor, size: 18.0),
onPressed: onPressed ?? () {}),
),
);
}
}
class ChatBox extends StatefulWidget {
@override
_ChatBoxState createState() => _ChatBoxState();
}
class _ChatBoxState extends State<ChatBox> {
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height - 150,
padding: EdgeInsets.fromLTRB(36.0, 16.0, 36.0, 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Avatar(),
SizedBox(width: 8.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Name, 8:30PM',
style: TextStyle(color: Theme.secondaryTextColor),
),
Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(8.0)),
child: Text('Hello\nMy test message')),
],
),
],
),
SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(8.0)),
child: Text('Hello')),
],
),
],
),
);
}
}

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close