WhatsApp UI Clone | Flutter

Use the power of Flutter Widgets to take your development skills to next level

Mohammad Usama
8 min readAug 15, 2023

Let’s say you have a basic knowledge of Flutter, so why not practise it by making an amazing project, something like this:

WhatsApp

Let’s start by creating your Flutter project with flutter create whatsapp_ui_cloneand opening it in Android Studio, VS Code, or IntelliJ IDEA.

In the left pane of VS Code, make sure that Explorer is selected, and open the pubspec.yaml file.

pubspec.yaml

Replace the contents of this file with the following:

name: whatsapp_ui_clone
description: A new Flutter project.

publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1

environment:
sdk: ">=3.0.6 <4.0.0"

dependencies:
flutter:
sdk: flutter

cupertino_icons: ^1.0.2

dev_dependencies:
flutter_test:
sdk: flutter

flutter_lints: ^2.0.0

flutter:
uses-material-design: true

assets:
- assets/

The pubspec.yaml file specifies basic information about your app, such as its current version, its dependencies, and the assets with which it will ship.

Note: If you gave your app a name other than whatsapp_ui_clone, you need to change the first line correspondingly.

Next, open another configuration file in the project, analysis_options.yaml.

analysis_options.yaml

Replace its contents with the following:

include: package:flutter_lints/flutter.yaml

linter:
rules:
prefer_const_constructors: false
prefer_final_fields: false
use_key_in_widget_constructors: false
prefer_const_literals_to_create_immutables: false
prefer_const_constructors_in_immutables: false
avoid_print: false

This file determines how strict Flutter should be when analyzing your code. Since this is your first foray into Flutter, you’re telling the analyzer to take it easy. You can always tune this later. In fact, as you get closer to publishing an actual production app, you will almost certainly want to make the analyzer stricter than this.

Now create some files under the lib/ directory. It should look something like this:

lib/

Finally, open the main.dart file under the lib/ directory.

Replace the contents of this file with the following:

import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.teal,
),
title: 'WhatsApp',
);
}
}

Open list_tile_widget.dart in lib/components directory and paste the following code:

import 'package:flutter/material.dart';

class ListTileWidget extends StatelessWidget {
const ListTileWidget({
super.key,
required this.leading,
required this.title,
required this.subtitle,
required this.trailing,
});

final Widget leading;
final Widget title;
final Widget subtitle;
final Widget trailing;

@override
Widget build(BuildContext context) {
return ListTile(
leading: leading,
title: title,
subtitle: subtitle,
trailing: trailing,
);
}
}

Will be using this ListTile in further code.

Open chat_screen.dart, status_screen.dart, and calls_screen.dart, files in lib/screens directory and following codes respectively:

chat_screen.dart

import 'package:flutter/material.dart';

class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});

@override
State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => const ListTile(
leading: CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600'),
),
title: Text('Usama'),
subtitle: Text('Hey, how are you?'),
trailing: Text('3:59 PM'),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.teal,
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.message_outlined,
color: Colors.white,
),
),
);
}
}

status_screen.dart

import 'package:flutter/material.dart';

class StatusScreen extends StatefulWidget {
const StatusScreen({super.key});

@override
State<StatusScreen> createState() => _StatusScreenState();
}

class _StatusScreenState extends State<StatusScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemBuilder: (context, index) => index == 0
? ListTile(
leading: Stack(
children: [
const CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600',
),
),
Positioned(
bottom: -1,
left: 28,
child: Container(
decoration: BoxDecoration(
color: Colors.teal,
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: 1,
),
),
child: const Icon(
Icons.add,
color: Colors.white,
size: 20,
),
),
),
],
),
title: const Text('My Status'),
subtitle: const Text('Tap to add status update'),
)
: index == 1
? const Padding(
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 20,
),
child: Text(
'Recent updates',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black54,
),
),
)
: ListTile(
leading: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.green,
width: 3,
),
),
child: const CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600'),
),
),
title: const Text('Usama'),
subtitle: const Text('35 minutes ago'),
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
mini: true,
backgroundColor: const Color.fromARGB(255, 192, 252, 247),
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.edit,
color: Colors.teal,
),
),
const SizedBox(
height: 20.0,
),
FloatingActionButton(
backgroundColor: Colors.teal,
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.camera_alt,
color: Colors.white,
),
),
const SizedBox(
height: 10.0,
),
],
),
);
}
}

