An open API service indexing awesome lists of open source software.

https://github.com/e-mre/hair_salon_nearby

Hair Salon App
https://github.com/e-mre/hair_salon_nearby

Last synced: 3 months ago
JSON representation

Hair Salon App

Awesome Lists containing this project

README

        




Flutter Template Project - MVVM Architecture


Flutter 3.7.0Null SafetyMVVM

- Flutter Template Project. Yeni projelerimizi buradan referans alarak kullanabiliriz. Bir projenin ihtiyaç duyabileceği temel özellikleri barındıran projedir.

## Neler Bulunuyor?

- Easy Localization (Çoklu dil desteği)
- Http & Dio ile GET,POST işlemleri yapabilme
- Secure Storage & Hive ile lokal depolama
- Svg resimleri kullanabilme
- Input Formatter
- Bloc & Provider ile State yönetimi
- Json Annotation
- Basit dosya işlemleri
- Navigation (Auto Route & NavigationService)
- ThemeManager ile tema yönetimi
- Responsive tasarım desteği (Responsive Framework & Responsive Builder)
- Assets Generator (Resim, font ve diğer assetleri yönetmek)
- DotEnvironments (Projenin belirli sabitlerini yönetmek)
- Versiyon Kontrolü

Yukarıdaki maddelere ek olarak küçük çaplı pek çok özellikte mevcut.

## Kodlama Standartları

- Bir projeyi geliştirirken tüm ekibin ortak dili konuşması proje geçişleri için son derece önemlidir. Bu kapsamda projeleri geliştirirken aşağıdaki standartlara uygun geliştirmeyi ihmal etmeyin!

### Models Katmanı

- Models klasörü 3 alt katmanı barındırır. Entity, Intent ve Request. Eğer oluşturacağımız model API'ye istek atmak için oluşturulacaksa Request katmanında, sayfalar arası veri transferi için kullanılacaksa Intent katmanında ve bir response veya ham bir data ise Entity katmanında tanımlanmalıdır.

- Oluşturduğumuz model bir request veya response ise yani içerisinde toJson, fromJson metotları barındırıyorsa mutlaka json_serializable paketi kullanılmalıdır.

- Entity modelleri zorunlu olarak EntityModel sınıfından kalıtım almalıdır. Kullanıma göre Request modelleri de alabilir.

- Model parametreleri final ile tanımlı olup her biri nullable yapılmalıdır. (İstisnalar olabilir.)

- Modele bağımlı sorgular veya filtrelemeler varsa getter ile genişletilmelidir.

ÖRNEK:

```dart
part 'example_entity_model.g.dart';

@JsonSerializable()
class ExampleEntityModel extends EntityModel {
final String? name;

ExampleEntityModel({this.name});

factory ExampleEntityModel.fromJson(Map json) => _$ExampleEntityModelFromJson(json);

ExampleEntityModel fromJson(Map json) => _$ExampleEntityModelFromJson(json);

Map toJson() => _$ExampleEntityModelToJson();
}
```

### Repository Katmanı

- Servis isteklerini yönetmek için kullanılan katmandır. Abstract ve concrete adında 2 bölümden oluşmaktadır. Abstract içerisine abstract class'ları tanımlıyoruz ve concrete içerisine de repositoryleri ekliyoruz.

- Her bir repository muhakkak bir abstract repository'den kalıtım almalıdır.

- Repository içerisindeki metotlar dönüş tipi olarak aşağıdaki 3 tipten birisini döndürmelidir;

* Result
* DataResult
* ApiResponse

- Abstract repositoryler BaseRepository veya BaseVexanaRepository'den kalıtım almalıdır. Kalıtım alırken constructor'a super.remote() veya super() eklenmelidir.

- Request validasyonları bu katmanda yapılmalıdır.

ÖRNEK:

```dart
abstract class ExampleRepository extends BaseVexanaRepository{
Future, ExampleEntityModel>> getExamples(ExampleRequestModel request);
}

class RemoteExampleRepository extends BaseVexanaRepository{
RemoteExampleRepository({
super.header,
super.baseUrl,
super.timeout,
super.dataService,
super.baseOptions,
super.tokenService,
super.onRefreshToken,
}) : super.remote();

@override
Future, ExampleEntityModel>> getExamples(ExampleRequestModel request) async {
return await dataService.postByResponseData, EmptyEntityModel>(
parseModel: ExampleEntityModel(),
endpoint: 'endpoint',
body: data,
);
}
}
```

### State Managers (Cubit)

- Projede cubit kullanılmıyorsa bu açıklamayı atlayabilirsiniz.

