A Boilerplate Project which adopts the concept of Clean Architecture and Modularization.

Overview

Flutter-Works Boilerplate

style: effective dart Bloc State Management Open Source Love License  MIT

GitHub stars GitHub forks GitHub watchers

Table Of Content


Overview

Repositori ini adalah Sebuah project Open-Source (Terbuka) yang diperuntukan untuk Boilerplate pada Flutter yang sangat mendukung produktifitas Anda, dengan banyak sekali fitur-fitur yang sudah kami siapkan secara instant yang dapat mempercepat dalam proses kerja Anda.

Screenshot

Theme 1 2 3 4 5
Dark
Light

Getting Started

Requirement

Berikut beberapa hal yang perlu Anda persiapkan sebelum setup Boilerplate ini:

  1. Flutter SDK Stable (Latest Version) Install
  2. Android Studio Install
  3. Visual Studio Code (Optional) Install
  4. Extension Dart dan Flutter:

Setup

Untuk menyiakan project Anda berdasarkan dengan boilplate ini, Anda perlu melakukan beberapa langkah-langkah yang perlu Anda lakukan. Untuk contoh simple implementasi bisa lihat di branch example

Berikut langhkah-langkah untuk menyiapkan Project dengan boilerplate Flutter-Works ini:

Step 1:

Pada langkah ini Anda perlu mengunduh(cloning) file dari repository ini ke lokal kompoter Anda:

git clone http://github.com/kodingworks/flutter-works-boilerplate.git

Atau

git clone [email protected]:kodingworks/flutter-works-boilerplate.git

Step 2:

Langkah selanjutnya buka folder yang telah di unduh/clone ke aplikasi cli seperti bash, cmd, terminal .

Dan kemudian jalankan perintah ini ke console:

flutter pub get

Setup Firebase

Karena pada boilerplate ini banyak sekali menggunkan teknologi yang dari firebase, maka Anda perlu mengkonfigurasinya terlebih dahulu sebelum Anda menjalankan aplikasi tersebut pertama kalinya. Berikut contoh/tutorial cara setup firebasenya:

Android

  1. Langkah pertama setup firebase pada Android yaitu create project anda terlebih dahulu di firebase console

  1. Download google-services.json sesuai dengan flavor dan package name masing-masing. Cek Change Package Name

  1. Aktifkan authentikasi lewat google pada firebase console.

  1. Buat sample remote config version

IOS

TODO: Implement Tutorial Setup Firebase IOS

Change Package Name

Secara default package name:

Prod: io.kodingworks Dev: io.kodingworks.dev Staging: io.kodingworks.staging

Untuk ganti package name cukup search semua io.kodingworks., kemudian replace dengan nama package name yang baru, misal: com.mycompany. .

Running/Debugger

Pada boilerplate ini sudah tersedia beberapa flavor yang memungkinkan Anda menjalankan beberapa tipe misal untuk mode pengembangan, produksi, dan masih banyak lagi. Di boilerplate ini secara default menyediakan boilerplate flavor untuk Android dan IOS. Berikut tipe-tipe flavor yang sudah kami konfigurasi:

1. Mode Dev (Development)

Pada flavor ini diperuntukkan hanya untuk test, dan debugging pada saat pengerjaan(development). Untuk menjalankan pada flavor ini Anda cukup jalankan perinta ini:

flutter run -t lib/main_dev.dart --flavor dev

2. Mode Staging

Pada flavor ini diperuntukkan pada mode demo:

flutter run -t lib/main_staging.dart --flavor staging

3. Mode Prod (Production)

Pada flavor ini diperuntukkan hanya pada release mode (untuk digunakan oleh user beneran):

Saran: Pada flavor ini jangan diperuntukan untuk test, demo.

flutter run -t lib/main_staging.dart --flavor staging

Jika Pengguna VS Code

Jika Anda adalah pengguna VS Code sebagai alat/tool dalam pengembangan app ada Flutter gunakan launch config yang sudah kami sediakan tinggal pilih flavor apa yang Anda ingin jalankan.

Kelebihan dari menggunakan VS Code yaitu: Anda dapat melakukan breakpoint tiap line code lebih mudah dibanding menggunkan cli.

