Flutter vs React Native - Contact List Demo - Performance & APK Size

Technical comparison of two cross-platform mobile app frameworks with a demo app that selects contacts from user's contact list

In this article, I am going to build two mobile apps, one in Flutter and another in React Native side by side. These apps will have the following functions:

  1. A home screen with a "Pick from the contacts" button and a list of the selected contacts.
  2. A popup that will contain a list of all contacts with a select/unselects all toggle buttons, a cancel button, and a confirm button.

Demo code is available on Github

Content

  1. Prerequisite
  2. Create widgets/components
  3. Create a Home Screen
  4. Create Contact Picker
  5. Performance and APK size
  6. Conclusion

Prerequisite

I am assuming that 

  1. You have React Native and Flutter installed
  2.  You have basic knowledge of React Native and Flutter

React Native

Create a new React Native app named react_native_contact_list by using:

  1. npx react-native init react_native_contact_list --template=react-native-template-typescript

I prefer to use TypeScript over Javascript for making my life easier. 

For accessing the contact list, we need a third-party package react-native-contacts. Don't forget to update Proguard file as follow https://www.npmjs.com/package/... as we will enable progaurd for reducing APK size.

Flutter

Create a new project named flutter_contact_list using Android Studio or Visual Studio Code as mentioned here 

https://flutter.dev/docs/get-started/editor?tab=androidstudio

https://flutter.dev/docs/get-started/test-drive?tab=vscode#create-app

For accessing the contacts in Flutter, I am going to use contact_services https://pub.dev/packages/conta... and permission_handler https://pub.dev/packages/permi...

Create Widgets / Components

For this demo, we will need some basic utilities like button UI. I am creating them before writing the code for the screens.

React Native

In your react-native project, create a new directory components and open a new file button.tsx in it. React Native buttons do not provide customerization options. So I am going to use Pressable component provided by React Native. Button will take two required and two optional arguments (terms as props):

  1.  onPress function
  2. Text inside button
  3. Button style
  4. Button text style
  1. import React from "react";
  2. import {
  3. Text,
  4. Pressable,
  5. StyleSheet,
  6. } from "react-native";
  7. type ButtonProps = {
  8. style?: any; // custom button style
  9. textStyle?: any; // text component style
  10. onPress: () => void; // action on press
  11. title: string; // text inside button
  12. }
  13. export default function(props: ButtonProps) {
  14. return (
  15. <Pressable
  16. style={[styles.button, props.style]}
  17. onPress={props.onPress}>
  18. <Text style={[styles.text, props.textStyle]}>
  19. {props.title}
  20. </Text>
  21. </Pressable>
  22. )
  23. }
  24. const styles = StyleSheet.create({
  25. // white background
  26. button: {
  27. backgroundColor: "#ffffff",
  28. },
  29. // default blue text color
  30. text: {
  31. textAlign: 'center',
  32. margin: 8,
  33. color: '#007AFF',
  34. fontSize: 18,
  35. },
  36. });

Other than the button, I am defining ListItem (contact) component along with TypeScript types for Contact data. Add the following in your App.tsx.

  1. import React, { useState, useEffect } from "react";
  2. //
  3. import {
  4. SafeAreaView,
  5. StyleSheet,
  6. View,
  7. Text,
  8. StatusBar,
  9. Modal,
  10. FlatList,
  11. Alert,
  12. Platform,
  13. PermissionsAndroid,
  14. } from "react-native";
  15. import Contacts from "react-native-contacts";
  16. // import button component
  17. import Button from "./components/button";
  18. // Contact data item
  19. type PickableContact = {
  20. name: string;
  21. phone: string;
  22. };
  23. // Contact List item props
  24. type ContactListItem = {
  25. item: PickableContact;
  26. index: number;
  27. };
  28. // contact list item component
  29. function renderContactListItem({ item: contact }: ContactListItem) {
  30. return (
  31. <View style={styles.listItem}>
  32. <View style={styles.userInfo}>
  33. <Text style={styles.name}>
  34. {contact.name}
  35. </Text>
  36. <Text style={styles.phone}>
  37. {contact.phone}
  38. </Text>
  39. </View>
  40. </View>
  41. )
  42. }

Flutter

I am creating a model file in lib/models/simplified_contract.dart.

  1. // This class will be used to store name and phone
  2. class SimplifiedContact {
  3. final int index;
  4. final String name;
  5. final String phone;
  6. SimplifiedContact(this.index, this.name, this.phone);
  7. }