- State'leri yönetmek adına cubit için oluşturulan BaseCubit ve BaseDataCubit adında iki adet manager bulunmaktadır. Bu managerlar içerisinde barındırdığı metotlar sayesinde API isteği atarken loading durumunu ayarlayıp gelen response'a göre success veya error durumlarını set etmektedir.

- Bir state manager oluşturmadan önce ilk olarak state sınıfı oluşturulmalıdır. State içerisinde tek bir data olacaksa BaseDataState tercih edilmelidir. Fakat data yoksa veya birden fazla bulunuyorsa o zaman BaseState tercih edilebilir. Unutmayın state sınıfınızı BaseState veya BaseDataState ile kalıtımlamayı ihmal etmeyin.

- State sınıfı düzenlendikten sonra cubit oluşturulmalıdır. Bu cubit sınıfı da bizim state'e göre kalıtım alacaktır. Eğer state sınıfı BaseState'ten kalıtım aldıysa BaseCubit ile diğer durumda BaseDataCubit'ten kalıtım almalıdır.

- Cubit içerisinde atılacak her future istek kalıtımdaki sınıfların metotları ile yapılmalıdır.

* sendRequestResult
* sendRequestDataResult
* sendRequestApiResponse

ÖRNEK:

```dart
class ExampleState extends BaseDataState> {
ExampleState({
required super.data,
required super.errorMessage,
required super.infoMessage,
required super.status,
}) : super();

ExampleState.initial() : super.initial();

@override
ExampleState copyWith({
List? data,
bool isSetPreviousData = true,
String? errorMessage,
String? infoMessage,
StateStatus? status,
}) {
return ExampleState(
data: data ?? (isSetPreviousData ? this.data : null),
errorMessage: errorMessage ?? this.errorMessage,
infoMessage: infoMessage ?? this.infoMessage,
status: status ?? this.status,
);
}
}

class ExampleCubit extends BaseDataCubit {
ExampleCubit({ExampleRepository? exampleRepository})
: _exampleRepository = exampleRepository ?? MockExampleRepository(),
super(ExampleState.initial());

final ExampleRepository _exampleRepository;

Future getExamples() async {
sendRequestDataResult(futureDataResult: _exampleRepository.getAll());
}
}
```

### Presentation Katmanı

- Bu katmanda view ve view modelleri tutarız ayrıca widget'lar da burada yer almaktadır. Yukarıdaki repository ve cubit mantığına uygun widget'lar da bulunmaktadır. BaseBlocProviderView ve BaseBlocBuilderView.

Bu iki widget sayfa içerisinde yazılacak kodu azaltarak daha hızlı kodlamaya katkısı bulunmaktadır.

ÖRNEK:

```dart
@override
Widget build(BuildContext context) {
return BaseBlocProviderView>(
create: (_) => ExampleCubit()..getExamples(),
successChildBuilder: (context, state) {
return ListView.builder(
itemCount: state.data?.length ?? 0,
itemBuilder: (context, index) => const Text('data'),
);
},
);
}

Widget _buildData() {
return BaseBlocBuilderView(
errorChildBuilder: (context, state) => Text('Error: ${state.errorMessage}'),
successChildBuilder: (context, state) => MyCard(child: state.data),
loadingChild: const CircularProgressIndicator(),
);
}
```

## Katmanlar

- core
- models
- repositories
- presentation
- utils

Her katmanın kendine has görev ve sorumlulukları bulunmaktadır.

## Katman Görevleri
- ``core``: Tüm mobil projelerinde kullanılabilan, ortak ve dinamik kodları içeren yapıdır.
Errors, Results, Utils gibi ortak kullanılabilir yapılar burada yer alır.

- ``models``: Verileri sakladığımız varlıklar burada tutulur. Modellerimiz json yardımı ile ayrıştırılmaktadır.
Bunun için JsonAnnotation kullanılmaktadır. Varsayılan olarak 3 alt başlık bulunmaktadır.

[Entity] : Temel sınıflarımızı bu kısma ekliyoruz. ``` class User{} ```

[Request]: API'ye göndereceğimiz datayı içeren sınıflar buraya dahil edilir. ``` class LoginRequest{} ```

[DTO] : Varlıkların birleşimi ile oluşan yeni model yapımız burada yer alır. Örneğin kategori ve ürün birleşiminden oluşan yeni bir sınıf. ``` class ProductCategoryDto {} ```

Klasörleme Örneği:

models:
|_ entity: User, Product ...
|_ dto: ProductCategoryDto, EmployeeCompanyDto ...
|_ request: LoginRequest, UserRequest ...