Features

Feature Android IOS
Multiple Language ✔️ ✔️
Dark Mode/Multiple Themes ✔️ ✔️
Google Analytic ✔️ ✔️
Firebase Perfomance ✔️ ✔️
Firebase Remote Config ✔️ ✔️
Firebase Push Notification ✔️ ⚠️
Firebase Authentication ✔️ ✔️
Login With Google ✔️ ✔️
Login With Apple ⚠️
Version Check ✔️ ✔️

Library / Dependency

Pada boilerplate ini ada beberapa library yang kami tambahkan untuk menunjang produktifitas dalam pengerjaannya. Berikut daftar beberapa library yang sudah tersedia pada boilerplate ini berseta deskripsi dan versinya:

Name Deskripsi Versi
dartz Digunakan untuk Functional Programming. ^0.9.2
dio Sebagai library untuk mengatasi http request yang sangat beragam. ^3.0.10
equatable Untuk mengatasi komparasi dari object. ^1.2.6
flutter_bloc Sebagai library untuk mengatasi segala State Management yang sangat flexible. ^6.1.2
formz Digunakan untuk mengatasi validasi Form pada State Management yang mudah dan reusable. ^0.3.2
get_it Library yang berfungi untuk mengatasi Dependency Injection. ^5.0.6
hive Sebagai DataBase utama yang mengatasi berbagai dynamic data, dengan performa yang sangat optimal. ^1.4.4+1
image_picker Untuk mengatasi pengambilan gambar dari camera maupun gallery. ^0.6.7+22
rxdart Untuk mengatasi reactiveX untuk asynchronous programming ^0.25.0
url_launcher Untuk mengatasi peluncuran berbagai URL/link ke berbagai aplikasi yang tersedia di App. ^5.7.10
package_info Untuk mengambil dari data native App sekarang seperti versi, nama aplikasi, dll. ^0.4.3+4
firebase_auth Untuk mengatasi authentikasi dari firebase. ^0.20.0+1
firebase_core Library utama dari Firebase SDK untuk Flutter ^0.7.0
firebase_crashlytics Library yang berfungsi untuk mencatat berbagai masalah error code, BUG, yang tersedia dalam App. ^0.4.0+1
firebase_messaging Untuk mengatasi notifikasi yang dari firebase console. ^8.0.0-dev.14
firebase_performance Untuk mencatat performa aplikasi kita dan request http di berbagai device yang dikimkan ke Firebase Console. ^0.5.0+1
firebase_remote_config Untuk mengambil data configurasi dari firebase console, dengan value yang dynamic. ^0.6.0
google_sign_in Mengatasi login melalui google berdasarkan dari firebase. ^4.5.9

Folder Structure

flutter_modular/
┣ .vscode/
┃ ┗ launch.json
┣ android/
┣ assets/
┃ ┣ cfg/
┃ ┃ ┣ dev_env.json
┃ ┃ ┣ prod_env.json
┃ ┃ ┗ stagging_env.json
┃ ┣ fonts/
┃ ┗ images/
┣ features/
┃ ┣ auth/
┃ ┃ ┣ lib/
┃ ┃ ┣ test/
┃ ┃ ┗ pubspec.yaml
┃ ┣ home/
┃ ┃ ┣ lib/
┃ ┃ ┣ test/
┃ ┃ ┗ pubspec.yaml
┃ ┗ settings/
┃ ┃ ┣ lib/
┃ ┃ ┣ test/
┃ ┃ ┗ pubspec.yaml
┣ ios/
┣ launcher/
┃ ┣ ic_foreground.png
┃ ┣ ic_launcher.png
┃ ┗ logo_splash.png
┣ lib/
┃ ┣ app.dart
┃ ┣ flavors.dart
┃ ┣ main_dev.dart
┃ ┣ main_prod.dart
┃ ┣ main_staging.dart
┃ ┣ module.dart
┃ ┗ routes.dart
┣ shared/
┃ ┣ component/
┃ ┣ core/
┃ ┣ dependencies/
┃ ┣ l10n/
┃ ┗ preferences/
┣ test/
┣ analysis_options.yaml
┣ pubspec.yaml
┗ README.md

