base project
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
// 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/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.interceptors.add(BadNetworkErrorInterceptor());
|
||||
_dio.interceptors.add(BadRequestErrorInterceptor());
|
||||
_dio.interceptors.add(InternalServerErrorInterceptor());
|
||||
_dio.interceptors.add(NotFoundErrorInterceptor());
|
||||
_dio.interceptors.add(UnauthorizedInterceptor());
|
||||
_dio.interceptors.add(ConnectionTimeoutErrorInterceptor());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// TODO: define your code
|
||||
@@ -0,0 +1,3 @@
|
||||
class AppConstant {
|
||||
static const String appName = "";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../presentation/router/app_router.dart';
|
||||
|
||||
@module
|
||||
abstract class AutoRouteDi {
|
||||
@lazySingleton
|
||||
AppRouter get appRouter => AppRouter();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
@module
|
||||
abstract class DioDi {
|
||||
@lazySingleton
|
||||
Dio get dio => Dio();
|
||||
}
|
||||
@@ -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 @@
|
||||
// TODO: define your code
|
||||
@@ -0,0 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void dismissKeyboard(BuildContext context) {
|
||||
final currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
part of 'theme.dart';
|
||||
|
||||
class AppColor {
|
||||
// Primary Colors (Merah)
|
||||
static const Color primary = Color(0xFFD90000); // #d90000
|
||||
static const Color primaryLight = Color(0xFFFF4D4D); // merah terang
|
||||
static const Color primaryDark = Color(0xFF990000); // merah gelap
|
||||
|
||||
// Secondary Colors (biar tetap harmonis → hijau dipertahankan)
|
||||
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);
|
||||
|
||||
// Gradient Colors
|
||||
static const List<Color> primaryGradient = [
|
||||
Color(0xFFD90000), // primary
|
||||
Color(0xFF990000), // dark red
|
||||
];
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
part of 'theme.dart';
|
||||
|
||||
class AppStyle {
|
||||
static TextStyle xs = TextStyle(color: AppColor.textPrimary, fontSize: 11);
|
||||
|
||||
static TextStyle sm = TextStyle(color: AppColor.textPrimary, fontSize: 12);
|
||||
|
||||
static TextStyle md = TextStyle(color: AppColor.textPrimary, fontSize: 14);
|
||||
|
||||
static TextStyle lg = TextStyle(color: AppColor.textPrimary, fontSize: 16);
|
||||
|
||||
static TextStyle xl = TextStyle(color: AppColor.textPrimary, fontSize: 18);
|
||||
|
||||
static TextStyle xxl = TextStyle(color: AppColor.textPrimary, fontSize: 20);
|
||||
|
||||
static TextStyle h6 = TextStyle(color: AppColor.textPrimary, fontSize: 22);
|
||||
|
||||
static TextStyle h5 = TextStyle(color: AppColor.textPrimary, fontSize: 24);
|
||||
|
||||
static TextStyle h4 = TextStyle(color: AppColor.textPrimary, fontSize: 26);
|
||||
|
||||
static TextStyle h3 = TextStyle(color: AppColor.textPrimary, fontSize: 28);
|
||||
|
||||
static TextStyle h2 = TextStyle(color: AppColor.textPrimary, fontSize: 30);
|
||||
|
||||
static TextStyle h1 = TextStyle(color: AppColor.textPrimary, fontSize: 32);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
part of 'theme.dart';
|
||||
|
||||
class AppValue {}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
part 'app_color.dart';
|
||||
part 'app_style.dart';
|
||||
part 'app_value.dart';
|
||||
|
||||
class ThemeApp {
|
||||
static ThemeData get theme => ThemeData(
|
||||
useMaterial3: true,
|
||||
);
|
||||
}
|
||||
@@ -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 => ''; // example value
|
||||
}
|
||||
|
||||
@Injectable(as: Env)
|
||||
@prod
|
||||
class ProdEnv implements Env {
|
||||
@override
|
||||
String get baseUrl => '';
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// dart format width=80
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// **************************************************************************
|
||||
// InjectableConfigGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// coverage:ignore-file
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:connectivity_plus/connectivity_plus.dart' as _i895;
|
||||
import 'package:dio/dio.dart' as _i361;
|
||||
import 'package:enaklo/common/api/api_client.dart' as _i842;
|
||||
import 'package:enaklo/common/di/di_auto_route.dart' as _i619;
|
||||
import 'package:enaklo/common/di/di_connectivity.dart' as _i644;
|
||||
import 'package:enaklo/common/di/di_dio.dart' as _i842;
|
||||
import 'package:enaklo/common/di/di_shared_preferences.dart' as _i672;
|
||||
import 'package:enaklo/common/network/network_client.dart' as _i109;
|
||||
import 'package:enaklo/env.dart' as _i372;
|
||||
import 'package:enaklo/presentation/router/app_router.dart' as _i698;
|
||||
import 'package:get_it/get_it.dart' as _i174;
|
||||
import 'package:injectable/injectable.dart' as _i526;
|
||||
import 'package:shared_preferences/shared_preferences.dart' as _i460;
|
||||
|
||||
const String _dev = 'dev';
|
||||
const String _prod = 'prod';
|
||||
|
||||
extension GetItInjectableX on _i174.GetIt {
|
||||
// initializes the registration of main-scope dependencies inside of GetIt
|
||||
Future<_i174.GetIt> init({
|
||||
String? environment,
|
||||
_i526.EnvironmentFilter? environmentFilter,
|
||||
}) async {
|
||||
final gh = _i526.GetItHelper(this, environment, environmentFilter);
|
||||
final sharedPreferencesDi = _$SharedPreferencesDi();
|
||||
final dioDi = _$DioDi();
|
||||
final autoRouteDi = _$AutoRouteDi();
|
||||
final connectivityDi = _$ConnectivityDi();
|
||||
await gh.factoryAsync<_i460.SharedPreferences>(
|
||||
() => sharedPreferencesDi.prefs,
|
||||
preResolve: true,
|
||||
);
|
||||
gh.lazySingleton<_i361.Dio>(() => dioDi.dio);
|
||||
gh.lazySingleton<_i698.AppRouter>(() => autoRouteDi.appRouter);
|
||||
gh.lazySingleton<_i895.Connectivity>(() => connectivityDi.connectivity);
|
||||
gh.lazySingleton<_i109.NetworkClient>(
|
||||
() => _i109.NetworkClient(gh<_i895.Connectivity>()),
|
||||
);
|
||||
gh.factory<_i372.Env>(() => _i372.DevEnv(), registerFor: {_dev});
|
||||
gh.factory<_i372.Env>(() => _i372.ProdEnv(), registerFor: {_prod});
|
||||
gh.lazySingleton<_i842.ApiClient>(
|
||||
() => _i842.ApiClient(gh<_i361.Dio>(), gh<_i372.Env>()),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class _$SharedPreferencesDi extends _i672.SharedPreferencesDi {}
|
||||
|
||||
class _$DioDi extends _i842.DioDi {}
|
||||
|
||||
class _$AutoRouteDi extends _i619.AutoRouteDi {}
|
||||
|
||||
class _$ConnectivityDi extends _i644.ConnectivityDi {}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import 'injection.config.dart';
|
||||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
@InjectableInit()
|
||||
Future<void> configureDependencies(String env) => getIt.init(environment: env);
|
||||
+20
-114
@@ -1,122 +1,28 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
import 'injection.dart';
|
||||
import 'presentation/app_widget.dart';
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||
// the application has a purple toolbar. Then, without quitting the app,
|
||||
// try changing the seedColor in the colorScheme below to Colors.green
|
||||
// and then invoke "hot reload" (save your changes or press the "hot
|
||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
||||
// the command line to start the app).
|
||||
//
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// state is not lost during the reload. To reset the state, use hot
|
||||
// restart instead.
|
||||
//
|
||||
// This works for code too, not just values: Most code changes can be
|
||||
// tested with just a hot reload.
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
),
|
||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
||||
);
|
||||
}
|
||||
}
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
),
|
||||
);
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
// This widget is the home page of your application. It is stateful, meaning
|
||||
// that it has a State object (defined below) that contains fields that affect
|
||||
// how it looks.
|
||||
|
||||
// This class is the configuration for the state. It holds the values (in this
|
||||
// case the title) provided by the parent (in this case the App widget) and
|
||||
// used by the build method of the State. Fields in a Widget subclass are
|
||||
// always marked "final".
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
|
||||
void _incrementCounter() {
|
||||
setState(() {
|
||||
// This call to setState tells the Flutter framework that something has
|
||||
// changed in this State, which causes it to rerun the build method below
|
||||
// so that the display can reflect the updated values. If we changed
|
||||
// _counter without calling setState(), then the build method would not be
|
||||
// called again, and so nothing would appear to happen.
|
||||
_counter++;
|
||||
});
|
||||
if (kReleaseMode) {
|
||||
debugPrint = (message, {wrapWidth}) => '';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
// The Flutter framework has been optimized to make rerunning build methods
|
||||
// fast, so that you can just rebuild anything that needs updating rather
|
||||
// than having to individually change instances of widgets.
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// TRY THIS: Try changing the color here to a specific color (to
|
||||
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
|
||||
// change color while the other colors stay the same.
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
// Center is a layout widget. It takes a single child and positions it
|
||||
// in the middle of the parent.
|
||||
child: Column(
|
||||
// Column is also a layout widget. It takes a list of children and
|
||||
// arranges them vertically. By default, it sizes itself to fit its
|
||||
// children horizontally, and tries to be as tall as its parent.
|
||||
//
|
||||
// Column has various properties to control how it sizes itself and
|
||||
// how it positions its children. Here we use mainAxisAlignment to
|
||||
// center the children vertically; the main axis here is the vertical
|
||||
// axis because Columns are vertical (the cross axis would be
|
||||
// horizontal).
|
||||
//
|
||||
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
|
||||
// action in the IDE, or press "p" in the console), to see the
|
||||
// wireframe for each widget.
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text('You have pushed the button this many times:'),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
await configureDependencies(
|
||||
kReleaseMode ? Environment.prod : Environment.dev,
|
||||
);
|
||||
|
||||
runApp(const AppWidget());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../common/theme/theme.dart';
|
||||
import '../common/constant/app_constant.dart';
|
||||
import '../injection.dart';
|
||||
import 'router/app_router.dart';
|
||||
import 'router/app_router_observer.dart';
|
||||
|
||||
class AppWidget extends StatefulWidget {
|
||||
const AppWidget({super.key});
|
||||
|
||||
@override
|
||||
State<AppWidget> createState() => _AppWidgetState();
|
||||
}
|
||||
|
||||
class _AppWidgetState extends State<AppWidget> {
|
||||
final _appRouter = getIt<AppRouter>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: AppConstant.appName,
|
||||
theme: ThemeApp.theme,
|
||||
routerConfig: _appRouter.config(
|
||||
navigatorObservers: () => <NavigatorObserver>[AppRouteObserver()],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// dart format width=80
|
||||
|
||||
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
/// *****************************************************
|
||||
/// FlutterGen
|
||||
/// *****************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class $AssetsImagesGen {
|
||||
const $AssetsImagesGen();
|
||||
|
||||
/// File path: assets/images/launcher.png
|
||||
AssetGenImage get launcher =>
|
||||
const AssetGenImage('assets/images/launcher.png');
|
||||
|
||||
/// File path: assets/images/logo.png
|
||||
AssetGenImage get logo => const AssetGenImage('assets/images/logo.png');
|
||||
|
||||
/// List of all assets
|
||||
List<AssetGenImage> get values => [launcher, logo];
|
||||
}
|
||||
|
||||
class Assets {
|
||||
const Assets._();
|
||||
|
||||
static const $AssetsImagesGen images = $AssetsImagesGen();
|
||||
}
|
||||
|
||||
class AssetGenImage {
|
||||
const AssetGenImage(
|
||||
this._assetName, {
|
||||
this.size,
|
||||
this.flavors = const {},
|
||||
this.animation,
|
||||
});
|
||||
|
||||
final String _assetName;
|
||||
|
||||
final Size? size;
|
||||
final Set<String> flavors;
|
||||
final AssetGenImageAnimation? animation;
|
||||
|
||||
Image image({
|
||||
Key? key,
|
||||
AssetBundle? bundle,
|
||||
ImageFrameBuilder? frameBuilder,
|
||||
ImageErrorWidgetBuilder? errorBuilder,
|
||||
String? semanticLabel,
|
||||
bool excludeFromSemantics = false,
|
||||
double? scale,
|
||||
double? width,
|
||||
double? height,
|
||||
Color? color,
|
||||
Animation<double>? opacity,
|
||||
BlendMode? colorBlendMode,
|
||||
BoxFit? fit,
|
||||
AlignmentGeometry alignment = Alignment.center,
|
||||
ImageRepeat repeat = ImageRepeat.noRepeat,
|
||||
Rect? centerSlice,
|
||||
bool matchTextDirection = false,
|
||||
bool gaplessPlayback = true,
|
||||
bool isAntiAlias = false,
|
||||
String? package,
|
||||
FilterQuality filterQuality = FilterQuality.medium,
|
||||
int? cacheWidth,
|
||||
int? cacheHeight,
|
||||
}) {
|
||||
return Image.asset(
|
||||
_assetName,
|
||||
key: key,
|
||||
bundle: bundle,
|
||||
frameBuilder: frameBuilder,
|
||||
errorBuilder: errorBuilder,
|
||||
semanticLabel: semanticLabel,
|
||||
excludeFromSemantics: excludeFromSemantics,
|
||||
scale: scale,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color,
|
||||
opacity: opacity,
|
||||
colorBlendMode: colorBlendMode,
|
||||
fit: fit,
|
||||
alignment: alignment,
|
||||
repeat: repeat,
|
||||
centerSlice: centerSlice,
|
||||
matchTextDirection: matchTextDirection,
|
||||
gaplessPlayback: gaplessPlayback,
|
||||
isAntiAlias: isAntiAlias,
|
||||
package: package,
|
||||
filterQuality: filterQuality,
|
||||
cacheWidth: cacheWidth,
|
||||
cacheHeight: cacheHeight,
|
||||
);
|
||||
}
|
||||
|
||||
ImageProvider provider({AssetBundle? bundle, String? package}) {
|
||||
return AssetImage(_assetName, bundle: bundle, package: package);
|
||||
}
|
||||
|
||||
String get path => _assetName;
|
||||
|
||||
String get keyName => _assetName;
|
||||
}
|
||||
|
||||
class AssetGenImageAnimation {
|
||||
const AssetGenImageAnimation({
|
||||
required this.isAnimation,
|
||||
required this.duration,
|
||||
required this.frames,
|
||||
});
|
||||
|
||||
final bool isAnimation;
|
||||
final Duration duration;
|
||||
final int frames;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// dart format width=80
|
||||
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
/// *****************************************************
|
||||
/// FlutterGen
|
||||
/// *****************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import
|
||||
|
||||
class FontFamily {
|
||||
FontFamily._();
|
||||
|
||||
/// Font family: Quicksand
|
||||
static const String quicksand = 'Quicksand';
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@RoutePage()
|
||||
class SplashPage extends StatefulWidget {
|
||||
const SplashPage({super.key});
|
||||
|
||||
@override
|
||||
State<SplashPage> createState() => _SplashPageState();
|
||||
}
|
||||
|
||||
class _SplashPageState extends State<SplashPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Text("Splash Page"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'app_router.gr.dart';
|
||||
|
||||
@AutoRouterConfig()
|
||||
class AppRouter extends RootStackRouter {
|
||||
@override
|
||||
List<AutoRoute> get routes => [
|
||||
// Splash
|
||||
AutoRoute(page: SplashRoute.page, initial: true),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// dart format width=80
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// **************************************************************************
|
||||
// AutoRouterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// coverage:ignore-file
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:auto_route/auto_route.dart' as _i2;
|
||||
import 'package:enaklo/presentation/pages/splash/splash_page.dart' as _i1;
|
||||
|
||||
/// generated route for
|
||||
/// [_i1.SplashPage]
|
||||
class SplashRoute extends _i2.PageRouteInfo<void> {
|
||||
const SplashRoute({List<_i2.PageRouteInfo>? children})
|
||||
: super(SplashRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'SplashRoute';
|
||||
|
||||
static _i2.PageInfo page = _i2.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i1.SplashPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppRouteObserver extends AutoRouterObserver {
|
||||
@override
|
||||
void didPush(Route route, Route? previousRoute) {
|
||||
log('New route pushed: ${route.settings.name}');
|
||||
}
|
||||
|
||||
@override
|
||||
void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {
|
||||
log('Tab route visited: ${route.name}');
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) {
|
||||
log('Tab route re-visited: ${route.name}');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user