Unline React Native, flutter provide option to customerize its Button widget. I still need a Button Widget for toggling purpose. I am creating a ActionButton widget in lib/widgets/action_button.dart. For keeping minimum dependencies, I am not using state managing packages like redux and mobx.

  1. import 'package:flutter/material.dart';
  2. class ActionButton extends StatelessWidget {
  3. // flag for selected contact
  4. final bool isSelected;
  5. // callback on de-selecting
  6. final Function() onRemove;
  7. // callback on selecting
  8. final Function() onSelect;
  9. ActionButton({this.isSelected, this.onRemove, this.onSelect});
  10. @override
  11. Widget build(BuildContext context) {
  12. if (isSelected) {
  13. return RaisedButton(
  14. color: Colors.white,
  15. elevation: 0,
  16. child: Text(
  17. 'Remove',
  18. style: TextStyle(color: Colors.redAccent),
  19. ),
  20. onPressed: onRemove,
  21. );
  22. }
  23. return RaisedButton(
  24. color: Colors.white,
  25. elevation: 0,
  26. child: Text(
  27. 'Select',
  28. style: TextStyle(color: Colors.blueAccent),
  29. ),
  30. onPressed: onSelect,
  31. );
  32. }
  33. }

Create a Home Screen

Lets create a home screen with a button and selected contact list.

React Native

Update the App.tsx file to return a button, a popup and a list

  1. return (
  2. <>
  3. <StatusBar barStyle="dark-content" />
  4. {shouldShowModal && renderContactModal()}
  5. <SafeAreaView style={styles.container}>
  6. <Button
  7. style={styles.contactBtn}
  8. title="Select Contacts"
  9. onPress={showContactModal}
  10. />
  11. <FlatList
  12. style={styles.selectedList}
  13. data={selectedContacts}
  14. renderItem={renderContactListItem}
  15. keyExtractor={(item, index) => index + item.name + item.phone}
  16. />
  17. </SafeAreaView>
  18. </>
  19. );


Also we need to define the state variables (inside App function):

  1. // all contacts
  2. const [allContacts, setAllContacts] = useState<PickableContact[]>([]);
  3. // temporary selected contacts indices
  4. const [tempSelectedContacts, setTempSelectedContacts] = useState<number[]>([]);
  5. // finally selected contact indicies
  6. const [selectedIndices, setSelectedIndices] = useState<number[]>([]);
  7. // selected contacts
  8. const [selectedContacts, setSelectedContacts] = useState<PickableContact[]>([]);
  9. // popup (modal) state (open or closed)
  10. const [shouldShowModal, setModalStatus] = useState(false);

For toggling contact modal, we need to write a function as follow:

  1. function showContactModal() {
  2. // load contacts (I will write this function in next part)
  3. loadContacts();
  4. // open popup
  5. setModalStatus(true);
  6. // copy selected contact indices to temporary selected contact indices
  7. setTempSelectedContacts(selectedIndices);
  8. }

Flutter

I am creating a new file in lib directory named as home.dart and adding a StatefullWidget class.

  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. // I will define this file in next section
  4. import 'package:flutter_contact_list/widgets/contact_list.dart';
  5. // SimpifiedContact class file
  6. import 'models/simplified_contact.dart';
  7. class HomePage extends StatefulWidget {
  8. // Get contacts permission
  9. @override
  10. _HomePageState createState() => _HomePageState();
  11. }
  12. class _HomePageState extends State<HomePage> {
  13. // for triggering submit function in contact widget
  14. // in professional code, I suggest to use mobx or redux for such purposes
  15. final GlobalKey<dynamic> contactWidgetKey = GlobalKey();
  16. // selected contacts
  17. List<SimplifiedContact> selectedContacts = [];
  18. // selected contact indices
  19. List<int> selectedContactIndices = [];
  20. // action on contact list selection
  21. void onContactSelect(List<SimplifiedContact> contacts, List<int> indices) {
  22. this.setState(() {
  23. selectedContacts = contacts;
  24. selectedContactIndices = indices;
  25. });
  26. }
  27. Future<void> onContactBtnPress(BuildContext context) async {
  28. // TODO: I will write this function in the next section
  29. }
  30. @override
  31. Widget build(BuildContext context) {
  32. return Scaffold(
  33. body: SafeArea(
  34. child: Column(
  35. mainAxisSize: MainAxisSize.max,
  36. children: [
  37. // Contact Pick Button
  38. Container(
  39. margin: EdgeInsets.only(top: 20),
  40. child: ElevatedButton(
  41. onPressed: () {
  42. onContactBtnPress(context);
  43. },
  44. child: Text('Select Contacts'),
  45. ),
  46. ),
  47. Container(
  48. child: Flexible(
  49. // selected contact list
  50. child: ListView.builder(
  51. scrollDirection: Axis.vertical,
  52. shrinkWrap: true,
  53. itemCount: selectedContacts?.length ?? 0,
  54. itemBuilder: (BuildContext context, int index) {
  55. SimplifiedContact contact =
  56. selectedContacts?.elementAt(index);
  57. return Column(
  58. children: [
  59. ListTile(
  60. contentPadding: const EdgeInsets.symmetric(
  61. vertical: 2,
  62. horizontal: 18,
  63. ),
  64. title: Text(contact.name),
  65. subtitle: Text(contact.phone),
  66. ),
  67. Divider(
  68. height: 1,
  69. ),
  70. ],
  71. );
  72. },
  73. ),
  74. ),
  75. ),
  76. ],
  77. ),
  78. ),
  79. );
  80. }
  81. }