Module

Karena pada project boilerplate ini adalah bersifat modular, jadi tiap feature perlu dipecah jadi module sendiri-sendiri. Jadi ini sangat memungkinkan jika Anda bekerja bersama tim, proses pengerjaan kode jadi lebih mudah dan terstruktur karena tiap anggota bisa mengerjakan dengan fokus di module yang mereka kerjakan.

List Default Modules

Secara default ketika Anda menggunakan boilerplate ini, terdapat beberapa module yang sudah terpasang secara otomatis, berikut daftar module-module yang sudah tersedia:

Shared Module:

Name Description
core Sebuah module yang memuat class, function globa dan inti
component Sebuah module yang memuat global component, yang sifatnya tidak merender untuk spesifik module tertentu
dependencies Sebuah module yang memuat beberapa global dependency/library yang dijadikan satu package
l10n Sebuah module yang memuat data translasi
preference Sebuah module yang memuat tentang macam-macam gaya aplikasi, dan yang berhubungan dengan UI(User Interface)

Features Module:

Name Description
auth Sebuah module yang diperuntukan untuk mengatasi bagian authentikasi pada aplikasi
home Sebuah module yang diperuntukan untuk Home
profile Sebuah module yang diperuntukan untuk Profile Management
settings Sebuah module yang diperuntukan untuk mengatasi berbagai fitur setting pada app

Create Module

Ketika Anda membuat fitur baru yang dimana fitur tersebut mampu berjalan dengan sendiri tanpa ketergantungan sekali dengan fitur lain, Anda bisa membuat module terpisah sebagai module fitur terbaru Anda. Berikut contoh cara setup module baru:

Step 1: Copy template module.

Untuk template module sudah tersedia yang berada di root folder dengan nama folder template_module. Copy folder template_module tersebut ke destinasi/lokasi module di letakkan. Jika module tersebut adalah feature maka taruh module tersebut di dalam folder /features.

├── features/

Step 2: Rename Nama Module.

Langkah selanjutnya yaitu Anda perlu ganti nama module sesuai dengan feture Anda. Misal jika module tersebut adalah fitur payment, maka:

Before:

├─ features/
│  └─ template_module/
│    └─ lib/
│      └─ name_module.dart

After:

├─ features/
│  └─ payment
│    └─ lib/
│      └─ payment.dart

Pada file lib/payment.dart:

Before:

// TODO: change name module
library module;

export 'src/data/data.dart';
export 'src/domain/domain.dart';
export 'src/module.dart';
export 'src/presentation/presentation.dart';

After:

library payment;

export 'src/data/data.dart';
export 'src/domain/domain.dart';
export 'src/module.dart';
export 'src/presentation/presentation.dart';

Pada file lib/src/module.dart:

Before:

import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

// TODO: Change Your Name Module
class NameModule implements BaseModule {
  @override
  void inject(GetIt getIt) {
    // Data

    // Domain

    // Presentation
  }

  @override
  Map<String, Route> routes(RouteSettings settings) {
    return {
      // Routes data in module
    };
  }
}

After:

import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

class PaymentModule implements BaseModule {
  @override
  void inject(GetIt getIt) {
    // Data

    // Domain

    // Presentation
  }

  @override
  Map<String, Route> routes(RouteSettings settings) {
    return {
      // Routes data in module
    };
  }
}

Pada file pubspec.yaml:

Before:

# TODO: change name module
name: module
description: A new Flutter package project.
version: 0.0.1
author: "My Name"
homepage: "https://example.com"

environment:
  sdk: ">=2.7.0 <3.0.0"
  flutter: ">=1.17.0"

dependencies:
  flutter:
    sdk: flutter
  # TODO: UnComment & fix change path package
  # core:
  #   path: ../../shared/core
  # component:
  #   path: ../../shared/component
  # dependencies:
  #   path: ../../shared/dependencies
  # l10n:
  #   path: ../../shared/l10n
  # preferences:
  #   path: ../../shared/preferences
  

