27
loading...
This website collects cookies to deliver better user experience
lib
and test
folder. And lastly you will learn how to setup a basic monorepo for your future projects, however I am not going to tackle about monorepos in this post on what are its pros and cons. /dev/personal-projects
so I'll cd
into it.mkdir
terminal command.mkdir flutter-fake-data-factory-pattern
cd flutter-fake-data-factory-pattern && git init
packages
and create a Flutter package inside that directory and call it app_core
mkdir packages
cd packages
flutter create --template=package app_core
pubspec.yaml
file# Add this so our local package will not be published to pub.dev
publish_to: "none"
dependencies:
freezed_annotation: ^0.14.2
json_annotation: ^4.0.1
faker: ^2.0.0
uuid: ^3.0.4
dev_dependencies:
build_runner: ^2.0.5
freezed: ^0.14.2
json_serializable: ^4.1.3
cd app_core && flutter pub get
cd ../..
mkdir apps
flutter create client_app
pubspec.yaml
app_core:
path: ../../packages/app_core
app_core
import 'package:faker/faker.dart';
import 'package:uuid/uuid.dart';
abstract class ModelFactory<T> {
Faker get faker => Faker();
/// Creates a fake uuid.
String createFakeUuid() {
return Uuid().v4();
}
/// Generate a single fake model.
T generateFake();
/// Generate fake list based on provided length.
List<T> generateFakeList({required int length});
}
faker
is the Faker
instance that got instantiated, it is where we grab all random data of any type.createFakeUuid
method which is responsible for generating us a fake uuid out of the fly. Typically uuids are used when you have a data provider like Firestore or NoSQL databases like MongoDB, or a relational database that has a primary key type of uuid. But you can switch this up any however you want it to be.generateFake
is responsible for creating a single data model that requires implementation details; We'll have the implementation details for this in the factory class that extends this abstract class.generateFakeList
will return a list, implementation details are not concerned here as well and it is being handled in the factory class the implements this abstract. It will just simply return a list of what is returned from generateFake
method.User
and a Tweet
data model.import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.g.dart';
part 'user.freezed.dart';
@freezed
class User with _$User {
factory User({
required String id,
required String name,
required String email,
int? age,
@Default(false) bool suspended,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
import 'package:freezed_annotation/freezed_annotation.dart';
part 'tweet.g.dart';
part 'tweet.freezed.dart';
@freezed
class Tweet with _$Tweet {
factory Tweet({
required String id,
required String content,
@Default([]) List<String> replies,
@Default(0) int likes,
@Default(0) int retweets,
}) = _Tweet;
factory Tweet.fromJson(Map<String, dynamic> json) => _$TweetFromJson(json);
}
build_runner
to let it generated the boilerplate code for us. Run it via this terminal command (You have to be in app_core
directory otherwise this won't run):flutter pub run build_runner build --delete-conflicting-outputs
freezed
package, read more about it from this tutorial that I wrote.ModelFactory
import 'package:app_core/app_core.dart';
class UserFactory extends ModelFactory<User> {
@override
User generateFake() {
return User(
id: createFakeUuid(),
email: faker.internet.email(),
name: '${faker.person.firstName()} ${faker.person.lastName()}'.trim(),
age: faker.randomGenerator.integer(25),
suspended: faker.randomGenerator.boolean(),
);
}
@override
List<User> generateFakeList({required int length}) {
return List.generate(length, (index) => generateFake());
}
}
import 'package:app_core/app_core.dart';
class TweetFactory extends ModelFactory<Tweet> {
@override
Tweet generateFake() {
return Tweet(
id: createFakeUuid(),
content: faker.lorem.words(99).join(' '),
likes: faker.randomGenerator.integer(5000),
retweets: faker.randomGenerator.integer(2500),
replies: List.generate(
faker.randomGenerator.integer(105),
(index) => faker.lorem.words(120).join(' '),
),
);
}
@override
List<Tweet> generateFakeList({required int length}) {
return List.generate(length, (index) => generateFake());
}
}
/// A new instance of [UserFactory]
final userFactory = UserFactory();
/// Generates a [User] data model
final fakeUser = userFactory.generateFake();
/// Will generate a [List] of 10 [User] data model
final fakeUsers = userFactory.generateFakeList(10);
Faker
package and applied abstraction to avoid writing up the same code again and again for other data models to generate fake data. This would come quite so handy when we write a lot of unit tests with mocking as we don't have to create a data model instance and define each properties for each test case. And lastly we can also make use of this in the UI for placeholder data when the API is not yet ready to integrate with the frontend app.