In this project we will discuss:
- โ Problem
- โ Solution
- โ Reason of the structure
- โ Supports
- โ Flavours
- โ Translation
- โ Dependencies
- ๐ฒ Tests
- ๐ Links
Develop an application that load images from the internet. Images should be shown in an infinite scroll without lagging. For this project you are required to use http://api.shutterstock.com to create api-key.
lib
will contains view(pages, widgets, components), BLoC, configuration, etc.packages
will keep network and repository layer.
Let's discuss the implementation layer by layer๐ป
- The
pages
section will have UI of the application and business logic layer and BLoC placed with respective UI/view - We have used flutter_bloc as a state management library and Business logic's implemented
in
DashboardBloc
because we have only one (1) page and we created one (1)bloc. DashboardBloc
communicates withRepositoryService
interface.RepositoryService
implements the function in aRepository
where we are readingurl
andaccess token
from.env
file.- A
NetworkClient
injected inRepository
so api can be called while passing reading all the respective parameters. - The
NetworkClient
is a wrapper class ofdio
so function can be improvised without touching core library. For example if we require set token for few api and not for all then we can control in this layer. - The
NetworkException
are being thrown from this(NetworkClient) layer and parsed in the respective class. - A
Custom Interceptor
is placed in the network layer. We can use it whenever we require as explained in the layer.
- This project is using
BLoC's 7.0.0
version as State management library. But asBloc
team is working on newer version that will help to remove boilerplate of writingmapEventsToSate
and bloc will look like this:class DashboardBloc extends Bloc<DashboardEvent, DashboardState> { DashboardBloc(): super(DashboardInitial()) { // search images action on<SearchImage>(_searchImages); } // perform your action here void _searchImages(SearchImage event, Emit<DashboardState> emit) async { // TODO: your business logic will be here to handle state and event } }
- Sentry.io is being used to log all errors and issues in a server.
The solution's focusing Single Responsibility concept and let me defend it:
- Moved the Repository and network layer in packages section because it's good to go with layer by layer. In layers, tests can be performed efficiently. How? You can write test cases in the respective package rather than creating in the main section.
- Repository is handling single responsibility of communication between BLoC(Business logic component) and the network layer.
- NetworkClient is only focusing the communication between Repository and the rest api client.
- BLoC is handling communication between UI and Repository and logics will be implemented there before and after repository response. BLoC handle events and emits the streams for the UI.
- UI Layer only responsible to interact with User and pass the value to the BLoC.
โ Shutterstock works on iOS and Android.
This project contains 3 flavors:
Before run application you need to create .env
file in app directory and add these varaibles:
#Logs Controller -- create your key (https://sentry.io/welcome/) and add
SENTRY_IO=
#Shutterstock credentials -- create your key (http://api.shutterstock.com) and add
TOKEN=
API_URL=https://api.shutterstock.com/v2
To run the desired flavor either use the launch configuration in VSCode/Android Studio or use the following commands:
# Development
$ flutter run --flavor development --target lib/main_development.dart
# Staging
$ flutter run --flavor staging --target lib/main_staging.dart
# Production
$ flutter run --flavor production --target lib/main_production.dart
*Shutterstock works on iOS and Android.
This project relies on flutter_localizations and follows the official internationalization guide for Flutter.
Note: For demo purposes, I have added two locales but have same strings. We can update and even add more locales to support.
- To add a new localizable string, open the
app_en.arb
file atlib/l10n/arb/app_en.arb
.
{
"@@locale": "en",
"appBarTitle": "Shutterstock",
"@appBarTitle": {
"description": "Text shown in the AppBar of the ShutterStock Page"
}
}
- Then add a new key/value and description
{
"@@locale": "en",
"appBarTitle": "Shutterstock",
"@appBarTitle": {
"description": "Text shown in the AppBar of the ShutterStock Page"
}
}
- Use the new string
import 'package:shutterstock/l10n/l10n.dart';
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Text(l10n.appBarTitle);
}
Update the CFBundleLocalizations
array in the Info.plist
at ios/Runner/Info.plist
to include the new locale.
...
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>es</string>
</array>
...
- For each supported locale, add a new ARB file in
lib/l10n/arb
.
โโโ l10n
โ โโโ arb
โ โ โโโ app_en.arb
โ โ โโโ app_es.arb
- Add the translated strings to each
.arb
file:
app_en.arb
{
"@@locale": "en",
"appBarTitle": "Shutterstock",
"@appBarTitle": {
"description": "Text shown in the AppBar of the ShutterStock Page"
}
}
app_es.arb
{
"@@locale": "es",
"appBarTitle": "Shutterstock",
"@appBarTitle": {
"description": "Text shown in the AppBar of the ShutterStock Page"
}
}
We like to highlight some major dependencies:
- dio: ^4.0.0 -- A powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.
- flutter_bloc: ^7.0.0 -- A predictable state management library that helps implement the BLoC design.
- pull_to_refresh: ^2.0.0 -- A widget provided to the flutter scroll component drop-down refresh and pull up load.support android and ios.
- sentry_flutter: ^5.1.0 -- This package includes support to native crashes through Sentry's native SDKs: (Android and iOS). It will capture errors in the native layer, including (Java/Kotlin/C/C++ for Android and Objective-C/Swift for iOS).
This section is under development and will be continued after a break.
Most of the work I have done is placed in private repositories, and it's confidential. It's not possible for me to make it public without permission.