dev_dependencies:
  flutter_test:
    sdk: flutter
  effective_dart: ^1.3.0

flutter:

After:

name: payment
description: A new Flutter package project.
version: 0.0.1
author: "My Name"
homepage: "https://example.com"

environment:
  sdk: ">=2.7.0 <3.0.0"
  flutter: ">=1.17.0"

dependencies:
  flutter:
    sdk: flutter
  
  core:
    path: ../../shared/core
  component:
    path: ../../shared/component
  dependencies:
    path: ../../shared/dependencies
  l10n:
    path: ../../shared/l10n
  preferences:
    path: ../../shared/preferences

dev_dependencies:
  flutter_test:
    sdk: flutter
  effective_dart: ^1.3.0

flutter:

Step 3: Daftarkan module ke root project.

Untuk mendaftarkan module feature yang pernah kita buat itu wajib, karena jika tidak kita daftarkan besar kemungkinan akan terjadi error. Untuk mendaftarkannya lihat berikut contohnya:

Langkah pertama pasang module tersebut di pubspec.yaml pada root project.

dependencies:
  flutter:
    sdk: flutter
  
  ...

  payment: 
    path: features/payment
  
dev_dependencies:
...

Sesuaikan dengan lokasi module Anda, pada contoh diatas module berada di dalam folder /features maka pathnya adalah features/payment.

Kemudian buka file lib/module.dart, dan tambahkan class Module yang Anda buat tadi. contoh:

...

List<BaseModule> appModules = [
  AuthModule(),
  HomeModule(),
  SettingsModule(),
  PaymentModule(), // New Module
];

Pada contoh tersebut PaymentModule adalah module terbaru yang telah dibuat. Setelah selesai jalankan ulang aplikasi Anda.

Global Config/Variable

Semua konfigurasi yang sifatnya global di simpan berupa env yang pada kasus ini kami menyimpanya di folder assets/cfg/.

Didalam folder tersebut terdapat beberapa file konfigurasi yang sebelumnya tersedia yaitu:

  • dev_env.json
  • prod_env.json
  • stagging_env.json

Pada tiap item tersebut memuat konfig yang berbeda-beda berdasarkan dari nama flavornya, misa jika flavornya dev maka namanya dev_env.json.

Untuk format konfigurasinya kami simpan berupa format file dengan extensions json karena config json yang paling mudah dibaca oleh Flutter itu sendiri.

Berikut contoh yang ada di konfigurasi env nya:

{
    "base_url": "https://example.co.id/v2",
    "app_name": "App",
    "encrypt": "AlOp7lBkcFRdJnXFkGcBHwM9I9TJMMas",
    "version_name": "1"
}

Pada contoh diatas semua variable tersebut itu adalah variable global, maupun variable konfigurasi enable/disable fitur.

Note: Ketika ada perubahan dalam file konfigurasi, tidak dapat melakukan hot reload data, dan Anda wajib melakukan restart debug. Hal ini terjadi karena setiap konfigurasi itu di muat pada saat pertama kali peluncuran aplikasi, atau pada saat method main() pada dart dijalankan pertama kali.

Panggil Global Variable

Untuk memanggil Global Variable Anda perlu menggunakan class GlobalConfiguration dan class GlobalConfiguration adalah bagian dari module core. Berikut contoh penggunaanya:

import 'package:core/core.dart';
import 'package:dependencies/dependencies.dart';

....

final appName = GetIt.I<GlobalConfiguration>().getValue('app_name');

print(appName);
....

Penggunaan Translation/Localization

Untuk data translation berada pada module l10n yang letaknya di /shared/l10n. Semua data translasi pada app Anda dimuat di module ini. Secara default bahasa yang di dukung sudah 2, yaitu bahasa Indonesia dan English.

Create Item Translation

Untuk membuat item translasi Anda perlu generate dari kode arb ke dart. Tapi jagan khawatir pada boilerplate ini sudah tersedia fitur generate translasi, untuk data-data translasi Anda bisa cek di folder lib/l10n yang berada di module l10n, yang filenya berextension .arb. Untuk contohnya bisa lihat ini:

{
  "hello": "Hello World"
}

