Compare commits

...
10 Commits
Author SHA1 Message Date
efrilm 13b1b6e6b8 search text field 2025-10-24 14:00:53 +07:00
efrilm 4dbdcadeaf current outlet 2025-10-24 13:55:00 +07:00
efrilm 97c03bcf96 change name login form 2025-10-24 02:04:28 +07:00
efrilm 05609d592d main page 2025-10-24 02:02:21 +07:00
efrilm 59062f9af1 auth state 2025-10-24 02:02:00 +07:00
efrilm ca5d2a58e7 login 2025-10-24 01:16:50 +07:00
efrilm 5d9197c986 color and theme 2025-10-23 23:20:44 +07:00
efrilm 9a61cbecc4 crashlytic interceptro 2025-10-23 23:13:39 +07:00
efrilm cf8f0b373d logo, fonts, firebase analytic 2025-10-23 23:06:02 +07:00
efrilm 4fee7c4bb7 base project 2025-10-23 22:16:53 +07:00
161 changed files with 10963 additions and 359 deletions
+15 -23
View File
@@ -1,28 +1,20 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
linter: linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule sort_pub_dependencies: false
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule prefer_relative_imports: true
# Additional information about this file can be found at analyzer:
# https://dart.dev/guides/language/analysis-options errors:
missing_required_param: error
missing_return: error
must_be_immutable: error
sort_unnamed_constructors_first: ignore
invalid_annotation_target: ignore
use_build_context_synchronously: ignore
deprecated_member_use: ignore
exclude:
- test/generated/**
- "**/**.g.dart"
- "**/**.freezed.dart"
+10 -2
View File
@@ -3,10 +3,11 @@ plugins {
id("kotlin-android") id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin") id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services")
} }
android { android {
namespace = "com.example.apskel_pos_flutter_v2" namespace = "com.apskel.pos"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@@ -21,7 +22,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.apskel_pos_flutter_v2" applicationId = "com.apskel.pos"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion
@@ -42,3 +43,10 @@ android {
flutter { flutter {
source = "../.." source = "../.."
} }
dependencies {
// Import the Firebase BoM
implementation(platform("com.google.firebase:firebase-bom:34.4.0"))
implementation("com.google.firebase:firebase-analytics")
}
+29
View File
@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "765730035527",
"project_id": "apskel-pos-v2",
"storage_bucket": "apskel-pos-v2.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:765730035527:android:498defd7071336dd241e62",
"android_client_info": {
"package_name": "com.apskel.pos"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAOZwVSQwUeeM9BjcyTOK9GUh8AmTWucuc"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
+13 -2
View File
@@ -1,8 +1,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<!-- Izin khusus untuk akses foto (media images) di Android 33 ke atas -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<application <application
android:label="apskel_pos_flutter_v2" android:label="Apskel POS"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/launcher_icon">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@@ -1,4 +1,4 @@
package com.example.apskel_pos_flutter_v2 package com.apskel.pos
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+1
View File
@@ -20,6 +20,7 @@ plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.3" apply false id("com.android.application") version "8.7.3" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false id("org.jetbrains.kotlin.android") version "2.1.0" apply false
id("com.google.gms.google-services") version "4.4.4" apply false
} }
include(":app") include(":app")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

+1
View File
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"
+1
View File
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"
+43
View File
@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
+2 -2
View File
@@ -427,7 +427,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@@ -484,7 +484,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@@ -1,122 +1 @@
{ {"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+15
View File
@@ -0,0 +1,15 @@
# Generate: dart run flutter_launcher_icons -f launcher_icon.yaml
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "assets/images/logo.png"
remove_alpha_ios: true
min_sdk_android: 21 # android min sdk min:16, default 21
web:
generate: true
image_path: "assets/images/logo.png"
windows:
generate: true
image_path: "assets/images/logo.png"
icon_size: 48
+66
View File
@@ -0,0 +1,66 @@
import 'package:dartz/dartz.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../domain/auth/auth.dart';
import '../../domain/outlet/outlet.dart';
part 'auth_event.dart';
part 'auth_state.dart';
part 'auth_bloc.freezed.dart';
@injectable
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final IAuthRepository _repository;
final IOutletRepository _outletRepository;
AuthBloc(this._repository, this._outletRepository)
: super(AuthState.initial()) {
on<AuthEvent>(_onAuthEvent);
}
Future<void> _onAuthEvent(AuthEvent event, Emitter<AuthState> emit) {
return event.map(
fetchCurrentUser: (e) async {
emit(state.copyWith(failureOption: none()));
final token = await _repository.hasToken();
final failureOrAuth = await _repository.currentUser();
failureOrAuth.fold(
(f) => emit(
state.copyWith(
failureOption: optionOf(f),
status: token
? AuthStatus.authenticated()
: AuthStatus.unauthenticated(),
),
),
(user) => emit(
state.copyWith(
user: user,
status: token
? AuthStatus.authenticated()
: AuthStatus.unauthenticated(),
),
),
);
add(AuthEvent.fetchCurrentOutlet(state.user.outletId));
},
fetchCurrentOutlet: (e) async {
emit(state.copyWith(failureOutletOption: none()));
final failureOrOutlet = await _outletRepository.getOutletById(
e.outletId,
);
failureOrOutlet.fold(
(f) => emit(state.copyWith(failureOutletOption: optionOf(f))),
(outlet) => emit(state.copyWith(outlet: outlet)),
);
},
);
}
}
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
part of 'auth_bloc.dart';
@freezed
class AuthEvent with _$AuthEvent {
const factory AuthEvent.fetchCurrentUser() = _FetchCurrentUser;
const factory AuthEvent.fetchCurrentOutlet(String outletId) =
_FetchCurrentOutlet;
}
+33
View File
@@ -0,0 +1,33 @@
part of 'auth_bloc.dart';
@freezed
class AuthState with _$AuthState {
const AuthState._();
const factory AuthState({
required User user,
required Outlet outlet,
@Default(AuthStatus.initial()) AuthStatus status,
required Option<AuthFailure> failureOption,
required Option<OutletFailure> failureOutletOption,
@Default(false) bool isFetching,
@Default(false) bool isFetchingOutlet,
}) = _AuthState;
factory AuthState.initial() => AuthState(
user: User.empty(),
failureOption: none(),
outlet: Outlet.empty(),
failureOutletOption: none(),
);
bool get isAuthenticated => status == const AuthStatus.authenticated();
bool get isInitial => status == const AuthStatus.initial();
}
@freezed
sealed class AuthStatus with _$AuthStatus {
const factory AuthStatus.authenticated() = _Authenticated;
const factory AuthStatus.unauthenticated() = _Unauthenticated;
const factory AuthStatus.initial() = _Initial;
}
@@ -0,0 +1,62 @@
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/auth/auth.dart';
part 'login_form_event.dart';
part 'login_form_state.dart';
part 'login_form_bloc.freezed.dart';
@injectable
class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
final IAuthRepository _authRepository;
LoginFormBloc(this._authRepository) : super(LoginFormState.initial()) {
on<LoginFormEvent>(_onLoginFormEvent);
}
Future<void> _onLoginFormEvent(
LoginFormEvent event,
Emitter<LoginFormState> emit,
) {
return event.map(
emailChanged: (e) async {
emit(state.copyWith(email: e.email, failureOrLoginOption: none()));
},
passwordChanged: (e) async {
emit(
state.copyWith(password: e.password, failureOrLoginOption: none()),
);
},
submitted: (e) async {
Either<AuthFailure, Login>? failureOrLogin;
emit(state.copyWith(isSubmitting: true, failureOrLoginOption: none()));
final emailValid = state.email.isNotEmpty;
final passwordValid = state.password.isNotEmpty;
log(
'emailValid: $emailValid, passwordValid: $passwordValid, email: ${state.email}, password: ${state.password}',
);
if (emailValid && passwordValid) {
failureOrLogin = await _authRepository.login(
email: state.email,
password: state.password,
);
emit(
state.copyWith(
isSubmitting: false,
failureOrLoginOption: optionOf(failureOrLogin),
),
);
}
emit(state.copyWith(showErrorMessages: true, isSubmitting: false));
},
);
}
}
@@ -0,0 +1,734 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'login_form_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
/// @nodoc
mixin _$LoginFormEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String email) emailChanged,
required TResult Function(String password) passwordChanged,
required TResult Function() submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String email)? emailChanged,
TResult? Function(String password)? passwordChanged,
TResult? Function()? submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String email)? emailChanged,
TResult Function(String password)? passwordChanged,
TResult Function()? submitted,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_EmailChanged value) emailChanged,
required TResult Function(_PasswordChanged value) passwordChanged,
required TResult Function(_Submitted value) submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_EmailChanged value)? emailChanged,
TResult? Function(_PasswordChanged value)? passwordChanged,
TResult? Function(_Submitted value)? submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_EmailChanged value)? emailChanged,
TResult Function(_PasswordChanged value)? passwordChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoginFormEventCopyWith<$Res> {
factory $LoginFormEventCopyWith(
LoginFormEvent value,
$Res Function(LoginFormEvent) then,
) = _$LoginFormEventCopyWithImpl<$Res, LoginFormEvent>;
}
/// @nodoc
class _$LoginFormEventCopyWithImpl<$Res, $Val extends LoginFormEvent>
implements $LoginFormEventCopyWith<$Res> {
_$LoginFormEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$EmailChangedImplCopyWith<$Res> {
factory _$$EmailChangedImplCopyWith(
_$EmailChangedImpl value,
$Res Function(_$EmailChangedImpl) then,
) = __$$EmailChangedImplCopyWithImpl<$Res>;
@useResult
$Res call({String email});
}
/// @nodoc
class __$$EmailChangedImplCopyWithImpl<$Res>
extends _$LoginFormEventCopyWithImpl<$Res, _$EmailChangedImpl>
implements _$$EmailChangedImplCopyWith<$Res> {
__$$EmailChangedImplCopyWithImpl(
_$EmailChangedImpl _value,
$Res Function(_$EmailChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? email = null}) {
return _then(
_$EmailChangedImpl(
null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$EmailChangedImpl implements _EmailChanged {
const _$EmailChangedImpl(this.email);
@override
final String email;
@override
String toString() {
return 'LoginFormEvent.emailChanged(email: $email)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$EmailChangedImpl &&
(identical(other.email, email) || other.email == email));
}
@override
int get hashCode => Object.hash(runtimeType, email);
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$EmailChangedImplCopyWith<_$EmailChangedImpl> get copyWith =>
__$$EmailChangedImplCopyWithImpl<_$EmailChangedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String email) emailChanged,
required TResult Function(String password) passwordChanged,
required TResult Function() submitted,
}) {
return emailChanged(email);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String email)? emailChanged,
TResult? Function(String password)? passwordChanged,
TResult? Function()? submitted,
}) {
return emailChanged?.call(email);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String email)? emailChanged,
TResult Function(String password)? passwordChanged,
TResult Function()? submitted,
required TResult orElse(),
}) {
if (emailChanged != null) {
return emailChanged(email);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_EmailChanged value) emailChanged,
required TResult Function(_PasswordChanged value) passwordChanged,
required TResult Function(_Submitted value) submitted,
}) {
return emailChanged(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_EmailChanged value)? emailChanged,
TResult? Function(_PasswordChanged value)? passwordChanged,
TResult? Function(_Submitted value)? submitted,
}) {
return emailChanged?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_EmailChanged value)? emailChanged,
TResult Function(_PasswordChanged value)? passwordChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) {
if (emailChanged != null) {
return emailChanged(this);
}
return orElse();
}
}
abstract class _EmailChanged implements LoginFormEvent {
const factory _EmailChanged(final String email) = _$EmailChangedImpl;
String get email;
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$EmailChangedImplCopyWith<_$EmailChangedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$PasswordChangedImplCopyWith<$Res> {
factory _$$PasswordChangedImplCopyWith(
_$PasswordChangedImpl value,
$Res Function(_$PasswordChangedImpl) then,
) = __$$PasswordChangedImplCopyWithImpl<$Res>;
@useResult
$Res call({String password});
}
/// @nodoc
class __$$PasswordChangedImplCopyWithImpl<$Res>
extends _$LoginFormEventCopyWithImpl<$Res, _$PasswordChangedImpl>
implements _$$PasswordChangedImplCopyWith<$Res> {
__$$PasswordChangedImplCopyWithImpl(
_$PasswordChangedImpl _value,
$Res Function(_$PasswordChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? password = null}) {
return _then(
_$PasswordChangedImpl(
null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$PasswordChangedImpl implements _PasswordChanged {
const _$PasswordChangedImpl(this.password);
@override
final String password;
@override
String toString() {
return 'LoginFormEvent.passwordChanged(password: $password)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PasswordChangedImpl &&
(identical(other.password, password) ||
other.password == password));
}
@override
int get hashCode => Object.hash(runtimeType, password);
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PasswordChangedImplCopyWith<_$PasswordChangedImpl> get copyWith =>
__$$PasswordChangedImplCopyWithImpl<_$PasswordChangedImpl>(
this,
_$identity,
);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String email) emailChanged,
required TResult Function(String password) passwordChanged,
required TResult Function() submitted,
}) {
return passwordChanged(password);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String email)? emailChanged,
TResult? Function(String password)? passwordChanged,
TResult? Function()? submitted,
}) {
return passwordChanged?.call(password);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String email)? emailChanged,
TResult Function(String password)? passwordChanged,
TResult Function()? submitted,
required TResult orElse(),
}) {
if (passwordChanged != null) {
return passwordChanged(password);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_EmailChanged value) emailChanged,
required TResult Function(_PasswordChanged value) passwordChanged,
required TResult Function(_Submitted value) submitted,
}) {
return passwordChanged(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_EmailChanged value)? emailChanged,
TResult? Function(_PasswordChanged value)? passwordChanged,
TResult? Function(_Submitted value)? submitted,
}) {
return passwordChanged?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_EmailChanged value)? emailChanged,
TResult Function(_PasswordChanged value)? passwordChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) {
if (passwordChanged != null) {
return passwordChanged(this);
}
return orElse();
}
}
abstract class _PasswordChanged implements LoginFormEvent {
const factory _PasswordChanged(final String password) = _$PasswordChangedImpl;
String get password;
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PasswordChangedImplCopyWith<_$PasswordChangedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$SubmittedImplCopyWith<$Res> {
factory _$$SubmittedImplCopyWith(
_$SubmittedImpl value,
$Res Function(_$SubmittedImpl) then,
) = __$$SubmittedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SubmittedImplCopyWithImpl<$Res>
extends _$LoginFormEventCopyWithImpl<$Res, _$SubmittedImpl>
implements _$$SubmittedImplCopyWith<$Res> {
__$$SubmittedImplCopyWithImpl(
_$SubmittedImpl _value,
$Res Function(_$SubmittedImpl) _then,
) : super(_value, _then);
/// Create a copy of LoginFormEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SubmittedImpl implements _Submitted {
const _$SubmittedImpl();
@override
String toString() {
return 'LoginFormEvent.submitted()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SubmittedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String email) emailChanged,
required TResult Function(String password) passwordChanged,
required TResult Function() submitted,
}) {
return submitted();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String email)? emailChanged,
TResult? Function(String password)? passwordChanged,
TResult? Function()? submitted,
}) {
return submitted?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String email)? emailChanged,
TResult Function(String password)? passwordChanged,
TResult Function()? submitted,
required TResult orElse(),
}) {
if (submitted != null) {
return submitted();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_EmailChanged value) emailChanged,
required TResult Function(_PasswordChanged value) passwordChanged,
required TResult Function(_Submitted value) submitted,
}) {
return submitted(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_EmailChanged value)? emailChanged,
TResult? Function(_PasswordChanged value)? passwordChanged,
TResult? Function(_Submitted value)? submitted,
}) {
return submitted?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_EmailChanged value)? emailChanged,
TResult Function(_PasswordChanged value)? passwordChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) {
if (submitted != null) {
return submitted(this);
}
return orElse();
}
}
abstract class _Submitted implements LoginFormEvent {
const factory _Submitted() = _$SubmittedImpl;
}
/// @nodoc
mixin _$LoginFormState {
String get email => throw _privateConstructorUsedError;
String get password => throw _privateConstructorUsedError;
Option<Either<AuthFailure, Login>> get failureOrLoginOption =>
throw _privateConstructorUsedError;
bool get isSubmitting => throw _privateConstructorUsedError;
bool get showErrorMessages => throw _privateConstructorUsedError;
/// Create a copy of LoginFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LoginFormStateCopyWith<LoginFormState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoginFormStateCopyWith<$Res> {
factory $LoginFormStateCopyWith(
LoginFormState value,
$Res Function(LoginFormState) then,
) = _$LoginFormStateCopyWithImpl<$Res, LoginFormState>;
@useResult
$Res call({
String email,
String password,
Option<Either<AuthFailure, Login>> failureOrLoginOption,
bool isSubmitting,
bool showErrorMessages,
});
}
/// @nodoc
class _$LoginFormStateCopyWithImpl<$Res, $Val extends LoginFormState>
implements $LoginFormStateCopyWith<$Res> {
_$LoginFormStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LoginFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? email = null,
Object? password = null,
Object? failureOrLoginOption = null,
Object? isSubmitting = null,
Object? showErrorMessages = null,
}) {
return _then(
_value.copyWith(
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
password: null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
failureOrLoginOption: null == failureOrLoginOption
? _value.failureOrLoginOption
: failureOrLoginOption // ignore: cast_nullable_to_non_nullable
as Option<Either<AuthFailure, Login>>,
isSubmitting: null == isSubmitting
? _value.isSubmitting
: isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,
showErrorMessages: null == showErrorMessages
? _value.showErrorMessages
: showErrorMessages // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$LoginFormStateImplCopyWith<$Res>
implements $LoginFormStateCopyWith<$Res> {
factory _$$LoginFormStateImplCopyWith(
_$LoginFormStateImpl value,
$Res Function(_$LoginFormStateImpl) then,
) = __$$LoginFormStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
String email,
String password,
Option<Either<AuthFailure, Login>> failureOrLoginOption,
bool isSubmitting,
bool showErrorMessages,
});
}
/// @nodoc
class __$$LoginFormStateImplCopyWithImpl<$Res>
extends _$LoginFormStateCopyWithImpl<$Res, _$LoginFormStateImpl>
implements _$$LoginFormStateImplCopyWith<$Res> {
__$$LoginFormStateImplCopyWithImpl(
_$LoginFormStateImpl _value,
$Res Function(_$LoginFormStateImpl) _then,
) : super(_value, _then);
/// Create a copy of LoginFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? email = null,
Object? password = null,
Object? failureOrLoginOption = null,
Object? isSubmitting = null,
Object? showErrorMessages = null,
}) {
return _then(
_$LoginFormStateImpl(
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
password: null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
failureOrLoginOption: null == failureOrLoginOption
? _value.failureOrLoginOption
: failureOrLoginOption // ignore: cast_nullable_to_non_nullable
as Option<Either<AuthFailure, Login>>,
isSubmitting: null == isSubmitting
? _value.isSubmitting
: isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,
showErrorMessages: null == showErrorMessages
? _value.showErrorMessages
: showErrorMessages // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$LoginFormStateImpl implements _LoginFormState {
const _$LoginFormStateImpl({
required this.email,
required this.password,
required this.failureOrLoginOption,
this.isSubmitting = false,
this.showErrorMessages = false,
});
@override
final String email;
@override
final String password;
@override
final Option<Either<AuthFailure, Login>> failureOrLoginOption;
@override
@JsonKey()
final bool isSubmitting;
@override
@JsonKey()
final bool showErrorMessages;
@override
String toString() {
return 'LoginFormState(email: $email, password: $password, failureOrLoginOption: $failureOrLoginOption, isSubmitting: $isSubmitting, showErrorMessages: $showErrorMessages)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoginFormStateImpl &&
(identical(other.email, email) || other.email == email) &&
(identical(other.password, password) ||
other.password == password) &&
(identical(other.failureOrLoginOption, failureOrLoginOption) ||
other.failureOrLoginOption == failureOrLoginOption) &&
(identical(other.isSubmitting, isSubmitting) ||
other.isSubmitting == isSubmitting) &&
(identical(other.showErrorMessages, showErrorMessages) ||
other.showErrorMessages == showErrorMessages));
}
@override
int get hashCode => Object.hash(
runtimeType,
email,
password,
failureOrLoginOption,
isSubmitting,
showErrorMessages,
);
/// Create a copy of LoginFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoginFormStateImplCopyWith<_$LoginFormStateImpl> get copyWith =>
__$$LoginFormStateImplCopyWithImpl<_$LoginFormStateImpl>(
this,
_$identity,
);
}
abstract class _LoginFormState implements LoginFormState {
const factory _LoginFormState({
required final String email,
required final String password,
required final Option<Either<AuthFailure, Login>> failureOrLoginOption,
final bool isSubmitting,
final bool showErrorMessages,
}) = _$LoginFormStateImpl;
@override
String get email;
@override
String get password;
@override
Option<Either<AuthFailure, Login>> get failureOrLoginOption;
@override
bool get isSubmitting;
@override
bool get showErrorMessages;
/// Create a copy of LoginFormState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoginFormStateImplCopyWith<_$LoginFormStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,9 @@
part of 'login_form_bloc.dart';
@freezed
class LoginFormEvent with _$LoginFormEvent {
const factory LoginFormEvent.emailChanged(String email) = _EmailChanged;
const factory LoginFormEvent.passwordChanged(String password) =
_PasswordChanged;
const factory LoginFormEvent.submitted() = _Submitted;
}
@@ -0,0 +1,15 @@
part of 'login_form_bloc.dart';
@freezed
class LoginFormState with _$LoginFormState {
const factory LoginFormState({
required String email,
required String password,
required Option<Either<AuthFailure, Login>> failureOrLoginOption,
@Default(false) bool isSubmitting,
@Default(false) bool showErrorMessages,
}) = _LoginFormState;
factory LoginFormState.initial() =>
LoginFormState(email: '', password: '', failureOrLoginOption: none());
}
+243
View File
@@ -0,0 +1,243 @@
// ignore: depend_on_referenced_packages
import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:injectable/injectable.dart';
import '../../env.dart';
import 'api_failure.dart';
import 'errors/bad_network_error.dart';
import 'errors/bad_request_error.dart';
import 'errors/connection_timeout_error.dart';
import 'errors/internal_server_error.dart';
import 'errors/not_found_error.dart';
import 'errors/unauthorized_error.dart';
import 'interceptors/bad_network_interceptor.dart';
import 'interceptors/bad_request_interceptor.dart';
import 'interceptors/connection_timeout_interceptor.dart';
import 'interceptors/crashlytic_interceptor.dart';
import 'interceptors/internal_server_interceptor.dart';
import 'interceptors/not_found_interceptor.dart';
import 'interceptors/unauthorized_interceptor.dart';
@lazySingleton
class ApiClient {
final Dio _dio;
final Env _env;
ApiClient(this._dio, this._env) {
_dio.options.baseUrl = _env.baseUrl;
_dio.options.connectTimeout = const Duration(seconds: 20);
_dio.options.validateStatus = (status) =>
status != null && status >= 200 && status < 500;
_dio.interceptors.add(BadNetworkErrorInterceptor());
_dio.interceptors.add(BadRequestErrorInterceptor());
_dio.interceptors.add(InternalServerErrorInterceptor());
_dio.interceptors.add(NotFoundErrorInterceptor());
_dio.interceptors.add(UnauthorizedInterceptor());
_dio.interceptors.add(ConnectionTimeoutErrorInterceptor());
_dio.interceptors.add(CrashlyticsInterceptor());
if (kDebugMode) {
_dio.interceptors.add(
AwesomeDioInterceptor(
logResponseHeaders: false,
logRequestTimeout: false,
logRequestHeaders: true,
),
);
}
}
Future<Response> post(
String path, {
dynamic data,
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
}) async {
try {
return await _dio.post(
path,
data: data,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on ConnectionTimeoutError {
throw const ApiFailure.connectionTimeout();
} on DioException catch (e) {
var errorMessage =
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error;
if (errorMessage.toString().contains('Connection reset')) {
errorMessage = 'Connection reset';
}
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage: errorMessage.toString(),
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
Future<Response> get(
String path, {
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
}) async {
try {
return await _dio.get(
path,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on ConnectionTimeoutError {
throw const ApiFailure.connectionTimeout();
} on DioException catch (e) {
var errorMessage =
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error;
if (errorMessage.toString().contains('Connection reset')) {
errorMessage = 'Connection reset';
}
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage: errorMessage.toString(),
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
Future<Response> put(
String path, {
dynamic data,
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
void Function(int count, int total)? onSendProgress,
}) async {
try {
return await _dio.put(
path,
data: data,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
onSendProgress: onSendProgress,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on ConnectionTimeoutError {
throw const ApiFailure.connectionTimeout();
} on DioException catch (e) {
var errorMessage =
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error;
if (errorMessage.toString().contains('Connection reset')) {
errorMessage = 'Connection reset';
}
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage: errorMessage.toString(),
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
Future<Response> delete(
String path, {
dynamic data,
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
}) async {
try {
return await _dio.delete(
path,
data: data,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on DioException catch (e) {
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage:
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error,
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
}
+58
View File
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'api_failure.freezed.dart';
@freezed
sealed class ApiFailure with _$ApiFailure {
const ApiFailure._();
const factory ApiFailure.serverError({
required int statusCode,
required Object errorMessage,
}) = _ServerError;
const factory ApiFailure.unexpectedError({
required Object errorMessage,
required StackTrace stackTrace,
}) = _UnexpectedError;
const factory ApiFailure.connectionError() = _ConnectionError;
const factory ApiFailure.internalServerError() = _InternalServerError;
const factory ApiFailure.unauthorized(String? message) = _Unauthorized;
const factory ApiFailure.badRequest(String? message) = _BadRequest;
const factory ApiFailure.notFound(String? message) = _NotFound;
const factory ApiFailure.connectionTimeout() = _ConnectionTimeout;
String toStringFormatted(
BuildContext context, {
String? unauthorizedMessage,
}) {
return switch (this) {
_ServerError(:final statusCode, :final errorMessage) =>
'There is a problem with the server. Status code: $statusCode Error: $errorMessage',
_UnexpectedError() => 'An error occurred. Please try again later.',
_ConnectionError() => 'No Internet',
_InternalServerError() =>
'The server is experiencing problems. Please try again later.',
_Unauthorized(:final message) =>
message ?? unauthorizedMessage ?? 'Session has expired.',
_BadRequest(:final message) =>
message ?? 'There is an incorrect entry. Please check again',
_NotFound(:final message) => message ?? 'Not Found',
_ConnectionTimeout() => 'Connection Timeout',
};
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
class BadNetworkError extends DioException {
final DioException dioError;
BadNetworkError(this.dioError)
: super(
requestOptions: dioError.requestOptions,
error: dioError.error,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
class BadRequestError extends DioException {
final DioException dioError;
final String? messageError;
BadRequestError(this.dioError, this.messageError)
: super(
error: dioError.error,
requestOptions: dioError.requestOptions,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
class ConnectionTimeoutError extends DioException {
final DioException dioError;
ConnectionTimeoutError(this.dioError)
: super(
error: dioError.error,
requestOptions: dioError.requestOptions,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
class InternalServerError extends DioException {
final DioException dioError;
InternalServerError(this.dioError)
: super(
requestOptions: dioError.requestOptions,
error: dioError.error,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
class NotFoundError extends DioException {
final DioException dioError;
final String? messageError;
NotFoundError(this.dioError, this.messageError)
: super(
error: dioError.error,
requestOptions: dioError.requestOptions,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
class UnauthorizedError extends DioException {
final DioException dioError;
final String? messageError;
UnauthorizedError(this.dioError, this.messageError)
: super(
requestOptions: dioError.requestOptions,
error: dioError.error,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,22 @@
import 'package:dio/dio.dart';
import '../../../injection.dart';
import '../../network/network_client.dart';
import '../errors/bad_network_error.dart';
class BadNetworkErrorInterceptor extends Interceptor {
final _networkClient = getIt<NetworkClient>();
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
final isConnected = await _networkClient.isConnected;
if (err.type == DioExceptionType.connectionTimeout ||
!isConnected ||
err.type == DioExceptionType.receiveTimeout ||
err.type == DioExceptionType.connectionError) {
return super.onError(BadNetworkError(err), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
import '../errors/bad_request_error.dart';
class BadRequestErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 422 ||
err.response?.statusCode == 400 ||
err.response?.statusCode == 405) {
return super.onError(BadRequestError(err, null), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,13 @@
import 'package:dio/dio.dart';
import '../errors/connection_timeout_error.dart';
class ConnectionTimeoutErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.type == DioExceptionType.connectionTimeout) {
return super.onError(ConnectionTimeoutError(err), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,51 @@
import 'package:dio/dio.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
class CrashlyticsInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
// Log semua error ke Crashlytics
FirebaseCrashlytics.instance.recordError(
err,
err.stackTrace,
reason: _getErrorReason(err),
information: _getErrorInformation(err),
fatal: false,
);
super.onError(err, handler);
}
String _getErrorReason(DioException err) {
final statusCode = err.response?.statusCode;
if (statusCode != null) {
return 'HTTP $statusCode Error: ${err.requestOptions.uri.path}';
}
switch (err.type) {
case DioExceptionType.connectionTimeout:
return 'Connection Timeout';
case DioExceptionType.sendTimeout:
return 'Send Timeout';
case DioExceptionType.receiveTimeout:
return 'Receive Timeout';
case DioExceptionType.connectionError:
return 'Connection Error';
case DioExceptionType.cancel:
return 'Request Cancelled';
default:
return 'Network Error: ${err.type.name}';
}
}
List<String> _getErrorInformation(DioException err) {
return [
'URL: ${err.requestOptions.uri}',
'Method: ${err.requestOptions.method}',
'Status Code: ${err.response?.statusCode ?? 'N/A'}',
'Error Type: ${err.type.name}',
'Message: ${err.message ?? 'No message'}',
];
}
}
@@ -0,0 +1,18 @@
import 'package:dio/dio.dart';
import '../errors/internal_server_error.dart';
class InternalServerErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response != null) {
if (err.response?.statusCode != null &&
err.response!.statusCode! >= 500 &&
err.response!.statusCode! < 600) {
return super.onError(InternalServerError(err), handler);
}
}
super.onError(err, handler);
}
}
@@ -0,0 +1,13 @@
import 'package:dio/dio.dart';
import '../errors/not_found_error.dart';
class NotFoundErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 404) {
return super.onError(NotFoundError(err, null), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
import '../errors/unauthorized_error.dart';
class UnauthorizedInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 401 ||
err.response?.statusCode == 403 ||
err.response?.statusCode == 419) {
return super.onError(UnauthorizedError(err, null), handler);
}
super.onError(err, handler);
}
}
+1
View File
@@ -0,0 +1 @@
// TODO: define your code
+3
View File
@@ -0,0 +1,3 @@
class AppConstant {
static const String appName = "Apskel POS";
}
@@ -0,0 +1,5 @@
class LocalStorageKey {
static const String token = 'token';
static const String user = 'user';
static const String outlet = 'outlet';
}
+9
View File
@@ -0,0 +1,9 @@
import 'package:injectable/injectable.dart';
import '../../presentation/router/app_router.dart';
@module
abstract class AutoRouteDi {
@lazySingleton
AppRouter get appRouter => AppRouter();
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:injectable/injectable.dart';
@module
abstract class ConnectivityDi {
@lazySingleton
Connectivity get connectivity => Connectivity();
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
@module
abstract class DioDi {
@lazySingleton
Dio get dio => Dio();
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:injectable/injectable.dart';
import 'package:shared_preferences/shared_preferences.dart';
@module
abstract class SharedPreferencesDi {
@preResolve
Future<SharedPreferences> get prefs => SharedPreferences.getInstance();
}
@@ -0,0 +1,7 @@
part of 'extension.dart';
extension BuildContextExt on BuildContext {
double get deviceHeight => MediaQuery.of(this).size.height;
double get deviceWidth => MediaQuery.of(this).size.width;
}
+3
View File
@@ -0,0 +1,3 @@
import 'package:flutter/material.dart';
part 'build_context_extension.dart';
+19
View File
@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../injection.dart';
import '../constant/local_storage_key.dart';
void dismissKeyboard(BuildContext context) {
final currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
}
Map<String, dynamic> getAuthorizationHeader() {
return {
'Authorization':
'Bearer ${getIt<SharedPreferences>().getString(LocalStorageKey.token)}',
};
}
+19
View File
@@ -0,0 +1,19 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:injectable/injectable.dart';
@lazySingleton
class NetworkClient extends NetworkInfoBase {
final Connectivity connectivity;
NetworkClient(this.connectivity);
@override
Future<bool> get isConnected async {
final result = await connectivity.checkConnectivity();
return result.first != ConnectivityResult.none;
}
}
abstract class NetworkInfoBase {
Future<bool> get isConnected;
}
+68
View File
@@ -0,0 +1,68 @@
part of 'theme.dart';
class AppColor {
// Primary Colors
static const Color primary = Color(0xFF36175e);
static const Color primaryLight = Color(0xFF5a2d85);
static const Color primaryDark = Color(0xFF1e0d35);
// Secondary Colors
static const Color secondary = Color(0xFF4CAF50);
static const Color secondaryLight = Color(0xFF81C784);
static const Color secondaryDark = Color(0xFF388E3C);
// Background Colors
static const Color background = Color(0xFFF8F9FA);
static const Color backgroundLight = Color(0xFFFFFFFF);
static const Color backgroundDark = Color(0xFF1A1A1A);
static const Color surface = Color(0xFFFFFFFF);
static const Color surfaceDark = Color(0xFF2D2D2D);
// Text Colors
static const Color textPrimary = Color(0xFF212121);
static const Color textSecondary = Color(0xFF757575);
static const Color textLight = Color(0xFFBDBDBD);
static const Color textWhite = Color(0xFFFFFFFF);
// Status Colors
static const Color success = Color(0xFF4CAF50);
static const Color error = Color(0xFFE53E3E);
static const Color warning = Color(0xFFFF9800);
static const Color info = Color(0xFF2196F3);
// Border Colors
static const Color border = Color(0xFFE0E0E0);
static const Color borderLight = Color(0xFFF0F0F0);
static const Color borderDark = Color(0xFFBDBDBD);
// Basic Color
static const Color white = Color(0xFFFFFFFF);
static const Color black = Color(0xFF000000);
static const Color disabled = Color(0xFFC8D1E1);
// Gradient Colors
static const List<Color> primaryGradient = [
Color(0xFF36175e),
Color(0xFF5a2d85),
];
static const List<Color> successGradient = [
Color(0xFF4CAF50),
Color(0xFF81C784),
];
static const List<Color> backgroundGradient = [
Color(0xFFF5F5F5),
Color(0xFFE8E8E8),
];
// Opacity Variations
static Color primaryWithOpacity(double opacity) =>
primary.withOpacity(opacity);
static Color successWithOpacity(double opacity) =>
success.withOpacity(opacity);
static Color errorWithOpacity(double opacity) => error.withOpacity(opacity);
static Color warningWithOpacity(double opacity) =>
warning.withOpacity(opacity);
}
+27
View File
@@ -0,0 +1,27 @@
part of 'theme.dart';
class AppStyle {
static TextStyle xs = TextStyle(color: AppColor.black, fontSize: 11);
static TextStyle sm = TextStyle(color: AppColor.black, fontSize: 12);
static TextStyle md = TextStyle(color: AppColor.black, fontSize: 14);
static TextStyle lg = TextStyle(color: AppColor.black, fontSize: 16);
static TextStyle xl = TextStyle(color: AppColor.black, fontSize: 18);
static TextStyle xxl = TextStyle(color: AppColor.black, fontSize: 20);
static TextStyle h6 = TextStyle(color: AppColor.black, fontSize: 22);
static TextStyle h5 = TextStyle(color: AppColor.black, fontSize: 24);
static TextStyle h4 = TextStyle(color: AppColor.black, fontSize: 26);
static TextStyle h3 = TextStyle(color: AppColor.black, fontSize: 28);
static TextStyle h2 = TextStyle(color: AppColor.black, fontSize: 30);
static TextStyle h1 = TextStyle(color: AppColor.black, fontSize: 32);
}
+5
View File
@@ -0,0 +1,5 @@
part of 'theme.dart';
class AppValue {
// TODO: define value
}
+47
View File
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import '../../presentation/components/assets/fonts.gen.dart';
part 'app_color.dart';
part 'app_style.dart';
part 'app_value.dart';
class ThemeApp {
static ThemeData get theme => ThemeData(
useMaterial3: true,
primaryColor: AppColor.primary,
scaffoldBackgroundColor: AppColor.white,
appBarTheme: AppBarTheme(
color: AppColor.white,
elevation: 0,
titleTextStyle: TextStyle(
color: AppColor.primary,
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
iconTheme: const IconThemeData(color: AppColor.primary),
),
fontFamily: FontFamily.quicksand,
colorScheme: ColorScheme.fromSeed(seedColor: AppColor.primary),
inputDecorationTheme: InputDecorationTheme(
contentPadding: const EdgeInsets.symmetric(horizontal: 16.0),
hintStyle: const TextStyle(color: AppColor.textSecondary),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: AppColor.borderDark),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: AppColor.primary),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: AppColor.borderDark),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: AppColor.error),
),
),
);
}
+4
View File
@@ -0,0 +1,4 @@
class ApiPath {
static const String login = '/api/v1/auth/login';
static const String outlets = '/api/v1/outlets';
}
+28
View File
@@ -0,0 +1,28 @@
class AppValidator {
static String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email wajib diisi';
}
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(value)) {
return 'Format email tidak valid';
}
return null;
}
static String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password wajib diisi';
}
if (value.length < 8) {
return 'Password minimal 8 karakter';
}
// if (!RegExp(r'[A-Z]').hasMatch(value)) {
// return 'Password harus mengandung huruf besar';
// }
// if (!RegExp(r'[0-9]').hasMatch(value)) {
// return 'Password harus mengandung angka';
// }
return null;
}
}
+11
View File
@@ -0,0 +1,11 @@
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../common/api/api_failure.dart';
part 'auth.freezed.dart';
part 'entities/login_entity.dart';
part 'entities/user_entity.dart';
part 'failures/auth_failure.dart';
part 'repositories/i_auth_repository.dart';
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,20 @@
part of '../auth.dart';
@freezed
class Login with _$Login {
const factory Login({
required String token,
required String refreshToken,
required String expiresAt,
required String refreshExpiresAt,
required User user,
}) = _Login;
factory Login.empty() => Login(
token: '',
refreshToken: '',
expiresAt: '',
refreshExpiresAt: '',
user: User.empty(),
);
}
+30
View File
@@ -0,0 +1,30 @@
part of '../auth.dart';
@freezed
class User with _$User {
const factory User({
required String id,
required String organizationId,
required String outletId,
required String name,
required String email,
required String role,
required Map<String, dynamic> permissions,
required bool isActive,
required String createdAt,
required String updatedAt,
}) = _User;
factory User.empty() => const User(
id: '',
organizationId: '',
outletId: '',
name: '',
email: '',
role: '',
permissions: {},
isActive: false,
createdAt: '',
updatedAt: '',
);
}
@@ -0,0 +1,9 @@
part of '../auth.dart';
@freezed
sealed class AuthFailure with _$AuthFailure {
const factory AuthFailure.serverError(ApiFailure failure) = _ServerError;
const factory AuthFailure.unexpectedError() = _UnexpectedError;
const factory AuthFailure.dynamicErrorMessage(String erroMessage) =
_DynamicErrorMessage;
}
@@ -0,0 +1,10 @@
part of '../auth.dart';
abstract class IAuthRepository {
Future<Either<AuthFailure, Login>> login({
required String email,
required String password,
});
Future<bool> hasToken();
Future<Either<AuthFailure, User>> currentUser();
}
@@ -0,0 +1,32 @@
part of '../outlet.dart';
@freezed
class Outlet with _$Outlet {
const factory Outlet({
required String id,
required String organizationId,
required String name,
required String address,
required String phoneNumber,
required String businessType,
required String currency,
required double taxRate,
required bool isActive,
required String createdAt,
required String updatedAt,
}) = _Outlet;
factory Outlet.empty() => const Outlet(
id: '',
organizationId: '',
name: '',
address: '',
phoneNumber: '',
businessType: '',
currency: '',
taxRate: 0.0,
isActive: false,
createdAt: '',
updatedAt: '',
);
}
@@ -0,0 +1,10 @@
part of '../outlet.dart';
@freezed
sealed class OutletFailure with _$OutletFailure {
const factory OutletFailure.serverError(ApiFailure failure) = _ServerError;
const factory OutletFailure.unexpectedError() = _UnexpectedError;
const factory OutletFailure.empty() = _Empty;
const factory OutletFailure.dynamicErrorMessage(String erroMessage) =
_DynamicErrorMessage;
}
+10
View File
@@ -0,0 +1,10 @@
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../common/api/api_failure.dart';
part 'outlet.freezed.dart';
part 'failures/outlet_failure.dart';
part 'entities/outlet_entity.dart';
part 'repositories/i_outlet_repository.dart';
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
part of '../outlet.dart';
abstract class IOutletRepository {
Future<Either<OutletFailure, List<Outlet>>> getOutlets({
int page = 1,
int limit = 10,
});
Future<Either<OutletFailure, Outlet>> getOutletById(String id);
}
+20
View File
@@ -0,0 +1,20 @@
import 'package:injectable/injectable.dart';
abstract class Env {
String get baseUrl;
// add getter here...
}
@Injectable(as: Env)
@dev
class DevEnv implements Env {
@override
String get baseUrl => 'https://api-pos.apskel.id'; // example value
}
@Injectable(as: Env)
@prod
class ProdEnv implements Env {
@override
String get baseUrl => 'https://api-pos.apskel.id';
}
+9
View File
@@ -0,0 +1,9 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../domain/auth/auth.dart';
part 'auth_dtos.freezed.dart';
part 'auth_dtos.g.dart';
part 'dto/login_dto.dart';
part 'dto/user_dto.dart';
@@ -0,0 +1,700 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'auth_dtos.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
LoginDto _$LoginDtoFromJson(Map<String, dynamic> json) {
return _LoginDto.fromJson(json);
}
/// @nodoc
mixin _$LoginDto {
@JsonKey(name: "token")
String? get token => throw _privateConstructorUsedError;
@JsonKey(name: "refresh_token")
String? get refreshToken => throw _privateConstructorUsedError;
@JsonKey(name: "expires_at")
String? get expiresAt => throw _privateConstructorUsedError;
@JsonKey(name: "refresh_expires_at")
String? get refreshExpiresAt => throw _privateConstructorUsedError;
@JsonKey(name: "user")
UserDto? get user => throw _privateConstructorUsedError;
/// Serializes this LoginDto to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of LoginDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LoginDtoCopyWith<LoginDto> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoginDtoCopyWith<$Res> {
factory $LoginDtoCopyWith(LoginDto value, $Res Function(LoginDto) then) =
_$LoginDtoCopyWithImpl<$Res, LoginDto>;
@useResult
$Res call({
@JsonKey(name: "token") String? token,
@JsonKey(name: "refresh_token") String? refreshToken,
@JsonKey(name: "expires_at") String? expiresAt,
@JsonKey(name: "refresh_expires_at") String? refreshExpiresAt,
@JsonKey(name: "user") UserDto? user,
});
$UserDtoCopyWith<$Res>? get user;
}
/// @nodoc
class _$LoginDtoCopyWithImpl<$Res, $Val extends LoginDto>
implements $LoginDtoCopyWith<$Res> {
_$LoginDtoCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LoginDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? token = freezed,
Object? refreshToken = freezed,
Object? expiresAt = freezed,
Object? refreshExpiresAt = freezed,
Object? user = freezed,
}) {
return _then(
_value.copyWith(
token: freezed == token
? _value.token
: token // ignore: cast_nullable_to_non_nullable
as String?,
refreshToken: freezed == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String?,
expiresAt: freezed == expiresAt
? _value.expiresAt
: expiresAt // ignore: cast_nullable_to_non_nullable
as String?,
refreshExpiresAt: freezed == refreshExpiresAt
? _value.refreshExpiresAt
: refreshExpiresAt // ignore: cast_nullable_to_non_nullable
as String?,
user: freezed == user
? _value.user
: user // ignore: cast_nullable_to_non_nullable
as UserDto?,
)
as $Val,
);
}
/// Create a copy of LoginDto
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$UserDtoCopyWith<$Res>? get user {
if (_value.user == null) {
return null;
}
return $UserDtoCopyWith<$Res>(_value.user!, (value) {
return _then(_value.copyWith(user: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$LoginDtoImplCopyWith<$Res>
implements $LoginDtoCopyWith<$Res> {
factory _$$LoginDtoImplCopyWith(
_$LoginDtoImpl value,
$Res Function(_$LoginDtoImpl) then,
) = __$$LoginDtoImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
@JsonKey(name: "token") String? token,
@JsonKey(name: "refresh_token") String? refreshToken,
@JsonKey(name: "expires_at") String? expiresAt,
@JsonKey(name: "refresh_expires_at") String? refreshExpiresAt,
@JsonKey(name: "user") UserDto? user,
});
@override
$UserDtoCopyWith<$Res>? get user;
}
/// @nodoc
class __$$LoginDtoImplCopyWithImpl<$Res>
extends _$LoginDtoCopyWithImpl<$Res, _$LoginDtoImpl>
implements _$$LoginDtoImplCopyWith<$Res> {
__$$LoginDtoImplCopyWithImpl(
_$LoginDtoImpl _value,
$Res Function(_$LoginDtoImpl) _then,
) : super(_value, _then);
/// Create a copy of LoginDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? token = freezed,
Object? refreshToken = freezed,
Object? expiresAt = freezed,
Object? refreshExpiresAt = freezed,
Object? user = freezed,
}) {
return _then(
_$LoginDtoImpl(
token: freezed == token
? _value.token
: token // ignore: cast_nullable_to_non_nullable
as String?,
refreshToken: freezed == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String?,
expiresAt: freezed == expiresAt
? _value.expiresAt
: expiresAt // ignore: cast_nullable_to_non_nullable
as String?,
refreshExpiresAt: freezed == refreshExpiresAt
? _value.refreshExpiresAt
: refreshExpiresAt // ignore: cast_nullable_to_non_nullable
as String?,
user: freezed == user
? _value.user
: user // ignore: cast_nullable_to_non_nullable
as UserDto?,
),
);
}
}
/// @nodoc
@JsonSerializable()
class _$LoginDtoImpl extends _LoginDto {
const _$LoginDtoImpl({
@JsonKey(name: "token") this.token,
@JsonKey(name: "refresh_token") this.refreshToken,
@JsonKey(name: "expires_at") this.expiresAt,
@JsonKey(name: "refresh_expires_at") this.refreshExpiresAt,
@JsonKey(name: "user") this.user,
}) : super._();
factory _$LoginDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$LoginDtoImplFromJson(json);
@override
@JsonKey(name: "token")
final String? token;
@override
@JsonKey(name: "refresh_token")
final String? refreshToken;
@override
@JsonKey(name: "expires_at")
final String? expiresAt;
@override
@JsonKey(name: "refresh_expires_at")
final String? refreshExpiresAt;
@override
@JsonKey(name: "user")
final UserDto? user;
@override
String toString() {
return 'LoginDto(token: $token, refreshToken: $refreshToken, expiresAt: $expiresAt, refreshExpiresAt: $refreshExpiresAt, user: $user)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoginDtoImpl &&
(identical(other.token, token) || other.token == token) &&
(identical(other.refreshToken, refreshToken) ||
other.refreshToken == refreshToken) &&
(identical(other.expiresAt, expiresAt) ||
other.expiresAt == expiresAt) &&
(identical(other.refreshExpiresAt, refreshExpiresAt) ||
other.refreshExpiresAt == refreshExpiresAt) &&
(identical(other.user, user) || other.user == user));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
token,
refreshToken,
expiresAt,
refreshExpiresAt,
user,
);
/// Create a copy of LoginDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoginDtoImplCopyWith<_$LoginDtoImpl> get copyWith =>
__$$LoginDtoImplCopyWithImpl<_$LoginDtoImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LoginDtoImplToJson(this);
}
}
abstract class _LoginDto extends LoginDto {
const factory _LoginDto({
@JsonKey(name: "token") final String? token,
@JsonKey(name: "refresh_token") final String? refreshToken,
@JsonKey(name: "expires_at") final String? expiresAt,
@JsonKey(name: "refresh_expires_at") final String? refreshExpiresAt,
@JsonKey(name: "user") final UserDto? user,
}) = _$LoginDtoImpl;
const _LoginDto._() : super._();
factory _LoginDto.fromJson(Map<String, dynamic> json) =
_$LoginDtoImpl.fromJson;
@override
@JsonKey(name: "token")
String? get token;
@override
@JsonKey(name: "refresh_token")
String? get refreshToken;
@override
@JsonKey(name: "expires_at")
String? get expiresAt;
@override
@JsonKey(name: "refresh_expires_at")
String? get refreshExpiresAt;
@override
@JsonKey(name: "user")
UserDto? get user;
/// Create a copy of LoginDto
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoginDtoImplCopyWith<_$LoginDtoImpl> get copyWith =>
throw _privateConstructorUsedError;
}
UserDto _$UserDtoFromJson(Map<String, dynamic> json) {
return _UserDto.fromJson(json);
}
/// @nodoc
mixin _$UserDto {
@JsonKey(name: "id")
String? get id => throw _privateConstructorUsedError;
@JsonKey(name: "organization_id")
String? get organizationId => throw _privateConstructorUsedError;
@JsonKey(name: "outlet_id")
String? get outletId => throw _privateConstructorUsedError;
@JsonKey(name: "name")
String? get name => throw _privateConstructorUsedError;
@JsonKey(name: "email")
String? get email => throw _privateConstructorUsedError;
@JsonKey(name: "role")
String? get role => throw _privateConstructorUsedError;
@JsonKey(name: "permissions")
Map<String, dynamic>? get permissions => throw _privateConstructorUsedError;
@JsonKey(name: "is_active")
bool? get isActive => throw _privateConstructorUsedError;
@JsonKey(name: "created_at")
String? get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: "updated_at")
String? get updatedAt => throw _privateConstructorUsedError;
/// Serializes this UserDto to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of UserDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$UserDtoCopyWith<UserDto> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $UserDtoCopyWith<$Res> {
factory $UserDtoCopyWith(UserDto value, $Res Function(UserDto) then) =
_$UserDtoCopyWithImpl<$Res, UserDto>;
@useResult
$Res call({
@JsonKey(name: "id") String? id,
@JsonKey(name: "organization_id") String? organizationId,
@JsonKey(name: "outlet_id") String? outletId,
@JsonKey(name: "name") String? name,
@JsonKey(name: "email") String? email,
@JsonKey(name: "role") String? role,
@JsonKey(name: "permissions") Map<String, dynamic>? permissions,
@JsonKey(name: "is_active") bool? isActive,
@JsonKey(name: "created_at") String? createdAt,
@JsonKey(name: "updated_at") String? updatedAt,
});
}
/// @nodoc
class _$UserDtoCopyWithImpl<$Res, $Val extends UserDto>
implements $UserDtoCopyWith<$Res> {
_$UserDtoCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of UserDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? organizationId = freezed,
Object? outletId = freezed,
Object? name = freezed,
Object? email = freezed,
Object? role = freezed,
Object? permissions = freezed,
Object? isActive = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
return _then(
_value.copyWith(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String?,
organizationId: freezed == organizationId
? _value.organizationId
: organizationId // ignore: cast_nullable_to_non_nullable
as String?,
outletId: freezed == outletId
? _value.outletId
: outletId // ignore: cast_nullable_to_non_nullable
as String?,
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
email: freezed == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String?,
role: freezed == role
? _value.role
: role // ignore: cast_nullable_to_non_nullable
as String?,
permissions: freezed == permissions
? _value.permissions
: permissions // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
isActive: freezed == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as String?,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$UserDtoImplCopyWith<$Res> implements $UserDtoCopyWith<$Res> {
factory _$$UserDtoImplCopyWith(
_$UserDtoImpl value,
$Res Function(_$UserDtoImpl) then,
) = __$$UserDtoImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
@JsonKey(name: "id") String? id,
@JsonKey(name: "organization_id") String? organizationId,
@JsonKey(name: "outlet_id") String? outletId,
@JsonKey(name: "name") String? name,
@JsonKey(name: "email") String? email,
@JsonKey(name: "role") String? role,
@JsonKey(name: "permissions") Map<String, dynamic>? permissions,
@JsonKey(name: "is_active") bool? isActive,
@JsonKey(name: "created_at") String? createdAt,
@JsonKey(name: "updated_at") String? updatedAt,
});
}
/// @nodoc
class __$$UserDtoImplCopyWithImpl<$Res>
extends _$UserDtoCopyWithImpl<$Res, _$UserDtoImpl>
implements _$$UserDtoImplCopyWith<$Res> {
__$$UserDtoImplCopyWithImpl(
_$UserDtoImpl _value,
$Res Function(_$UserDtoImpl) _then,
) : super(_value, _then);
/// Create a copy of UserDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? organizationId = freezed,
Object? outletId = freezed,
Object? name = freezed,
Object? email = freezed,
Object? role = freezed,
Object? permissions = freezed,
Object? isActive = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
return _then(
_$UserDtoImpl(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String?,
organizationId: freezed == organizationId
? _value.organizationId
: organizationId // ignore: cast_nullable_to_non_nullable
as String?,
outletId: freezed == outletId
? _value.outletId
: outletId // ignore: cast_nullable_to_non_nullable
as String?,
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
email: freezed == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String?,
role: freezed == role
? _value.role
: role // ignore: cast_nullable_to_non_nullable
as String?,
permissions: freezed == permissions
? _value._permissions
: permissions // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
isActive: freezed == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as String?,
),
);
}
}
/// @nodoc
@JsonSerializable()
class _$UserDtoImpl extends _UserDto {
const _$UserDtoImpl({
@JsonKey(name: "id") this.id,
@JsonKey(name: "organization_id") this.organizationId,
@JsonKey(name: "outlet_id") this.outletId,
@JsonKey(name: "name") this.name,
@JsonKey(name: "email") this.email,
@JsonKey(name: "role") this.role,
@JsonKey(name: "permissions") final Map<String, dynamic>? permissions,
@JsonKey(name: "is_active") this.isActive,
@JsonKey(name: "created_at") this.createdAt,
@JsonKey(name: "updated_at") this.updatedAt,
}) : _permissions = permissions,
super._();
factory _$UserDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$UserDtoImplFromJson(json);
@override
@JsonKey(name: "id")
final String? id;
@override
@JsonKey(name: "organization_id")
final String? organizationId;
@override
@JsonKey(name: "outlet_id")
final String? outletId;
@override
@JsonKey(name: "name")
final String? name;
@override
@JsonKey(name: "email")
final String? email;
@override
@JsonKey(name: "role")
final String? role;
final Map<String, dynamic>? _permissions;
@override
@JsonKey(name: "permissions")
Map<String, dynamic>? get permissions {
final value = _permissions;
if (value == null) return null;
if (_permissions is EqualUnmodifiableMapView) return _permissions;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
@override
@JsonKey(name: "is_active")
final bool? isActive;
@override
@JsonKey(name: "created_at")
final String? createdAt;
@override
@JsonKey(name: "updated_at")
final String? updatedAt;
@override
String toString() {
return 'UserDto(id: $id, organizationId: $organizationId, outletId: $outletId, name: $name, email: $email, role: $role, permissions: $permissions, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$UserDtoImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.organizationId, organizationId) ||
other.organizationId == organizationId) &&
(identical(other.outletId, outletId) ||
other.outletId == outletId) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.email, email) || other.email == email) &&
(identical(other.role, role) || other.role == role) &&
const DeepCollectionEquality().equals(
other._permissions,
_permissions,
) &&
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
id,
organizationId,
outletId,
name,
email,
role,
const DeepCollectionEquality().hash(_permissions),
isActive,
createdAt,
updatedAt,
);
/// Create a copy of UserDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$UserDtoImplCopyWith<_$UserDtoImpl> get copyWith =>
__$$UserDtoImplCopyWithImpl<_$UserDtoImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$UserDtoImplToJson(this);
}
}
abstract class _UserDto extends UserDto {
const factory _UserDto({
@JsonKey(name: "id") final String? id,
@JsonKey(name: "organization_id") final String? organizationId,
@JsonKey(name: "outlet_id") final String? outletId,
@JsonKey(name: "name") final String? name,
@JsonKey(name: "email") final String? email,
@JsonKey(name: "role") final String? role,
@JsonKey(name: "permissions") final Map<String, dynamic>? permissions,
@JsonKey(name: "is_active") final bool? isActive,
@JsonKey(name: "created_at") final String? createdAt,
@JsonKey(name: "updated_at") final String? updatedAt,
}) = _$UserDtoImpl;
const _UserDto._() : super._();
factory _UserDto.fromJson(Map<String, dynamic> json) = _$UserDtoImpl.fromJson;
@override
@JsonKey(name: "id")
String? get id;
@override
@JsonKey(name: "organization_id")
String? get organizationId;
@override
@JsonKey(name: "outlet_id")
String? get outletId;
@override
@JsonKey(name: "name")
String? get name;
@override
@JsonKey(name: "email")
String? get email;
@override
@JsonKey(name: "role")
String? get role;
@override
@JsonKey(name: "permissions")
Map<String, dynamic>? get permissions;
@override
@JsonKey(name: "is_active")
bool? get isActive;
@override
@JsonKey(name: "created_at")
String? get createdAt;
@override
@JsonKey(name: "updated_at")
String? get updatedAt;
/// Create a copy of UserDto
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$UserDtoImplCopyWith<_$UserDtoImpl> get copyWith =>
throw _privateConstructorUsedError;
}

Some files were not shown because too many files have changed in this diff Show More