Every evident application requires structured organization, clean coding practices, and manageable components. Flutter as a framework is powerful and flexible, but leveraging its potency requires a good understanding of available Flutter tools. The role of architectural tools is vital in simplifying development and boosting productivity.
The architecture of a Flutter application is determined by how its codebase is organized. This usually involves grouping similar functionalities together and creating a data flow hierarchy that's easy to understand and manipulate. A well-organized app ensures maintainability, scalability, and developer efficiency.
The base Flutter framework provides rudimentary state management solutions, but for anything more complex than simple apps, we generally consider external libraries. In this blog, we are going to explore Flutter GetIt, Flutter AutoRoute, and Flutter Injectable, which are some useful architectural tools.
As your Flutter project grows, you need scalable solutions to manage dependencies, navigate routes, and organize your codebase. That’s where advanced Flutter tools come into play.
In a larger app, managing dependencies manually becomes cumbersome. Flutter GetIt and Flutter Injectable provide solutions for automatic dependency injection and easy management with minimal boilerplate code. Similarly, for complex applications with various screens and navigation paths, the default Navigator class might not suit best, and here, Flutter AutoRoute shines with its highly flexible and strongly typed routing solution.
GetIt is a simple service locator for Dart and Flutter projects, highly inspired by Splat. It can be used as an alternative to Provider or InheritedWidget to access objects such as services and data models from your UI.
Flutter GetIt provides a way for your app's UI components to access your business logic classes, taking a straightforward, manageable approach to app architecture. This approach decouples the interface from the implementation, allowing us to access instances from anywhere in the app while maintaining encapsulated classes.
The main concept behind GetIt is the use of service locators for your App's logic, thus promoting a clean, efficient architecture.
To keep the widgets free from direct dependencies, you need to position your app's logic in separate classes. With that, your code becomes easy to maintain, test, and organize.
GetIt works as a standalone service locator for Flutter that helps in managing dependencies in a decoupled architecture. It allows us to access these objects from our UI without requiring a BuildContext. This way, it also provides easy access to our business layer.
Here's a basic way to use GetIt to serve dependencies:
1 final getIt = GetIt.instance; 2 3 void setup() { 4 getIt.registerSingleton<AppModel>(AppModel()); 5 // Alternatively you could write it if you don't like global variables 6 GetIt.I.registerSingleton<AppModel>(AppModel()); 7 } 8 // Later in the code, you can access your AppModel class from anywhere like this 9 MaterialButton( 10 child: Text("Update"), 11 onPressed: getIt<AppModel>().update // given that your AppModel has a method update 12 ), 13
The setup for GetIt is quite straightforward. At your app’s start, you register all objects that you might have to access at a later point. GetIt supports singleton and factory registrations for your classes.
GetIt provides support for asynchronously initializing your Singleton classes at the startup. This can be especially useful when you have to perform time-consuming initializations. We can control this async startup behaviour using mechanisms provided by GetIt.
Understanding the theoretical aspects is quite helpful in grasping the core concepts. But nothing quite replaces the real-world use case to cement our understanding. In a real Flutter app, you might typically find GetIt being used to provide dependencies for different classes throughout the app, making them available for your UI widgets.
Dependency injection forms the heart of any complex software system with multiple interacting classes, and Flutter Injectable helps you manage this in a much more structured manner.
Flutter Injectable is an automatic dependency injection generator inspired by Angular DI, Guice DI and Dagger DI. The main aim of Injectable is to generate all the boilerplate code you would normally need to write to wire up your dependencies manually, making dependency management convenient and fuss-free.
Injectable makes it easier to manage your dependencies for different development environments and test cases.
Flutter Injectable works seamlessly with Flutter GetIt. It leverages GetIt functionalities to generate and provide dependencies throughout your app.
Injectable automates the generation of the required GetIt initialization code, reducing boilerplate and keeping your code clean while providing an easy strategy for managing dependencies.
Setting up Injectable is simple. You need to add a couple of dependencies to your pubspec.yaml file:
Then, you need to define a setup function, which you'll annotate with @injectableInit. Finally, you need to run the generator using build_runner.
For instance, you would include Injectable in your project like so:
1 dependencies: 2 get_it: 3 4 dev_dependencies: 5 injectable_generator: 6 build_runner: 7
Next, write a setup function and annotate it with @injectableInit.
1 import '<FILE_NAME>.config.dart'; 2 3 final getIt = GetIt.instance; 4 5 @InjectableInit( 6 initializerName: 'init' 7 ) 8 configureDependencies() => getIt.init(); 9
Finally, run the generator:
1 flutter packages pub run build_runner build 2
Let's now look at a real-world example. Consider you have a Service which depends on ApiProvider. Here's how you would implement it using Injectable:
1 @injectable 2 class ServiceA { 3 final ApiProvider apiProvider; 4 5 ServiceA(this.apiProvider); 6 } 7 @singleton 8 class ApiProvider {} 9
In this example, ServiceA is set up to be injectable, and it depends on ApiProvider. This dependency is automatically handled by Injectable. The singleton annotation also tells Injectable that ApiProvider is a singleton.
Therefore, in this section, we have explored how Injectable can be set up and used to manage dependencies through the use of annotations and auto-generated code. Now, we shall move on to the next Flutter architectural tool, AutoRoute.
AutoRoute is a Flutter package that simplifies navigation setup with advanced features like route guards and custom transitions.
With Flutter AutoRoute, you can manage your app's navigation seamlessly with zero boilerplate and strong typing. It leverages Dart's code generation utility in creating route management code, leaving you to worry less about routing and focus more on the actual logic of your Flutter app.
A robust navigation system is foundational to any application that has multiple screens. The significance of AutoRoute comes into the picture in scenarios where:
As your application grows more complex, maintaining the navigation code and passing data between routes can become hard. AutoRoute simplifies this by generating code for you, taking care of the complexities behind the scenes.
AutoRoute provides strong typing for route parameters. This eliminates potential runtime errors resulting from wrong argument types, leading to safer, more reliable code.
AutoRoute relies on code generation, which means you define your router and annotate it with @AutoRouter. For each of your screens, you define a PageRoute and list its route parameters. AutoRoute then generates a router class for you. The setup generally includes:
Routes are defined essentially using the @AutoRoute annotation. For every auto route, you specify the widget that should be loaded and optionally the route name and parameters.
1 @AutoRoute(page: BooksListScreen) 2 class BooksListRoute extends PageRouteInfo {} 3
Here's how you would navigate to BooksListScreen:
1 context.router.push(BooksListRoute()); 2
To sum up, AutoRoute is a powerful tool that makes Flutter navigation much easier while enabling advanced features like automatic deep linking, nested routing, and routing guards. Now with this basic understanding, you can easily experiment with and explore the power of AutoRoute in your Flutter apps.
The power held by GetIt, Injectable, and AutoRoute truly shines when combined. They collectively form an efficient architecture that eliminates boilerplate code, handles dependency injection and streamlines navigation.
These libraries are designed to work in harmony, each complementing the functionalities of the other:
When used together, these tools create an ecosystem that boosts Flutter development productivity and maintainability.
Let's consider an application where you have various screens, each requiring different services or dependencies. With these three libraries combined, we can set up our application as follows:
1 // Setting Up GetIt and Injectable 2 final getIt = GetIt.instance; 3 4 @InjectableInit( 5 initializerName: 'init', 6 ) 7 void configureDependencies() => getIt.init(); 8 9 // Registering Some Dependencies 10 @injectable 11 class BookRepository { 12 final ApiProvider apiProvider; 13 14 BookRepository(this.apiProvider); 15 } 16 17 @singleton 18 class ApiProvider {} 19 20 // Setting Up AutoRoute 21 @AutoRouterConfig() 22 class AppRouter extends $AppRouter { 23 @override 24 List<AutoRoute> get routes => [ 25 AutoRoute(page: BookListRoute.page), 26 AutoRoute(page: BookDetailsRoute.page), 27 ]; 28 } 29 30 // With the above setup, within your widgets, you can now access dependencies 31 // and navigate between screens effortlessly: 32 33 class BookListScreen extends StatelessWidget { 34 final BookRepository repository = getIt<BookRepository>(); 35 36 // Fetch and display books using bookRepository 37 // Navigate to details screen 38 void seeDetails(Book book) { 39 context.router.push(BookDetailsRoute(book: book)); 40 } 41 ... 42 } 43 44 class BookDetailsScreen extends StatelessWidget { 45 BookDetailsScreen({@pathParam required this.bookId}); 46 final int bookId; 47 48 final BookRepository repository = getIt<BookRepository>(); 49 50 // Fetch and display book details using bookId and bookRepository 51 ... 52 } 53
In this example, Flutter GetIt and Flutter Injectable are used together to manage dependencies efficiently. AutoRoute, on the other hand, handles the navigation from BookListScreen to BookDetailsScreen in a type-safe manner.
By integrating all three tools, we achieve a streamlined, clean, and easy-to-maintain project structure.
Having a profound knowledge of the Flutter ecosystem and mastering the tools that play a significant role in it can make the difference between a good developer and a great one.
Each Flutter tool we've covered offers its unique edge:
Provides a straightforward approach for managing dependencies across your Flutter application, allowing you easy access to instances of your classes across the widget tree.
Reduces the boilerplate associated with setting up dependency injection, and makes your code cleaner and easier to manage.
Simplifies and streamlines navigation setup, provide typesafe argument passing between routes, and ease in handling complex routing scenarios.
Together, they form a trio that saves you significant time and effort when organizing and structuring your Flutter applications.
While there are ample benefits to using these three libraries, one should be aware of the considerations involved:
Learn to adapt and pick the right set of tools that suits the demands of your particular project. The field of software development is ever-evolving, and staying open-minded to new tools and practices is a key trait for a successful developer.
As we've seen, Flutter GetIt, Flutter Injectable, and Flutter AutoRoute together make for a powerful toolkit that can significantly enhance your productivity when developing Flutter applications. However, always remember that these are just tools to aid you. The ultimate determinant of your software's quality is the level of understanding, logic, creativity, and problem-solving skills you bring as a developer.
Stay curious and keep exploring the Flutter world! 💙
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.