Jadi pada extension .arb format data sama persis dengan format .json jadi pastinya lebih familiar lagi. Dimana tiap item tersebut mempunyai key dan value. Pada contoh di atas "hello" adalah sebagai key dan "Hello World" adalah sebagai value.

  • key adalah sebagai nama variable yang akan digenerate
  • 'value` adalah text yang akan di tampilkan tiap bahasa masing-masing

Note: jika membuat translasi pastika semua bahasa key itu ada di semua bahasa yang ada di folder lib/l10n

**Jika Ingin menambahkan param di translasi **

{ // intl_en.arb
  "hello": "Hello my name is {name}"
}
{ // intl_id.arb
  "hello": "Halo namaku adalah {name}"
}

Dimana pada tanda {} tersebut berarti ada input param jika dipanggil.

Note: Pastikan jika Anda menambahkan param/plural di item translasi Anda wajib menambahkanya di semua localization bahasanya

Generate Translation

Setiap kali ada perubahan sekecil apapun yang ada di folder lib/l10n yang berada di module l10n maka Anda perlu mengenerate ulang kode, supaya hasil kodenya bisa terbaru. Untuk mengenerate anda cukup gunakan perintah seperti ini:

./generate_l10n.sh

Get Item Translation

Jika ingin panggil item translasi Anda perlu import terlebih dahulu module l10n. Dan tiap pemanggilan translasi Anda perlu panggil main class nya yaitu S. Contoh:

import 'package:l10n/l10n.dart';

...
S.current.hello; // Hello World
...

Jika Ada param:

import 'package:l10n/l10n.dart';

...
S.current.hello('Flutter'); // Hello My Name is Flutter
...

Create Locale

Untuk membuat translasi yang mendukung bahasa lain, Anda perlu membuat file tambahan pada module l10n dan di folder 'lib/l10n'. Dengan format:

intl_{language_code}.arb

Buat file dengan format seperti diatas, atau juga bisa mengcopy dari salah satu localization lain, dengan rename sesuai kode bahasanya.

Jangan lupa setelah membuat bahasa lain, Anda perlu generate ulang.

Set Main Locale

Untuk setting default bahasa apa yang digunakan, jika bahasa tersebut tidak/belum Anda dukung pada suatu negara maka Anda perlu custom confignya. Secara default config default locale di set dalam bahasa english, Jika ingin custom bisa cek di pubspec.yaml pada shared/l10n/pubspec.yaml. Untuk contoh confignya seperti ini:

flutter_intl:
  enabled: true
  main_locale: en

Pada contoh tersebut main_locale dengan value en, berarti en tersebut code bahasa yang berarti bahasa English. Selengkapnya cek di sini Untuk mengetahi daftar kode bahasa di seluruh dunia.

Generate Icon Launcher

Secara default di boilerplate ini sudah custom icon launchernya, tapi jika ingin custom sesuai dengan keinginan Anda sendiri, Anda perlu custom terlebih dahulu gambar icon-icon yang ingin Anda jadikan sebagai Icon Launcher. Secara default telah disediakan template ukuran icon, berada di folder /launcher.

Step 1:

Dan untuk konfigurasi nya berada di file pubspec.yaml pada root project. Berikut contoh konfignya:

flutter_icons:
  android: "ic_launcher"
  ios: true
  image_path_android: "launcher/ic_launcher.png"
  adaptive_icon_background: "#E67442"
  adaptive_icon_foreground: "launcher/ic_foreground.png"
  image_path_ios: "launcher/ic_launcher.png"

Untuk info lebih bisa lihat di pub.dev.

Step 2:

Dan untuk generete iconnya Anda perlu jalankan perintah seperti ini:

flutter pub run flutter_launcher_icons:main

Step 3:

Karena package flutter_launcher_icons ini generate icon launcher pada Android hanya pada main saja, belum sampai ke per item flavor, Jadi Anda perlu konfigurasi manual ulang lagi untuk set icon pada tiap flavor di Android.

  1. Copy folder res yang berada di /android/app/main/res

  1. Paste dan replace ke folder flavor tujuan Anda.