Note that I have defined only two state variable in Flutter (that is 5 in the React Native) because I don't need a popup state variable in Flutter (reduced one state variable) and I have split Contact picking code to another file (reduced 2 more state variables)

Create Contact Picker

For contact picker, I need:

  1. a permission request function
  2. a contact loading function 
  3. a popup rendering function (widget class in case of Flutter)
  4. a function for contact list picker item (React Native only)
  5. a function for selecting contact
  6. a function for unselecting contact
  7. a function for selecting all contacts
  8. a function for deselecting all contacts
  9. a final function for returning selected contacts

React Native

  1. // function for loading contacts (reading from user's mobile)
  2. async function loadContacts() {
  3. // ask for contact permission on android
  4. if (Platform.OS === "android") {
  5. const granted = await PermissionsAndroid.request(
  6. PermissionsAndroid.PERMISSIONS.READ_CONTACTS, {
  7. title: "Contacts",
  8. message: "This app would like to view your contacts.",
  9. buttonNeutral: "Ask Me Later",
  10. buttonNegative: "Cancel",
  11. buttonPositive: "OK"
  12. });
  13. if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
  14. Alert.alert("Error", "Contact permission is required!");
  15. return null;
  16. }
  17. }
  18. // read from user's mobile
  19. Contacts.getAll()
  20. .then(contacts => {
  21. // get list of name and phone numbers
  22. // response will be a list of lists like [[{name: "", phone:""}, {...}], [...]]
  23. const namedContacts = contacts.map(c => {
  24. const name = (c.givenName + " " + c.middleName + " " + c.familyName).trim();
  25. return c.phoneNumbers.map(p => ({
  26. name,
  27. phone: p.number,
  28. }))
  29. });
  30. // reduce nested list into a linear list like [{name:"", phone: ""}, {...}]
  31. const simplifiedContacts = namedContacts.reduce((arr, val) => arr.concat(val));
  32. // set contacts
  33. setAllContacts(simplifiedContacts);
  34. })
  35. .catch(e => {
  36. Alert.alert("Error", e);
  37. });
  38. }
  39. // renders popup
  40. function renderContactModal() {
  41. return (
  42. {/* React Native Popup component */}
  43. <Modal
  44. onRequestClose={cancelSelection}>
  45. <SafeAreaView style={styles.modalContainer}>
  46. {/* Header (back button, title and done button */}
  47. <View style={styles.header}>
  48. <Button
  49. title="Back"
  50. style={styles.backBtn}
  51. onPress={cancelSelection}
  52. />
  53. <Text style={styles.title}>
  54. Select Contacts
  55. </Text>
  56. <Button
  57. title="Done"
  58. style={styles.doneBtn}
  59. onPress={confirmSelection}
  60. />
  61. </View>
  62. {/* Contact list */}
  63. <FlatList
  64. style={styles.contactList}
  65. contentContainerStyle={styles.contactListContent}
  66. data={allContacts}
  67. renderItem={renderPickContactItem}
  68. keyExtractor={(item, index) => index + item.name + item.phone}
  69. getItemLayout={(data, index) => ({ length: 70, offset: index * 70, index })}
  70. />
  71. {
  72. // select all and unselect all buttons
  73. tempSelectedContacts.length === allContacts.length ? (
  74. <Button
  75. style={styles.toggleBtn}
  76. textStyle={styles.toggleBtnText}
  77. title="Unselect All"
  78. onPress={unselectAllContacts}
  79. />
  80. ) : (
  81. <Button
  82. style={styles.toggleBtn}
  83. textStyle={styles.toggleBtnText}
  84. title="Select All"
  85. onPress={selectAllContacts}
  86. />
  87. )
  88. }
  89. </SafeAreaView>
  90. </Modal>
  91. )
  92. }
  93. // contact list item
  94. function renderPickContactItem({ item: contact, index }: ContactListItem) {
  95. return (
  96. <View style={styles.listItem}>
  97. <View style={styles.userInfo}>
  98. <Text style={styles.name}>
  99. {contact.name}
  100. </Text>
  101. <Text style={styles.phone}>
  102. {contact.phone}
  103. </Text>
  104. </View>
  105. {
  106. tempSelectedContacts.indexOf(index) > -1 ? (
  107. <Button
  108. style={styles.actionBtn}
  109. textStyle={styles.removeText}
  110. title="Remove"
  111. onPress={() => removeContact(index)}
  112. />
  113. ) : (
  114. <Button
  115. style={styles.actionBtn}
  116. title="Select"
  117. onPress={() => selectContact(index)}
  118. />
  119. )
  120. }
  121. </View>
  122. )
  123. }
  124. // select contact
  125. function selectContact(index: number) {
  126. setTempSelectedContacts([
  127. ...tempSelectedContacts,
  128. index,
  129. ]);
  130. }
  131. // unselect contact
  132. function removeContact(index: number) {
  133. const existingIndex = tempSelectedContacts.indexOf(index);
  134. if (existingIndex > -1) {
  135. tempSelectedContacts.splice(existingIndex, 1);
  136. setTempSelectedContacts([...tempSelectedContacts]);
  137. }
  138. }
  139. // select all contacts
  140. function selectAllContacts() {
  141. setTempSelectedContacts(allContacts.map((a, i) => i));
  142. }
  143. // unselect all contacts
  144. function unselectAllContacts() {
  145. setTempSelectedContacts([]);
  146. }
  147. // cancel contact picking
  148. function cancelSelection() {
  149. setModalStatus(false);
  150. }
  151. // confirm & set contacts
  152. function confirmSelection() {
  153. setModalStatus(false);
  154. setSelectedIndices(tempSelectedContacts);
  155. setSelectedContacts(
  156. allContacts.filter((c, i) => tempSelectedContacts.indexOf(i) > -1)
  157. );
  158. }

