WhatsApp UI Clone | Flutter
Use the power of Flutter Widgets to take your development skills to next level
Let’s say you have a basic knowledge of Flutter, so why not practise it by making an amazing project, something like this:
Let’s start by creating your Flutter project with flutter create whatsapp_ui_clone
and 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:
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)