DEV Community

Holmes for GlopGeek

Posted on • Updated on

Best practices writing Clean Code with Flutter πŸ’» πŸ§‘πŸ»β€πŸ’»

πŸ“– Flutter is an open-source mobile application development SDK created by Google, facilitates cross-platform app creation for Android and iOS. Writing clean code with Flutter can be tricky, particularly for novices in the framework or Dart. In this article, we'll share tips for crafting clear, maintainable, and scalable Flutter code.

Why clean code with Flutter ?

To improves code readability and maintainability, making it easier for other devs to understand the purpose and functionality of our code

Use meaningful and easy pronounceable variable names

Clear and meaningful variable names make your code more readable and understandable. Facilitating comprehension for both yourself and others who may read it later


//! BAD 

final yyyyMMddstring = DateFormat('yyyy/MM/dd').format(DateTime.now());

//* GOOD 

final currentDate = DateFormat('yyyy/MM/dd').format(DateTime.now());

Enter fullscreen mode Exit fullscreen mode

Use Future.wait to make concurrent API calls

By using Future.wait, you can initiate multiple async tasks at the same time. Thereby reducing the overall execution time


//! BAD 

Future callMultipleApis() async { 
  await getUserInfo(); 
  await getLocations();
} 

//* GOOD 

Future callMultipleApis() async { 
  await Future.wait([
    getUserInfo(), 
    getLocations(), 
  ]);
}

Enter fullscreen mode Exit fullscreen mode

Use searchable names


//! BAD 

Future.delayed(const Duration(minutes: 30), () { 
  debugPrint('some logic here');
}); 

//* GOOD 

const MINUTES_DURATION = 30;

Future.delayed(const Duration(minutes: MINUTES_DURATION), () { 
  debugPrint('some logic');
}); 

//----OR---- 

const HALF_AN_HOUR = Duration(minutes: 30);

Future.delayed(HALF_AN_HOUR, () { 
  debugPrint('some logic');
});

Enter fullscreen mode Exit fullscreen mode

Use explanatory variables

First, we have class Person and saveInfo() in that class:


class Person { 
  Person({ 
    required this.fullName, 
    required this.age, 
    required this.email, 
  }); 

  final String fullName; 
  final String age; 
  final String email; 

  void saveInfo() { 
    final fullName = this.fullName; 
    final age = this.age; 
    final email = this.email; 

    debugPrint(''' 
      'Fullname': $fullName 
      'Age': $age 
      'Email': $email 
    ''')};
}

Enter fullscreen mode Exit fullscreen mode

and


const info = ['John', '31', 'john@dev.to']; 

Enter fullscreen mode Exit fullscreen mode

how to use explanatory variables:


//! BAD 

final infoPerson = Person(fullName: info[0], age: info[1], email: info[2]);

infoPerson.saveInfo(); 

//* GOOD 

final fullName = info[0];
final age = info[1];
final email = info[2];
final infoPerson = Person(fullName: fullName, age: age, email: email)
..saveInfo();

Enter fullscreen mode Exit fullscreen mode

Avoid mental mapping

We have a list that contains staff names


const staffs = ['Holmes', 'Dane', 'Dyno', 'Maker'];

Enter fullscreen mode Exit fullscreen mode

and we perform some actions with this list


//! BAD 

for (final n in staffs) { 
  doSomething(); 
  //... 
  //... 
  //... 
  //... 
  //... 
  //... 
  //"n" is defined as what? 
  doStuff(n);
} 

//* GOOD 

for (final staffName in staffs) { 
  doSomething(); 
  //... 
  //... 
  //... 
  //... 
  //... 
  //... 
  doStuff(staffName);
}

Enter fullscreen mode Exit fullscreen mode

Prefer default parameters over short-circuiting or conditionals

Default arguments are generally preferable to short-circuiting. However, it's important to note that using default arguments means that your function will only assign default values to undefined arguments.


//! BAD

void logErrorWithDefaultInternal({String? errorText}) { 
  print(errorText ?? 'Internal Server Error');
} 

//* GOOD

void logErrorWithDefaultInternal({String errorText = 'Internal Server Error'}){
   print(errorText);
}

Enter fullscreen mode Exit fullscreen mode

Single responsibility principle

We have:


const numbers = [33, 10, 99, 275, -100, 9000, -300];

Enter fullscreen mode Exit fullscreen mode

We create the function that return a list contains number is odd and positive:


//!BAD 

List getGenericNumbers() { 
  final result = numbers.where((number) { 
    if (number.isOdd && number > 0) { 
      return true; 
    } 
    return false; 
  }).toList(); 
  return result;
} 

//!GOOD 

bool checkNumberIsOddAndPositive(int number) { 
  return number.isOdd && number > 0;
} 

List getListPositiveOddNumbers() { 
  final result = numbers.where(checkNumberIsOddAndPositive).toList();
  return result;
}

Enter fullscreen mode Exit fullscreen mode

Don't make duplicate code

Avoid duplicate code as much as possible. Duplications can complicate maintenance and updates, leading to inconsistencies. Instead, create a solid abstraction that can handle multiple scenarios with a single function or module. However, ensure that the abstraction is well-designed and follows the SOLID principles.

First, we have 3 classes Person, Student, Teacher. Student and Teacher both extend Person


class Person {}

class Student extends Person {
  //properties...
}

class Teacher extends Person {
  //properties...
}

Enter fullscreen mode Exit fullscreen mode

and then, how does duplicate code occur


//! BAD

Widget _buildAvatarStudent() {
  return Image.asset('assets/images/student.png');
}

Widget _buildAvatarTeacher() {
  return Image.asset('assets/images/teacher.png');
}

//! GOOD

Widget _buildAvatar(Person person) {
  String image = 'assets/images/placeholder.png';
  if (person is Student) {
    image = 'assets/images/student.png';
  } else if (person is Teacher) {
    image = 'assets/iamges/teacher.png';
  }
  return Image.asset(image);
}

//----------DART 3------------

Widget _buildAvatar(Person person) {
  String image = switch(person) => {
    Student() => 'assets/images/student.png',
    Teacher() => 'assets/images/teacher.png',
    _ =>  'assets/images/placeholder.png'
  };

  return Image.asset(image);
}

Enter fullscreen mode Exit fullscreen mode

Hope y'all enjoyed :)

Top comments (0)