Flutter Starter App
At this point I’ve probably created 8-10 Flutter apps. The setup is the same each time so I created a starter app that I can quickly modify to get something up and running fast.
If you’d like to use it, you can clone this repro: https://github.com/pmmucsd/flutter_base_app
Here’s the basic structure:
The architecture is primarily built off of stacked, an architecture developed and revised by the FilledStacks community. This architecture was initially a version of MVVM which they’ve modified as they’ve built large production applications. Essentially all the display code lives in views and all the business logic lives in viewmodels which are tied to views in a reactive way so that as data changes, widgets that rely on the data are notified and updated. Services are the other main component of this architecture. They are basically functions that will be used in many different viewmodels (database, api, location, local storage, etc..).
These are all dependencies on third party libraries:
- stacked
- stacked_services
- stacked_generator
- shared_preferences
- intl
- changeapppackage_name (to easily use the starter by changing the package name)
- build_runner (for generated files – this is a dev dependency)
Let’s go through each of the folders/files one by one to explain what they do and how you might use this starter app.
/assets
This is where you place any assets that will be shipped with the app. In my apps I generally have fonts, images, and animations. If you want to add more folders you also have to update pubspec.yaml to add the folders to the spec.
/lib/app
The app folder holds the main configuration file for the app (app.dart). This file sets up the app, it’s routes, the logger and registers all the viewmodels, and services as dependencies.
Every time you add a new view, viewmodel or service you need update this file and run the following command in the terminal under project directory.
flutter pub run build_runner build --delete-conflicting-outputs
This will update the following generated files: app.locator.dart, app.router.dart, app.logger.dart.
/lib/constants
Here I put all my constants:
- I try to declare ui strings for all UI widgets in the ui_strings.dart file and then reference those in the widgets. This makes it easier to localize the app.
- I create material colors for each of the colors in the app’s palette is levels of opacity that I can use in widgets in app_colors.dart
- All app styles are in app_styles.dart
- App API keys to other services are in app_keys.dart. I put that file in my .gitignore so that the keys don’t end up on github.
/lib/models
In application_models.dart I put all the models the app will use (sometimes I break this up into files for each model. The models are useful/necessary when you want to save data or send it somewhere via an API. Each model defines a toJson and fromJson function to easily serialize into JSON strings and deserialize into models.
enums.dart are for simple enums
/lib/services
Services are functions that are used across many viewmodels. They are registered in app.dart as singletons (only one instance exists) and using the locator, a viewmodel can grab the singleton and use to perform its specified function. Common services I create are
- API – http calls to 3rd party services
- database – sqlite storage on device
- localStorage – key, value local storage using shared_preferences
I also use stacked_services for navigation and dialogs.
/lib/utils
Utils!! The worst directory to have because you stuff a bunch of random shit into it – but sometimes there isn’t another good place. I put ui_helpers.dart here which just contains a bunch of simple UI functions to remove the boilerplate in widgets.
/lib/views
For each screen in your app you’ll want to create a view and a viewmodel. The display code goes into the view which is built with a pointer to the corresponding viewmodel. When you need to perform logic, you call a function in the viewmodel which has access to all the services and data models you need. You can tell the view that the viewmodel is busy so that it can show progress indicators or not show certain widgets.
In some cases you have several custom widgets that compose the view and need access to the same view model. Stacked has a way to build several widget with a common viewmodel, so I’d review their docs if you find yourself in this situation.
Once you’ve created a view you’ll need to update app.dart with a route to that view and a registration of the viewmodel for that view.
I also created simple baseview and baseview_model files to make it easier to create new views.
/lib/widgets
Here is where you create custom widgets that will be used in the view. Widgets can have viewmodels too. I usually create a folder for each widget with a widget and a widgetviewmodel.
/lib/main.dart
This is the entry point for your app. For the most part you shouldn’t need to do much here but sometimes you need to set up a service before the app is run or you need to edit the MaterialApp widget that contains the rest of your code.
For the most part, that’s it! I would read the Stacked docs and maybe read/watch some of the FilledStacks tutorials (which are really great) so that you have a better understanding of the architecture before really diving in.