calls_screen.dart

import 'package:flutter/material.dart';

class CallsScreen extends StatefulWidget {
const CallsScreen({super.key});

@override
State<CallsScreen> createState() => _CallsScreenState();
}

class _CallsScreenState extends State<CallsScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => index == 0
? const ListTile(
leading: CircleAvatar(
radius: 22,
backgroundColor: Colors.teal,
child: Icon(Icons.settings_phone),
),
title: Text('Create call link'),
subtitle: Text('Share a link for your Whatsapp Call'),
)
: index == 1
? const Padding(
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 20,
),
child: Text(
'Recent',
style: TextStyle(fontWeight: FontWeight.bold),
),
)
: ListTile(
leading: const CircleAvatar(
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600'),
),
title: const Text('Usama'),
subtitle: const Text('3:59 PM'),
trailing: Icon(
index % 3 == 0 ? Icons.phone : Icons.video_call_outlined,
color: Colors.teal,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.phone),
),
);
}
}

Remember we created some files under lib/pop up menu screens open those files and paste the following content:

new_group.dart

import 'package:flutter/material.dart';
import '../components/list_tile_widget.dart';

class NewGroup extends StatelessWidget {
const NewGroup({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: const Text('New Group'),
actions: const [
Icon(Icons.search),
SizedBox(
width: 10.0,
),
],
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => const ListTileWidget(
leading: CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600'),
),
title: Text('Usama'),
subtitle: Text('Hey there, I am using WhatsApp'),
trailing: Text(''),
),
),
);
}
}

new_broadcast.dart

import 'package:flutter/material.dart';
import 'package:whatsapp_ui_clone/components/list_tile_widget.dart';

class NewBroadcast extends StatelessWidget {
const NewBroadcast({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: const Text('New broadcast'),
actions: const [
Icon(Icons.search),
SizedBox(
width: 10.0,
),
],
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => const ListTileWidget(
leading: CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600'),
),
title: Text('Usama'),
subtitle: Text('Hey there, I am using WhatsApp'),
trailing: Text(''),
),
),
);
}
}

linked_devices.dart

import 'package:flutter/material.dart';

class LinkedDevices extends StatelessWidget {
const LinkedDevices({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: const Text('Linked devices'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 20.0,
),
const Icon(
Icons.laptop,
size: 150,
),
const Text(
'Use WhatsApp on Web, Desktop, and other devices.',
style: TextStyle(
color: Colors.black45,
),
),
const Text(
'Learn more',
style: TextStyle(
color: Colors.blueAccent,
),
),
const SizedBox(
height: 20.0,
),
Container(
width: 300,
height: 35.0,
decoration: BoxDecoration(
color: Colors.teal,
borderRadius: BorderRadius.circular(50.0),
),
child: const Center(
child: Text(
'Link a device',
style: TextStyle(
color: Colors.white,
),
),
),
),
const SizedBox(
height: 40.0,
),
const Text(
'No device connected',
style: TextStyle(
color: Colors.black87,
fontSize: 18.0,
),
),
],
),
),
),
);
}
}

starred_messages.dart

import 'package:flutter/material.dart';

class StarredMessages extends StatelessWidget {
const StarredMessages({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: const Text('Starred messages'),
actions: const [
Icon(Icons.search),
SizedBox(
width: 20.0,
),
Icon(Icons.more_vert),
SizedBox(
width: 10.0,
),
],
),
body: const Center(
child: Text('No Message Starred'),
),
);
}
}

settings.dart

import 'package:flutter/material.dart';

import '../components/list_tile_widget.dart';

