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')), ], ), ], ), ); } }