A Native iOS Developer's Perspective on React Native vs. Flutter (Part 3)
React Native and Flutter comparison. How to choose the right platform.
As you all are probably aware that the
new version of Dart 2.7 was recently released. This version introduces one particularly interesting feature called "extension methods". It allows us to extend existing classes with a new functionality. As a basic demonstration of this new feature, we can extend the List
class
extension SafeLast<T> on List<T> {
T get safeLast {
return isEmpty ? null : last;
}
}
main() {
final list = ["test", "test1"];
print(list.safeLast);
}
This is quite a basic example and doesn't do much. Now let's try to solve a real-life problem I am constantly stumbling upon. I am talking about implementing copyWith
method on @immutable
classes. This is a very popular pattern used to copy a class with some modifications. Unfortunately, it requires a lot of boilerplate code. For example:
IconThemeData copyWith({ Color color, double opacity, double size }) {
return IconThemeData(
color: color ?? this.color,
opacity: opacity ?? this.opacity,
size: size ?? this.size,
);
}
This example is not that complex but still requires some boilerplate code. And as soon as you start adding new fields to your class, it gets messy pretty quickly. With more complex examples it can get out of control quite easily. This is something that the Dart community is aware of but the issue still exists.
With the new extension methods, it’s possible to define a class method aside from the main class implementation, in a separate file. This means that we can employ the code generation functionality provided by the Dart team to create this method for us.
I won’t go into details of creating a code generation package as it is a bit of a tricky topic to cover. Let's just focus on the basic idea and the actual code that generates it for us. The code generation package consists of two parts. The first part is used to mark a class with an annotation. It has only a single empty annotation class inside. And the second part contains the code generation itself, which generates code for classes marked with this annotation.
final constructor = element.unnamedConstructor;
constructorInput
to be used in the signature for our copyWith
method. And another paramsInput
for assigning new values to the copied class if they are provided:final constructorInput = fields.fold(
"",
(r, field) => "$r ${field.type} ${field.name},",
);
final paramsInput = fields.fold(
"",
(r, field) => "$r ${field.name}: ${field.name} ?? this.${field.name},",
);
return '''extension ${classElement.name}CopyWithExtension on ${classElement.name} {
${classElement.name} copyWith({$constructorInput}) {
return ${classElement.name}($paramsInput);
}
}''';
This is basically it.
dependencies:
copy_with_extension: ^1.4.0
dev_dependencies:
build_runner: ^1.10.3
copy_with_extension_gen: ^1.4.0
@CopyWith
annotation from the
copy_with_extension package.@CopyWith()
class BasicClass {
final String id;
BasicClass({this.id});
}
part
annotation to tell Dart that you are going to use a part
file:part 'your_file_name.g.dart';
flutter pub run build_runner build
I am glad that the Dart team is actively improving the language itself. With this update, we can make the development process much more comfortable and our architectures cleaner. I am also very excited about the upcoming null safety which should help us to significantly improve the quality of the code. And, of course, if you want to improve this library, feel free to fire off a pull request. I will be happy to help you with this.
React Native and Flutter comparison. How to choose the right platform.
My experience with Flutter. Overview and initial impressions.