An example of cool Flutter Architecture and best practices. Shows information about cats :)
In order to launch this application you need to set two dart defines
(if not specified, then there would be some limitations) thecatapi-key - your api key from https://thecatapi.com/
openai-key - your api key from https://beta.openai.com/
This Flutter project template provides a starting point for your Flutter projects, saving you time and effort in the setup process. It incorporates best practices and lessons learned from other Flutter templates and repositories, with a few new and unique features.
To use this template, simply click the "Use this template" button. The instructions below will guide you through the process of setting up and using this template in your own projects.
- ๐ฅ Fast setup
- ๐ง Extensible, flexible and easy to maintain
- ๐ฆ Bunch of useful and tested libraries included
- ๐ GitHub Actions and Gitlab CI configured
- ๐ Modern feature-oriented architecture
- ๐ Robust documentation & great plans for future
- ๐ Bug reporting, errors catching and analytics
- ๐ Themes and other stuff...
Here are the steps to initialize the dependencies. It is a map of steps, where the key is the name of the step and the value is a function that fills the initialization_progress
model. The steps are executed in the order in which they are specified in the map, which gives you the ability to access the results of the previous steps.
In initialization_progress
you can store the results of the steps. InitializationProgress is a model that is passed to the steps as an argument. You can add any fields to it. The fields are initialized with null
values and iteratively filled with the results of the steps. After all the steps are executed, the InitializationProgress is mapped to an immutable models with the same fields, but not null values, that are also described here. See RepositoriesStore
and DependenciesStore
. RepositoriesStore
is obviously used for repositories๐, when DependenciesStore
is supposed to store general dependencies like SharedPreferences, Database. All the process is controlled by the ininitialization_processor
.
InitializationProcessor
as said previously is controlling all the stuff. It is responsible for calling the steps, storing the result of each and mapping it to the immutable model and returns InitializationResult
with the time spent, all the models, etc. Later, all the results are delivered by inherited widgets and can be accessed from BuildContext, see dependencies_scope
which is in widget folder.
DependenciesScope
is a widget that provides access to the DependenciesStore
and RepositoriesStore
. It is a great DI in a flutter way which gives you a possibility to access your initialized dependencies from context which exists in each widget.
Color scheme is generated by Material Theme Builder. Then, generated code is added to core/theme/color_schemes.dart
. Then, pretty simple theme datas are passes to MaterialApp in app_context.dart
. This way you can easily change the color scheme of the app with many pros:
- Creating own design system in Figma
- Support of dynamic color schemes
- Easy to change the color scheme
- All Material widgets that come with Flutter are already supported and use the color scheme
- async - Future, Stream, Completer, FutureOr, Zone
- collection - List, Map, Queue, extensions
- convert - JSON, UTF8, ASCII, Base64, Hex, LineSplitter
- core - Object, num, int, double, bool, String, RegExp, Duration, DateTime, Stopwatch, Uri
- developer - log, Timeline, TimelineTask, TimelineTaskArgument
- meta - annotations
- typed_data - ByteData, Endian, Float32List, Float64List, Int16List, Int32List, Int64List, Int8List, Uint16List, Uint32List, Uint64List, Uint8ClampedList, Uint8List
- js - interop with JavaScript
- http - HTTP client
- math
- io
- bloc - An implementation of the BLoC pattern which helps implement the business logic layer of an application
- flutter_bloc - Flutter Widgets that make it easy to integrate blocs into Flutter
- sentry - Sentry client for Dart and Flutter
- firebase_crashlytics - Firebase Crashlytics for Flutter
- firebase_analytics - Firebase Analytics for Flutter
- firebase_messaging - Firebase Cloud Messaging for Flutter
- drift - A type-safe, composable SQL client for Dart and Flutter
- sqflite - A wrapper around SQLite for Flutter, but I'd recommend to use drift instead
- shared_preferences - A persistent key-value store for Flutter
- http - A composable, Future-based library for making HTTP requests
- rive - Great tool and library for creating animations. Moreover, their editor is also written in Flutter.
- rxdart - RxDart is a reactive programming library for Dart and Flutter. It provides a set of extension methods on Dart Streams and StreamControllers to transform and combine streams in a declarative way. It also provides a set of Subjects that extend StreamControllers to allow for broadcasting of the latest value(-s) to new listeners.
- stream_transform - A collection of Stream transformers
- path - A library that provides a cross-platform API for manipulating paths.
- url_launcher - A Crossplatform Flutter plugin for launching urls.
- funvas - Funvas is a library for creating animations in Flutter. It is a wrapper around the Canvas API, which makes it easy to create animations.
-
hive - key-value storage for Flutter and Dart. It loads all data into memory, which is not good for large data sets. It is better to use sqflite or drift for this purpose. If you need a KV storage for small data sets, you can use shared_preferences. In addition, you cannot apply migrations, which is a big problem.
-
getx - GetX is a library for Flutter that tries to combine everything in one package like navigation, state-management, storage, etc. Therefore, it is usually considered as a framework. However, everything is not so good. Even an idea to combine all the stuff is mad from the beginning. Moreover, the source code is awful. It has a lot of bugs, not only talking about the documentation, coverage, etc. On top of that, there is no single approach to write the code. In addition, the idea of how to manage the logic\business logic and all the stuff is completely non-engineering. That is why projects that use this lib are very difficult to maintain. Hence many problems arise. It becomes really tough to produce new features, releases.
-
get_it - Service locator with all the problems that come in. Basically, you register your objects in the map and access them throughthout the app. Obviously, having a possibility to access any object wherever you want from any place is a destructive idea. For example, you can pull one entity out of a completely different layer and/or location in the application, change its state, dispose resources, and so on. Even if you know it's not good to do that, it still doesn't prohibit you from doing it, or juniors with less experience. Moreover, in such cases it's better to have global variables, because they will be more understandable. To summarize, you deprive yourself of a transparent dependency injection, moreover, you are likely to violate the principle of dependency inversion. If you still need the get_it, then I would recommend you to use injectable. All in all, constructors are the best way to inject dependencies. In Flutter, you can use BuildContext to inject dependencies.
-
ferry - GraphQL codegen for Dart and Flutter. Generally, it is usable, but generates a lot of code. It can happen that at some point the time spent on codegen and build will be more than a few hours, or even not work at all. Moreover, using this package is fraught with the fact that the size of your application will also be increased by huge numbers, 20-60 megabytes
-
dio - HTTP client for Dart, which has many features. However, it has some drawbacks. It comes with DioError builtin which is thrown all the time losing all stacktrace. I would recommend to use http instead.
-
flutter_hooks - Hooks are only pointless tricks, imposing their own rules and complicating the flow. Also, the creation of subscriptions, animations will be greatly complicated. To summarize, this package will not make your life easier, you will not write code faster, on the contrary, you are more likely to have difficulties. I would advise you to use snippets instead and a more Flutter way with StatefulWidget and State.
-
mobx - MobX is a reactive state management library. You are likely to have many implicit subscriptions that cause rebuilds. I'd suggest to use bloc or Value Notifier instead.
-
riverpod - Riverpod is kinda popular library for state management. It is based on global variables that store state. Let alone creating a bunch of global variables, it is also a bad idea to store state in global variables. Moreover, it is not easy to test.
-
hydrated_bloc - Hydrated Bloc is a library that allows you to persist bloc state. It is a bad idea to persist state. It is better to persist data, but not state. For example, you emitted an error or loading state. It was persisted. Next time, when user opens the app they will see an error or loading state.
- Flutter docs - official docs, one of the best ways of learning
- Dart docs - official dart docs
- DartPad - it's a great tool to play with Dart, has some examples and tutorials
- Flutter CodeLabs - Flutter Google Codelabs(great starting point)
- Dart CodeLabs - Dart Google Codelabs(very little, but still)
- Great Roadmap
- Communities
- Influencers
- Human Resources
- Flutter Fundamentals
- Flutter Awesome
- Dart Awesome
- Flutter Channel
- Click
Use this template
button - Clone this repository via
git clone
- Decide which platforms your app will be running on
- Run
flutter create . --org com.yourdomain --platforms ios,android,... or nothing if you are writing an app for each platform
in your terminal. - Run
flutter pub get
to install all dependencies - Run
flutter run
to run your app - Here you go, start coding!
This section describes how to add a new dependency to your app. Please, check the initialization section before.
- Open
lib/src/feature/initialization/logic/initialization_progress.dart
- Add new dependency to
InitializationProgress
class like others - Add new dependency to your
Store
- Go to
lib/src/feature/initialization/logic/initialization_steps.dart
- Add new entry to the map and write down all the logic needed to initialize your dependency, return new
InitializationProgress
with updated field. - Now, you can use your dependency in your app receiving it from context.
- Purple Starter - Yakov Karpov
- Plugfox - Michael Matiunin