- ``repositories``: Verinin nereden alınacağına karar verdiğimiz katmandır. Genelde CRUD operasyonlarını içerir.
İçerisinde de `abstract` ve `concrete` klasörleri olmalıdır.

[Abstract]: Soyut sınıflarımız burada yer alır.

[Concrete]: Soyut sınıflardan implement edilen somut sınıflarımız burada yer alır.


Klasörleme Örneği:

repositories:
|_ abstract: LoginRepository
|_ concrete: HttpLoginRepository, DioLoginRepository, LocaleLoginRepository ...

- ``presentation``: View ve ViewModel katmanlarını burada tutarız. İçerik şu şekildedir.
[theme]: Temayı burada düzenleriz. Varsayılan ThemeManager kullanılır.

[widgets]: Widget'lar burada yer alır.

[screens]: Sayfaları burada saklarız. View ve ViewModel yapısı burada yer alıyor. ViewModelimiz
varsayılan olarak `cubit` ile oluşturulmaktadır. Yani Bloc yapısı kullanılmaktadır.

Klasörleme Örneği:

presentation
|_ theme: ThemeManager ...
|_ widgets: CustomButton, LoginForm ...
|_ screens
|_ login
|_ view: login_screen.dart
|_ view_model: login_view_model.dart

- ``utils``: Yardımcı araçlarımızı burada tutarız. Bunlar mixins, helpers, constants, enums olabilir.

## Ne, Nasıl Yapılır?

- [Sayfa Yönlendirmesi](https://github.com/E-MRE/base_flutter_project/tree/master/lib/utils/navigation)
- [Yerelleştirme](https://github.com/E-MRE/base_flutter_project/tree/master/lib/utils/constants/lang)
- [Model Oluşturma](https://github.com/E-MRE/base_flutter_project/tree/master/lib/models/)
- [Repository Oluşturma](https://github.com/E-MRE/base_flutter_project/tree/master/lib/repositories)

## Rehber

Bir nesnenin get/post işlemleri için hangi katmana hangi işlemler yapılması gerektiği aşağıda açıklanmaktadır.

1) endpoint_constants sınıfına kullanılacak endpoint eklenmelidir.

2) models - entity klasöründe nesne oluştur.

1.1- Eğer veri göndermek gerekiyorsa request klasörüne de ilgili sınıfımızı oluşturuyoruz.

3) repository - buraya ilgili metotlarımızı oluşturup veriyi çekiyoruz.

3.1- Abstract isimlendirme standardı -> 'operasyon_adi_repository.dart' şeklinde olmalıdır.

3.2- abstract class içerisinde nesneye ait get, post, add, vs. metotları yazılmalıdır.

3.3- (Eğer DataResult Result kullanıyorsan geçerli*) metotlar herhangi bir data dönüyorsa DataResult ile sarmallanmalı, eğer void tipindeyse o zaman Result yazılmalı.

3.4- (Eğer DataResult Result kullanıyorsan geçerli*) Örn: Future>> getUsernames(); // metot async olduğu için Future, liste döneceği için DataResult ile sarmallandı.

3.5- (Eğer DataResult Result kullanıyorsan geçerli*) Örn: Future addUsername(String name); //metot async olduğu için Future, geri dönüş değeri olmadığı için Result döndürdük.

3.6- (Eğer DataResult Result kullanıyorsan geçerli*) Result: içerisinde işlem başarılı,başarısız bilgisi ve mesaj bilgisini tutar.

3.7- (Eğer DataResult Result kullanıyorsan geçerli*) DataResult: Result'tan kalıtım alır. Ekstra olarak datayı da saklar.

3.8- somut sınıfımız implement edilir. İsimlendirmesi veri_alma_yöntemi_operasyon_adi_repository.dart ÖRN: http_login_repository.dart

3.9- Metotların içi doldurulur ve istek atılır. Gerekiyorsa validation yapılır ve işlem sonucu döndürülür.

4) view_model - Burada viewModel cubit'ten kalıtım alıcak şekilde oluşturulur.

4.1- Kullanacağımız repository'nin soyut sınıfı değişken olarak oluşturulur.

4.2- constructor ile repository istenir.

4.3- İhtiyacımız olan metotlar burada oluşturulur.

4.4- Repository'e erişim sağlanmadan önce varsa validation yapılır.

4.5- Repository'e erişim sağlanır, gelen bilgi view'e aktarılır.

5) view - Burada viewModel'i oluştururuz ve gerekli bağımlılıkları ayarlarız.

5.1- ViewModel üzerinden istek atılır.

5.2- statelere göre sayfa içeriği ayarlanır.