Using Stacked Architecture in Flutter App
Tags: flutter, dart, mobile, architecture
Published: 2021-11-01 14:13:37

The concept of Architecture is one of the most diverse topics in the world of programming today. A lot of solutions have been developed, all aimed at fixing one flaw or the other. App Architecture is one of the topics where personal choice has been advised to come in. If one meets your needs efficiently and enables you to release good, quality code, then go for it.

This article aims to introduce you to Stacked Architecture, an architecture that offers clean, efficient solutions to architecture your next Flutter app. From dependency Injection to the right out of the box services to layered structures that follow the principles of [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) and a whole lot. To get started on development with Flutter, check out this [article](https://flutter.dev/docs/get-started/install) from the Flutter team.

[Stacked Architecture,](https://pub.dev/packages/stacked) developed by Dane Mackier from [FilledStacks](https://www.filledstacks.com/), is an MVVM Architecture solution that offers a lot of components for building a highly scalable, testable, maintainable, and usable Flutter application. From

- State Management,
- Dependency Injection (Dependency Inversion),
- Navigation Abstraction,
- Services (Out-of-the-box),

 And a whole lot more. All of these are offered by the Stacked Architecture.

## State Management

Handling State in Flutter application using Stacked couldn’t be much better. It provides widgets and constructors which control how the state is being passed and managed in the application. Let’s dive into it using a sample app.
 
 The first thing is to create a new Flutter project, 
 
```
     flutter create intro_to_stacked
```

This command generates all the files and folders, which lays the foundation of the flutter app. 
Next is to add [stacked](https://pub.dev/packages/stacked) as a dependency in the pubspec.yaml file

```
    stacked: ^2.2.7
```

Next is to set up the basic folder structure for the sample app. Go to the lib folder and create a new folder titled home. Inside this, create two files named home_view.dart and home_viewmodel.dart. The homeViewModel file would be responsible for managing the state of the homeView, which is shown to the user. Clean Architecture, which says the view should contain zero logic, is strictly adhered to; the logic resides in the ViewModel.

The view is bound to the ViewModel using the ViewModelBuilder widget, which Stacked offers. This widget takes in 2 parameters which are 

- builder, which builds the UI that has its state in the ViewModel
- viewModelBuilder, which is the function that would return the ViewModel for this widget.

```
    import 'package:flutter/material.dart';
    import 'package:stacked/stacked.dart';
    import 'package:intro_to_stacked/ui/views/home/home_viewmodel.dart';

    class HomeView extends StatelessWidget {
     const HomeView({Key? key}) : super(key: key);
     @override
     Widget build(BuildContext context) {
      return ViewModelBuilder<HomeViewModel>.reactive(
       viewModelBuilder:()=> HomeViewModel(),
       builder: (context, model, child) {
        return Scaffold();
       },
      );
     }
    }
```

The ViewModelBuilder offers two constructors that deal with how the state is managed and subsequently update the UI for the application. They are the .reactive() and the .nonReactive() constructors. The .reactive() rebuilds the UI every time notifyListeners is called in the ViewModel. However, the .nonReactive() constructor rebuilds the UI once, after which it doesn’t rebuild on subsequent calls in the ViewModel. 

With that, we’ve set up the view file, moving to the ViewModel, we create a class that extends the BaseViewModel class that stacked provides. The BaseViewModel class provides functionalities used for maintaining the state in the application. In the ViewModel, we have a String that says Stacked is cool (definitely true). This String is the state currently offered by the ViewModel and which would need to be displayed on the screen.

```
    import 'package:stacked/stacked.dart';
    
    class HomeViewModel extends BaseViewModel {
     final String _declaration = 'Stacked is soo cool';
     String get myDeclaration => _declaration;
    
    }
```

Back to the view, to use this state in our builder, we use the model offered by the builder connecting the state in the ViewModel to the UI screen which uses it. 

```
    Scaffold(
         body: Center(
          child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
           children: [
            const Text('Stacked Introduction'),
            Text(model.myDeclaration),
           ],
          ),
         ),
        );
```

On updating the state in the application, let’s have a function that updates the text and calls notifyListeners after. We would link this function to the onPressed of a TextButton. 

```
    //In View(home_view.dart)
     TextButton(
      onPressed: () => model.updateDeclaration(),
      child: const Text('Update Text'),
     )
    
    //In ViewModel (home_viewmodel.dart)
     void updateDeclaration() {
      _declaration = 'I say Stacked is sooo cool';
      notifyListeners();
     }
```
The notifyListeners call informs the view to rebuild as there has been a state change in the ViewModel.

For a deep dive into the state management solution offered, check out this [article](https://www.filledstacks.com/post/flutter-state-management-with-stacked/) provided by the FilledStacks team. 


## How about Navigation and Dependency Injection?

Stacked provides a direct out the box to navigation without context. Using the NavigationService it gives, we can smoothly declare our navigation in our ViewModel and use them in the view. This NavigationService allows for a clean UI without the interference of logic or complex routing codes. With the aid of the build runner and stacked generator, we can auto-generate routes and smoothly perform navigation.

Let’s get to it.

The first is to add [build_runner](https://pub.dev/packages/build_runner) and [stacked_generator](https://pub.dev/packages/stacked_generator) to the dev_dependencies section in your pubspec.yaml file. Also, add the [stacked_services](https://pub.dev/packages/stacked_services) to the dependency section.

```
    dependencies:
     stacked: ^2.2.7
     stacked_services: ^0.8.15
    
    dev_dependencies:
     build_runner: ^2.1.4
     stacked_generator: ^0.5.5
```

Run the `flutter pub get` command to get the files locally for use.

Next is to create the new view that we would be routing to. Create a folder inside views and name it profile. Inside this folder, create two files, the profile_view.dart and profile_viewmodel.dart. 

In profile_viewmodel.dart, create a class named ProfileViewModel which extends BaseViewModel. In the ViewModel, we declare a String that the app would display on the view.

```
    import 'package:stacked/stacked.dart';
    
    class ProfileViewModel extends BaseViewModel {
     String _pageName = 'This is the Profile Page';
     String get pageName => _pageName;
    }
```

In profile_view.dart, create the stateless widget which returns the ViewModelBuilder and bind it to the ProfileViewModel. This view would contain a similar setup to the HomeView; it would display a Profile View text.

```
    import 'package:flutter/material.dart';
    import 'package:intro_to_stacked/ui/views/profile/profile_viewmodel.dart';
    import 'package:stacked/stacked.dart';
    class ProfileView extends StatelessWidget {
     const ProfileView({Key? key}) : super(key: key);
     @override
     Widget build(BuildContext context) {
      return ViewModelBuilder<ProfileViewModel>.reactive(
       viewModelBuilder: () => ProfileViewModel(),
       builder: (context, viewModel, child) {
        return Scaffold(
         body: Center(
          child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
            Text(viewModel.pageName),
           ],
          ),
         ),
        );
       },
      );
     }
    }
```

The next thing is to create a folder in lib named app. Inside this folder, create a new file titled app.dart. In this file would all the code needed for routing along with the dependencies.

Create a class named AppSetup and annotate it with the StackedApp; this class houses the annotation; it does nothing else asides that. The main focus is the annotation as it takes in two parameters, routes, and dependencies.


 ```   
    @StackedApp()
    class AppSetup {
     /** This class has no puporse besides housing the annotation that generates the required functionality **/
    }
 ```   

Stacked offers three different types of routes which determines the mode of transition

- MaterialRoute
- CupertinoRoute
- CustomRoute

Using the MaterialRoute, we register the Views present in the app that would use the NavigationService.

In addition to the routes, we would declare the dependencies we would be using in the app, enabling Stacked to create the locator file that would handle the injection of the declared types. We would register the NavigationService as a LazySingleton, which means it won’t be initialized until used for the first time in the app.

```
    import '../ui/views/home/home_view.dart';
    import '../ui/views/profile/profile_view.dart';
    import 'package:stacked/stacked_annotations.dart';
    @StackedApp(
     routes: [
      MaterialRoute(page: HomeView, initial: true),
      MaterialRoute(page: ProfileView)
     ],
     dependencies: [
      LazySingleton(classType: NavigationService),
     ],
    )
    class AppSetUp {}
```

As the HomeView is the first page we contact, we set the initial parameter to true. 

After setting this up, run the flutter command to auto-generate the needed files for routing.

```
    flutter pub run build_runner build --delete-conflicting-outputs
```
This command would generate the files needed for navigation and dependency injection setup. 

The last step in this process is to convert the main top-level function of the app into a Future and await the setupLocator function in the app.locator.dart file generated by the command used earlier.

```
    Future main() async {
     WidgetsFlutterBinding.ensureInitialized();
     await setupLocator();
     runApp(MyApp());
    }
```

## How do we use it?

To make use of the navigation in the HomeView, we create a function in the HomeViewModel that would perform the navigation. First, we declare the locator which would give us access to the Navigation Service we registered earlier.

```
    import 'package:intro_to_stacked/app/app.router.dart';
    import '../../../app/app.locator.dart';
    import 'package:stacked/stacked.dart';
    import 'package:stacked_services/stacked_services.dart';
    
    class HomeViewModel extends BaseViewModel {
     String _declaration = 'Stacked is soo cool';
     String get myDeclaration => _declaration;
     final _navigationService = locator<NavigationService>();
    
     void navigateToProfileView() {
      _navigationService.navigateTo(Routes.profileView);
     }
    }
```

The next thing is to create a button in the view and pass the function to its onPressed parameter.

```
     TextButton(
       onPressed: () => model.navigateToProfileView(),
       child: const Text('Go To Profile View'),
     )
```

Save the files and run the app; you would see the button there, click the button and see the seamless transition that we achieve using the NavigationService Stacked provides.


## Services

Stacked offers several services which can be set up and used within the app. These services include

- NavigationService
- BottomSheetService
- DialogService
- SnackbarService

 
These services can be set up and customized to fit various use cases, and similar to the NavigationService, they can be registered and used within the app on the fly. For further information, check out the [package](https://pub.dev/packages/stacked_services) on pub.dev and also the articles on [FilledStacks](https://www.filledstacks.com/).


## Conclusion

Stacked Architecture is a super pack ready to be discharged and used to build clean, scalable applications on the fly. This article has explained some of the concepts and components of Stacked Architecture, and with it, we have created a sample app that uses these components. Check out [FilledStack](https://www.filledstacks.com/) for further information on the various features that make up the Stacked Architecture.

You can find the code for the sample app [here](https://github.com/Blazebrain/intro_to_stacked). If you have any questions, don't hesitate to reach out to me on Twitter: [@Blazebrain](https://www.twitter.com/Blazebrain01) or LinkedIn: [@Blazebrain](https://www.linkedin.com/in/david-adegoke).

Cheers!