Kemudian jalankan ulang aplikasi Anda.

Generate Native Splash Screen

Info selengkapnya lihat di pub.dev, dan Setelah tergenerate copy ulang folder res ke tiap item flavor sama seperti generate icon_launcher.

You might also like...

Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to developing Flutter apps.

Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to developing Flutter apps.

Flutter Architecture Blueprints Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to dev

Apr 9, 2022

Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to developing Flutter apps.

Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to developing Flutter apps.

Flutter Architecture Blueprints Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to dev

Dec 31, 2022

Flutter boilerplate with TDD architecture

flutter_tdd_architecture A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you

May 25, 2022

Flutter project to find and discover events with Clean Architecture and Bloc from SeatGeek API.

Flutter project to find and discover events with Clean Architecture and Bloc from SeatGeek API.

Dec 6, 2022

This project follows the Reso Coder course for flutter test-driven-development with clean architecture and BloC state management for a random trivia simple app.

This project follows the Reso Coder course for flutter test-driven-development with clean architecture and BloC state management for a random trivia simple app.

Jan 5, 2022

This is an example project for the article about implementing clean architecture in flutter with riverpod and supabase as backend service.

The Example This is an example how to implement clean architecture with domain driven design and riverpod in flutter projects. Getting Started Rename

Dec 30, 2022

Starting template for a new Flutter project. Using clean architecture + Riverpod.

flutter_project_template_riverpod Installation Add Flutter to your machine Open this project folder with Terminal/CMD Ensure there's no cache/build le

Dec 27, 2022

Flutterstarterproject - Clean Architecture Flutter starter project, using tdd + bloc

Flutterstarterproject - Clean Architecture Flutter starter project, using tdd + bloc

Flutter Starter Project Generated by the Nero Lab CLI 🤖 A Nero Lab Project crea

Dec 8, 2022

Clean Architecture Project with TDD Approach

Clean Architecture Project with TDD Approach

Clean-Architecture-Project-with-TDD-Approach Small project to implement TDD(Testing Driven Development) by applying SOLID and YAGNI and rules(Clean Ar

Jun 24, 2022
Owner
KodingWorks
Open Source, Open Startup, Remote First - On Demand Engineering Platform
KodingWorks
Ouday 25 Dec 15, 2022
Flutter-clean-architecture - A simple flutter project developed with TDD and using Clean Architecture principles.

Clean Architecture This is a study project to practice TDD and a good approach of Clean Architecture for flutter projects. It is based on Reso Coder s

Luiz Paulo Franz 8 Jul 21, 2022
Flutter starter project - boilerPlate for Clean Architecture with Domain-Driven-Design with commonly used dependencies

flutter_starter_project A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you

MJ Montes 0 Jan 2, 2022
Flutter boilerplate - A boilerplate project created in flutter using MobX and Provider

Boilerplate Project A boilerplate project created in flutter using MobX and Prov

Wali Khan 0 Jan 22, 2022
A Flutter boilerplate made by GeniusCrew BV and inspired to the Clean Architecture structure.

GeniusArchitecture This is a boilerplate project created in flutter using Provider, Firebase, Dio, and some fundamentals of Robert C Martin's Clean Ar

GeniusCrew 10 Dec 16, 2022
Raden Saleh 20 Aug 12, 2023
Flutter ui boilerplate is easiest way to create new flutter project with clean code and well organized file folder.

Flutter UI Boilerplate "Sharing for fun" Flutter ui boilerplate is easiest way to create new flutter project with clean code and well organized file f

Dimas Ibnu Malik 122 Dec 1, 2022
clean architecture and clean code with flutter , with bloc and getx state managment .

example_bloc A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if

Khaled ElTohamy 6 Aug 22, 2022
Flutter Architecture inspired by Domain Driven Design, Onion and Clean Architecture

Inspiring Domain Driven Design Flutter Architecture Please take a look at my slides to learn more Strategic Domain Driven Design For Improving Flutter

Majid Hajian 324 Dec 25, 2022
Proyect with Clean Architecture / Hexagonal Architecture - Patron BLoC - The MovieDB API

flutter_movies_db A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

null 2 Sep 22, 2022