Flutter

I have split the code in two parts. First modify onContactBtnPress() in home.dart

  1. Future<void> onContactBtnPress(BuildContext context) async {
  2. // open al popup
  3. showDialog(
  4. context: context,
  5. barrierColor: Colors.white,
  6. barrierDismissible: false,
  7. builder: (_) => Scaffold(
  8. // header (back icon button, header, done icon button)
  9. appBar: AppBar(
  10. elevation: 0,
  11. backgroundColor: Colors.white,
  12. brightness: Brightness.light,
  13. textTheme: TextTheme(
  14. headline6: TextStyle(
  15. color: Colors.black,
  16. fontSize: 20,
  17. fontWeight: FontWeight.bold,
  18. ),
  19. ),
  20. iconTheme: IconThemeData(
  21. color: Colors.black,
  22. ),
  23. title: Text('Select Contact'),
  24. actions: [
  25. IconButton(
  26. icon: Icon(Icons.check),
  27. onPressed: () {
  28. contactWidgetKey.currentState.onDone(context);
  29. },
  30. )
  31. ],
  32. ),
  33. body: Column(
  34. children: [
  35. // including ContactList widget (I am going to define it)
  36. Container(
  37. child: ContactList(
  38. key: contactWidgetKey,
  39. onDone: onContactSelect,
  40. selectedContactIndices: selectedContactIndices,
  41. ),
  42. ),
  43. ],
  44. ),
  45. ),
  46. );
  47. }

