Cyberithub

Understanding Singleton Pattern in Flutter Using Best Examples

Advertisements

When first entering the world of Flutter or App development, we frequently opt to create a single instance of a class. However, as our applications become more complex, we occasionally create or have a tendency to create multiple instances of a class, and like other developers, we neglect to carefully manage each instance.

As a result, managing each instance of a class is extremely challenging and complex. However, it is still possible to create just one instance of a class, which is one of the most contentious issues in managing class instances.

 

Understanding Singleton Pattern in Flutter Using Best Examples

Understanding Singleton Pattern in Flutter Using Best Examples

Also Read: What are MVC, MVP, and MVVM Design Pattern in Flutter ?

Therefore, before learning the Singleton pattern's learning process, it is important to understand what Singleton is and some of its advantages and disadvantages.

 

Singleton Pattern

Accordingly, Singleton is an OOP (object-oriented programming) design pattern that guarantees both a single instance of the class and a global point of access to it. Singleton assists in the resolution of a number of other problems in addition to the primary single instance problem, including:-

  • Additionally, it is simple to access a single class instance.
  • You can instruct the code when a class should be instantiated as a developer, controlling the instantiation of a class.
  • As we all know, Singleton will only provide one instance of a class, but if someone wants to create additional instances, it will also limit those additional instances.
  • A global variable in Singleton can get access to the newly created instance.

Singleton is one of the simplest patterns, but also one of the most perplexing and misunderstood ones. For instance, if a system contains many printers, there will only be one printer spooler, along with a file manager and a file system.

 

Implementation

You will learn primarily how to use and how not to use the Singleton pattern in this section. You will also discover some additional scenarios in which you can create, grant access to, and call the instance. Well, the below example is the simplest code, which implements the Singleton pattern:-

class SingletonExample{
SingletonExample._();
static final instance = SingletonExample._();
}

You can see from the code above that the class only has one instance and that another instance cannot be made because the constructor has been designated as a private constructor. As a result, you can use it by requesting a SingletonExample class instance by calling "SingletonExample.instance". Similar to that, the following are some of the most typical illustrations of the Singleton pattern:-

RaisedButton(
onPressed:()=>FirebaseAuth.instance.signInAnonymously(),
child:Text("Sign in as a Guest"),
);

You can see that Firebase has a private constructor for Firebase classes as well as an internal Singleton pattern implementation in the aforementioned function.

 

Example

FirebaseFirestore.instance.doc("");
FirebaseFunctions.instance.httpsCallable('');
FirebaseMessaging.instance.deleteToken();

Similar to that, the code below won't run if you try it. Because we are using a single authentication service, you will receive a compile-time error; therefore, you must use the classes to create a single instance of it before using the class functions.

final authOne = FirebaseAuth();
final authTwo = FirebaseAuth();

 

Solution

final authOne = FirebaseAuth.instance;
final authTwo = FirebaseAuth.instance;

Additionally, we are aware that widget trees are used to build Flutter applications. As a result, the Singleton pattern makes it easy to obtain the desired object from any widget and any other widget. The singleton pattern has many benefits, but it also has some drawbacks.

 

Drawbacks/Disadvantages 

The disadvantages of using the Singleton pattern are covered in this section. We are aware that nothing is perfect and that most things in existence have both advantages and disadvantages. Even though Singleton has drawbacks, the pattern itself is not flawed. By the way, it might be, but not in all aspects. Some of the common drawbacks of singleton patterns are:-

a) Although singleton patterns are designed to manage a single instance, it is difficult to test the instance in all possible circumstances. For example:-

class AuthService{
Future<void> signOut()=>FirebaseAuth.instance.signOut();
}

In the above function, it is not possible to know whether the function is called or not. Testing example for the above code:-

test('signout function test',()async{
final authService = AuthService();
await authService.signOut();
});

The problem could be resolved by injecting the primary FirebaseAuth as a dependent. Alternately, you can simply design a mock response in your test that produces the desired result.

class AuthService{
const AuthService(this._auth);
final FirebaseAuth _auth;
Future<void> signOut()=>_auth.signOut();
}

b) Singleton is implicitly dependent. For example, it does not express itself directly, but it understands what concepts such as functions and variables are involved.

c) Singleton patterns are mostly or entirely lazy in their initialization. When using the Singleton pattern, initializing specific objects can be costly in terms of memory and time, and the app may also take a long time to load. For example:-

class AuthService{
AuthService._(){
print('AuthService work has been started');
}
static final instance = AuthService._();
}
void main(){
final AuthService = AuthService.instance;
}

In the above example, you can see that we have initialized the AuthService class, as the main function is triggered while building the app. In this case, it will take the AuthService class and process all the functions and methods of AuthService while building the app, and thus it will affect the build and load times of the app. Similarly, to solve this situation, we can use the "late" modifier to defer the object's initialization until it is used.

void main(){
late final authService = AuthService.instance;
authService.logResult();
}

d) Singleton instances don’t end when the application or process ends. So, it takes a lot of memory, and we need to dispose of the lifecycle of the instance after the process ends.

e) Currently, as Flutter users, we don't need to know about thread safety, but in multithreaded languages, we need to be more careful about accessing data from the instance of the class. If they are sharing mutable data, we also need to put some synchronization mechanisms in place.

 

Singleton Class Alternatives

You've now read about the advantages and disadvantages of using Singleton in the article above. The requirements of the project you are building will determine how you should use Singleton. Although using Singleton is a great strategy, if Singleton is unable to meet your project's needs, you can use some of the alternatives listed below:

a) Riverpod

To make it simple to create providers as global variables, Flutter's riverpod state management was designed in this manner.

final authServiceProvider = Provider<>((){
return AuthService(FirebaseAuth.instance);
});

Similarly, you can access it using:-

final authService = ref.read(authServiceProvider);

 

b) get_it

We can register your class as a lazy singleton when the app is first launched thanks to the package "get_it", which the Flutter developers developed in response to their observation.

void main(){
final getIt = GetIt.instance;
getIt.registerLazySingleton<AuthService>(
()=>AuthService(FirebaseAuth.instance),
);
runApp(const MyApp());
}

You can access it as follows after initializing it in the main method of Flutter apps:-

final authService = getIt.get<AuthService>();

 

Conclusion

The topic of how to create a single instance of a class and gain access to it has largely been mastered in this article. Along with that, we learned about the advantages and disadvantages of using this strategy, as well as some common errors and examples that we will encounter when using this pattern.

Leave a Comment