class Settings extends StatelessWidget {
const Settings({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: const Text('Settings'),
actions: const [
Icon(Icons.search),
SizedBox(
width: 10.0,
),
],
),
body: const SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ListTileWidget(
leading: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600'),
),
title: Text('Usama'),
subtitle: Text('Hey there, I am using Whatsapp'),
trailing: Icon(
Icons.qr_code,
color: Colors.teal,
),
),
SizedBox(
height: 20.0,
),
ListTileWidget(
leading: Icon(Icons.key),
title: Text('Account'),
subtitle: Text('Security notifications, change number'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.lock),
title: Text('Privacy'),
subtitle: Text('Block contacts, disappearing\nmesages'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.emoji_emotions),
title: Text('Avatar'),
subtitle: Text('Create, edit, profile photo'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.message),
title: Text('Chats'),
subtitle: Text('Theme, wallpapers, chat history'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.notifications),
title: Text('Notifications'),
subtitle: Text('Message, group & call tones'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.language),
title: Text('App language'),
subtitle: Text('English (device\'s language)'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.data_saver_off),
title: Text('Storage and data'),
subtitle: Text('Network usage, auto-download'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.help_outline),
title: Text('Help'),
subtitle: Text('Help centre, contact us, privacy p'),
trailing: Text(''),
),
ListTileWidget(
leading: Icon(Icons.group),
title: Text('Invite a friend'),
subtitle: Text(''),
trailing: Text(''),
),
SizedBox(
height: 10.0,
),
Text('from'),
Image(
width: 50,
height: 35,
image: AssetImage('assets/meta_logo.png'),
color: Colors.black,
),
],
),
),
);
}
}

Finally open home_screen.dart and paste the following code:

import 'package:flutter/material.dart';
import 'package:whatsapp_ui_clone/pop%20up%20menu%20screens/linked_devices.dart';
import 'package:whatsapp_ui_clone/pop%20up%20menu%20screens/new_broadcast.dart';
import 'package:whatsapp_ui_clone/pop%20up%20menu%20screens/new_group.dart';
import 'package:whatsapp_ui_clone/pop%20up%20menu%20screens/settings.dart';
import 'package:whatsapp_ui_clone/pop%20up%20menu%20screens/starred_messages.dart';
import 'package:whatsapp_ui_clone/screens/calls_screen.dart';
import 'package:whatsapp_ui_clone/screens/chat_screen.dart';
import 'package:whatsapp_ui_clone/screens/status_screen.dart';

class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: false,
title: const Text('WhatsApp'),
bottom: const TabBar(
tabs: [
Icon(Icons.groups),
Tab(
child: Text('Chats'),
),
Tab(
child: Text('Status'),
),
Tab(
child: Text('Calls'),
),
],
),
actions: [
const Icon(Icons.camera_alt_outlined),
const SizedBox(
width: 20.0,
),
const Icon(Icons.search_outlined),
PopupMenuButton(
icon: const Icon(Icons.more_vert_outlined),
itemBuilder: (context) => [
PopupMenuItem(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NewGroup(),
),
);
},
child: const Text(
'New group',
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
),
),
),
),
PopupMenuItem(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NewBroadcast(),
),
);
},
child: const Text(
'New broadcast',
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
),
),
),
),
PopupMenuItem(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LinkedDevices(),
),
);
},
child: const Text(
'Linked devices',
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
),
),
),
),
PopupMenuItem(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const StarredMessages(),
),
);
},
child: const Text(
'Starred messages',
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
),
),
),
),
PopupMenuItem(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Settings(),
),
);
},
child: const Text(
'Settings',
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
),
),
),
),
],
)
],
),
body: const TabBarView(
children: [
Center(
child: Text('Working'),
),
ChatScreen(),
StatusScreen(),
CallsScreen(),
],
),
),
);
}
}

BOOM! You are done. Run your app on an emulator or simulator. It should look like this:

Thankyou for reading this! I hope you learned something form this article. Share your feedback with me.

Please do follow me on Medium (Mohammad Usama) and Twitter (codewith_usama)

Let’s connect on LinkedIn at: (codewithusama)

--

--