Secondly, I created a contact_list.dart in widgets as:

  1. import 'package:contacts_service/contacts_service.dart';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_contact_list/models/simplified_contact.dart';
  5. import 'package:permission_handler/permission_handler.dart';
  6. // import button
  7. import 'action_button.dart';
  8. class ContactList extends StatefulWidget {
  9. // callback on contact list submit
  10. final Function(List<SimplifiedContact>, List<int>) onDone;
  11. // already selected contacts
  12. final List<int> selectedContactIndices;
  13. ContactList({
  14. Key key,
  15. this.onDone,
  16. this.selectedContactIndices,
  17. }) : super(key: key);
  18. @override
  19. _ContactListState createState() => _ContactListState();
  20. }
  21. class _ContactListState extends State<ContactList> {
  22. // all contacts
  23. List<SimplifiedContact> contactList;
  24. // currently selected contacts
  25. List<int> selectedIndices = [];
  26. @override
  27. void initState() {
  28. super.initState();
  29. // copy selected indices from arguments to state
  30. selectedIndices = widget.selectedContactIndices;
  31. // get contacts from user device
  32. getContacts();
  33. }
  34. // ask user's permission
  35. Future<PermissionStatus> _getPermission() async {
  36. final PermissionStatus permission = await Permission.contacts.status;
  37. final PermissionStatus granted = PermissionStatus.granted;
  38. final PermissionStatus denied = PermissionStatus.denied;
  39. final PermissionStatus undetermined = PermissionStatus.undetermined;
  40. if (permission != granted && permission != denied) {
  41. // permission is not given, so ask user again
  42. final permissionStatusMap = await [Permission.contacts].request();
  43. return permissionStatusMap[Permission.contacts] ?? undetermined;
  44. } else {
  45. // already have permission
  46. return permission;
  47. }
  48. }
  49. // get contacts from user's device
  50. Future<void> getContacts() async {
  51. final PermissionStatus permissionStatus = await _getPermission();
  52. if (permissionStatus == PermissionStatus.granted) {
  53. // execute with permission
  54. int index = 0;
  55. final Iterable<Contact> contacts = await ContactsService.getContacts();
  56. final List<SimplifiedContact> tempContacts = [];
  57. // convert Contact list to SimplifiedContact list
  58. contacts.forEach((contact) {
  59. contact.phones.forEach((phoneData) {
  60. tempContacts.add(SimplifiedContact(
  61. index,
  62. contact.displayName,
  63. phoneData.value,
  64. ));
  65. index++;
  66. });
  67. });
  68. setState(() {
  69. contactList = tempContacts;
  70. });
  71. } else {
  72. // contact read permission is not granted (show error)
  73. showDialog(
  74. context: context,
  75. builder: (BuildContext context) => CupertinoAlertDialog(
  76. title: Text('Permissions error'),
  77. content: Text('Grant contacts permission to see the contacts'),
  78. actions: <Widget>[
  79. CupertinoDialogAction(
  80. child: Text('OK'),
  81. onPressed: () => Navigator.of(context).pop(),
  82. )
  83. ],
  84. ),
  85. );
  86. }
  87. }
  88. // on a contact selection
  89. void onContactSelect(int index) {
  90. setState(() {
  91. selectedIndices.add(index);
  92. });
  93. }
  94. // on a contact unselect
  95. void onContactRemove(int index) {
  96. int elementIndex = selectedIndices.indexOf(index);
  97. if (elementIndex > -1) {
  98. setState(() {
  99. selectedIndices.removeAt(elementIndex);
  100. });
  101. }
  102. }
  103. // select all contacts
  104. void selectAll(BuildContext context) {
  105. List<int> indexList = [];
  106. int index = 0;
  107. contactList.forEach((element) {
  108. indexList.add(index++);
  109. });
  110. setState(() {
  111. selectedIndices = indexList;
  112. });
  113. }
  114. // unselect all contacts
  115. void unselectAll(BuildContext context) {
  116. setState(() {
  117. selectedIndices = [];
  118. });
  119. }
  120. // callback on compelte
  121. void onDone(BuildContext context) {
  122. int index = 0;
  123. List<SimplifiedContact> tempSelected = [];
  124. if (contactList != null) {
  125. contactList.forEach((element) {
  126. if (selectedIndices.indexOf(index++) > -1) {
  127. tempSelected.add(element);
  128. }
  129. });
  130. // NOTE: use mobx or redux call instead
  131. // return selected contacts back to parent widget
  132. widget.onDone(tempSelected, selectedIndices);
  133. }
  134. Navigator.pop(context);
  135. }
  136. @override
  137. Widget build(BuildContext context) {
  138. // contact is not loaded
  139. if (contactList == null) {
  140. return Flexible(
  141. child: Center(
  142. child: const CircularProgressIndicator(),
  143. ),
  144. );
  145. }
  146. return Flexible(
  147. child: Column(
  148. children: [
  149. Flexible(
  150. // contact list
  151. child: ListView.builder(
  152. scrollDirection: Axis.vertical,
  153. shrinkWrap: true,
  154. itemCount: contactList?.length ?? 0,
  155. itemBuilder: (BuildContext context, int index) {
  156. SimplifiedContact contact = contactList?.elementAt(index);
  157. return Column(
  158. children: [
  159. ListTile(
  160. contentPadding: const EdgeInsets.symmetric(
  161. vertical: 2,
  162. horizontal: 18,
  163. ),
  164. title: Text(contact.name ?? ''),
  165. subtitle: Text(contact.phone),
  166. trailing: new ActionButton(
  167. isSelected: selectedIndices.indexOf(index) > -1,
  168. onRemove: () {
  169. onContactRemove(index);
  170. },
  171. onSelect: () {
  172. onContactSelect(index);
  173. },
  174. ),
  175. ),
  176. Divider(
  177. height: 1,
  178. ),
  179. ],
  180. );
  181. },
  182. ),
  183. ),
  184. // select all and unselect all buttons
  185. Container(
  186. height: 60,
  187. padding: EdgeInsets.symmetric(vertical: 15, horizontal: 50),
  188. child: contactList != null &&
  189. selectedIndices.length == contactList.length
  190. ? ElevatedButton(
  191. child: Text('Unselect All'),
  192. onPressed: () {
  193. unselectAll(context);
  194. },
  195. )
  196. : ElevatedButton(
  197. child: Text('Select All'),
  198. onPressed: () {
  199. selectAll(context);
  200. },
  201. ),
  202. ),
  203. ],
  204. ),
  205. );
  206. }
  207. }

Performance and APK size

I am creating a release version only for Android because I don't have an iOS device to test real performance. Both apps are working fine in my Simulator. For creating a release APK, we have to follow:

https://reactnative.dev/docs/signed-apk-android and https://flutter.dev/docs/deployment/android

I have set true for both enableProguardInReleaseBuilds and enableSeparateBuildPerCPUArchitecture.

For React Native my APK size is 6.6 MB to 7.5 MB

For Flutter my APK size is 5.7 MB to 6.2 MB

Clearly, Flutter APK is around 1 MB smaller in the size.

Both apps are running fine on my Android device. In debugging mode, the React Native list update was slow but it surprised me in release mode. Of course, Flutter APK is also faster in the release version. Flutter is taking almost 5-6 seconds in reading my contacts (release version). I will work on it. All other things are crazy cool. I will add more APK Analysis later.

Conclusion

I had to write almost equal code for both frameworks. I created a very simple app but all of my APKs are more than 6 MB. Clearly, both React Native and Flutter add more size than a Native app. Large lists handling in Flutter is better than React Native. Anyway, I like Flutter touches (maybe because of the Material Theme). The good thing about the Flutter is that its size is relatively small even when it is including Material Theme & Icons.



Click on banner below to Learn: PHP with Laravel for beginners - Become a Master in Laravel

About Harish Kumar

Harish, a technical core team member at www.lyflink.com with five year experience in full stack web and mobile development, spends most of his time on coding, reading, analysing and curiously following businesses environments. He is a non-graduate alumni from IIT Roorkee, Computer Science and frequently writes on both technical and business topics.

Related Articles

With the expanding market of mobile apps, the developers are struggling to maintain the code bases for Native apps. M...
5 Elite and Imperative Hybrid App Frameworks
Being in the middle of 2021, we definitely can’t ignore the significance of designing remarkable mobile apps for gain...
8 Mobile App Design Trends You’d Regret Missing In 2021
In recent times mobile internet is rising speedily than the desktop internet as huge number of users is connecting in...
Best 10 Amazing Mobile Design Trends

Full-Stack Web Development with React

Best Udemy Courses

Top Posts

Recent Posts

The Complete Web Developer Course - Build 25 Websites

Subscribe

Subscribe now and get weekly updates directly in your inbox!

Any Course on Udemy in $6 (INR 455)

Development Category (English)300x250

PHP with Laravel for beginners - Become a Master in Laravel