From 32f0c7c8ede2cff6f1caa2c1f8603a50aeda949e Mon Sep 17 00:00:00 2001
From: Abdur Rafay Saleem <62943972+arafaysaleem@users.noreply.github.com>
Date: Sun, 1 Aug 2021 20:28:05 +0500
Subject: [PATCH] Release 01-08-2021 (#63)
---
.github/labeler.yml | 5 +-
.github/workflows/PR-merge-build-release.yaml | 4 +-
.github/workflows/PR-open-test-build.yaml | 2 +-
.gitignore | 2 +-
analysis_options.yaml | 18 +-
android/app/src/main/AndroidManifest.xml | 4 +-
codecov.yml | 10 +
coverage/lcov.info | 2055 +++++++++++++++++
coverage_helper_script.bash | 9 +
lib/enums/booking_status_enum.dart | 8 +-
lib/enums/movie_type_enum.dart | 6 +-
lib/enums/payment_method_enum.dart | 8 +-
lib/enums/role_type_enum.dart | 6 +-
lib/enums/show_status_enum.dart | 6 +-
lib/enums/show_type_enum.dart | 6 +-
lib/enums/theater_type_enum.dart | 6 +-
lib/enums/user_role_enum.dart | 6 +-
lib/helper/extensions/string_extension.dart | 2 +-
lib/helper/utils/assets_helper.dart | 4 +-
lib/helper/utils/constants.dart | 59 +-
lib/helper/utils/form_validator.dart | 107 +
lib/main.dart | 13 +-
lib/models/booking_model.dart | 2 +-
lib/models/movie_model.dart | 10 +-
lib/models/movie_role_model.dart | 6 +-
lib/models/payment_model.dart | 2 +-
lib/models/show_model.dart | 3 +-
lib/models/show_time_model.dart | 3 +-
lib/models/theater_model.dart | 2 +-
lib/models/user_payment_model.dart | 11 +-
lib/providers/all_providers.dart | 65 +-
lib/providers/auth_provider.dart | 65 +-
lib/providers/bookings_provider.dart | 15 +-
lib/providers/movies_provider.dart | 6 +-
lib/providers/payments_provider.dart | 9 +-
lib/providers/shows_provider.dart | 34 +-
lib/providers/theaters_provider.dart | 8 +-
lib/routes/app_router.dart | 27 +-
.../local_storage/key_value_storage_base.dart | 93 +
.../key_value_storage_service.dart | 90 +
lib/services/local_storage/prefs_base.dart | 51 -
lib/services/local_storage/prefs_service.dart | 79 -
lib/services/networking/api_endpoint.dart | 96 +-
lib/services/networking/api_service.dart | 27 +-
lib/services/networking/dio_service.dart | 51 +-
.../interceptors/api_interceptor.dart | 21 +-
.../interceptors/logging_interceptor.dart | 58 +-
.../refresh_token_interceptor.dart | 71 +-
.../networking/network_exception.dart | 38 +-
.../repositories/auth_repository.dart | 16 +-
.../repositories/bookings_repository.dart | 10 +-
.../repositories/movies_repository.dart | 6 +-
.../repositories/payments_repository.dart | 6 +-
.../repositories/shows_repository.dart | 6 +-
.../repositories/theaters_repository.dart | 6 +-
lib/views/screens/app_startup_screen.dart | 1 +
lib/views/screens/change_password_screen.dart | 10 +-
lib/views/screens/confirmation_screen.dart | 10 +-
lib/views/screens/home_screen.dart | 10 +-
lib/views/screens/login_screen.dart | 35 +-
lib/views/screens/movie_details_screen.dart | 2 +-
lib/views/screens/register_screen.dart | 188 +-
lib/views/screens/shows_screen.dart | 8 +-
lib/views/screens/trailer_screen.dart | 4 +-
lib/views/screens/user_bookings_screen.dart | 2 +-
lib/views/screens/welcome_screen.dart | 2 +-
.../skeletons/shows_skeleton_loader.dart | 6 +-
.../change_password_fields.dart | 48 +-
.../change_password/save_password_button.dart | 2 +-
.../widgets/common/custom_error_widget.dart | 4 +-
.../widgets/common/custom_network_image.dart | 2 +-
.../widgets/common/custom_textfield.dart | 2 +-
lib/views/widgets/common/ratings.dart | 6 +-
.../confirmation/more_bookings_button.dart | 2 +-
.../confirmation/retry_payment_button.dart | 2 +-
.../movie_details/movie_actors_list.dart | 2 +-
.../movie_details/movie_details_column.dart | 2 +-
.../movie_details/movie_details_sheet.dart | 14 +-
.../movie_details/movie_summary_box.dart | 2 +-
.../movie_details/play_button_widget.dart | 2 +-
.../widgets/movies/movie_backdrop_view.dart | 2 +-
.../widgets/movies/movie_overview_column.dart | 2 +-
.../widgets/movies/white_movie_container.dart | 2 +-
.../widgets/payment/billing_details.dart | 16 +-
.../widgets/payment/mode_details_input.dart | 104 +-
lib/views/widgets/payment/pay_button.dart | 2 +-
.../widgets/payment/payment_options.dart | 8 +-
.../theater/purchase_seats_button.dart | 2 +-
.../theater/seat_color_indicators.dart | 6 +-
.../confirm_bookings_button.dart | 4 +-
.../ticket_summary/show_details_section.dart | 12 +-
.../ticket_summary/ticket_details_list.dart | 6 +-
.../user_bookings/booking_details_dialog.dart | 10 +-
.../user_bookings/booking_summary_row.dart | 8 +-
.../widgets/welcome/browse_movies_button.dart | 2 +-
.../widgets/welcome/user_profile_details.dart | 16 +-
.../widgets/welcome/view_bookings_button.dart | 2 +-
pubspec.lock | 112 +-
pubspec.yaml | 27 +-
test/enums/booking_status_enum_test.dart | 46 +
test/enums/movie_type_enum_test.dart | 46 +
test/enums/payment_method_enum_test.dart | 68 +
test/enums/role_type_enum_test.dart | 68 +
test/enums/show_status_enum_test.dart | 69 +
test/enums/show_type_enum_test.dart | 46 +
test/enums/theater_type_enum_test.dart | 46 +
test/enums/user_role_enum_test.dart | 46 +
test/helper/utils/form_validator_test.dart | 242 ++
test/models/booking_model_test.dart | 423 ++++
test/models/genre_model_test.dart | 101 +
test/models/movie_model_test.dart | 451 ++++
test/models/movie_role_model_test.dart | 133 ++
test/models/payment_model_test.dart | 362 +++
test/models/role_model_test.dart | 117 +
test/models/seat_model_test.dart | 101 +
test/models/show_model_test.dart | 210 ++
test/models/show_seating_model_test.dart | 137 ++
test/models/show_time_model_test.dart | 212 ++
test/models/theater_model_test.dart | 370 +++
test/models/user_booking_model_test.dart | 263 +++
test/models/user_booking_show_model_test.dart | 110 +
test/models/user_model_test.dart | 165 ++
test/models/user_payment_model_test.dart | 244 ++
123 files changed, 7254 insertions(+), 872 deletions(-)
create mode 100644 codecov.yml
create mode 100644 coverage/lcov.info
create mode 100644 coverage_helper_script.bash
create mode 100644 lib/helper/utils/form_validator.dart
create mode 100644 lib/services/local_storage/key_value_storage_base.dart
create mode 100644 lib/services/local_storage/key_value_storage_service.dart
delete mode 100644 lib/services/local_storage/prefs_base.dart
delete mode 100644 lib/services/local_storage/prefs_service.dart
create mode 100644 test/enums/booking_status_enum_test.dart
create mode 100644 test/enums/movie_type_enum_test.dart
create mode 100644 test/enums/payment_method_enum_test.dart
create mode 100644 test/enums/role_type_enum_test.dart
create mode 100644 test/enums/show_status_enum_test.dart
create mode 100644 test/enums/show_type_enum_test.dart
create mode 100644 test/enums/theater_type_enum_test.dart
create mode 100644 test/enums/user_role_enum_test.dart
create mode 100644 test/helper/utils/form_validator_test.dart
create mode 100644 test/models/booking_model_test.dart
create mode 100644 test/models/genre_model_test.dart
create mode 100644 test/models/movie_model_test.dart
create mode 100644 test/models/movie_role_model_test.dart
create mode 100644 test/models/payment_model_test.dart
create mode 100644 test/models/role_model_test.dart
create mode 100644 test/models/seat_model_test.dart
create mode 100644 test/models/show_model_test.dart
create mode 100644 test/models/show_seating_model_test.dart
create mode 100644 test/models/show_time_model_test.dart
create mode 100644 test/models/theater_model_test.dart
create mode 100644 test/models/user_booking_model_test.dart
create mode 100644 test/models/user_booking_show_model_test.dart
create mode 100644 test/models/user_model_test.dart
create mode 100644 test/models/user_payment_model_test.dart
diff --git a/.github/labeler.yml b/.github/labeler.yml
index dc35649..080914b 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -98,4 +98,7 @@ native/android:
- any: ['android/**/*']
gradle:
- - any: ['android/**/*']
\ No newline at end of file
+ - any: ['android/**/*']
+
+tests:
+ - any: [ 'test/**/*' ]
\ No newline at end of file
diff --git a/.github/workflows/PR-merge-build-release.yaml b/.github/workflows/PR-merge-build-release.yaml
index ed12af2..d76e195 100644
--- a/.github/workflows/PR-merge-build-release.yaml
+++ b/.github/workflows/PR-merge-build-release.yaml
@@ -28,6 +28,8 @@ jobs:
run: flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Run Dart Analyzer
run: flutter analyze .
+ - name: Run tests
+ run: flutter test
assemble-release:
name: Setup signing keys
@@ -92,7 +94,7 @@ jobs:
- name: Run build runner for codegen files
run: flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Generate Splitted Release APKs
- run: flutter build apk --target-platform android-arm,android-arm64 --split-per-abi --obfuscate --split-debug-info=./ez_tickets_app/debug_trace
+ run: flutter build apk --target-platform android-arm,android-arm64 --split-per-abi --obfuscate --split-debug-info=./ez_tickets_app/debug_trace --dart-define=BASE_URL=${{ secrets.BASE_URL }}
- name: Remove bundled APK
run: rm build/app/outputs/flutter-apk/app.apk
- name: Upload Built APKs Artifact
diff --git a/.github/workflows/PR-open-test-build.yaml b/.github/workflows/PR-open-test-build.yaml
index 734bafe..fe37ffa 100644
--- a/.github/workflows/PR-open-test-build.yaml
+++ b/.github/workflows/PR-open-test-build.yaml
@@ -38,4 +38,4 @@ jobs:
files: ./coverage/lcov.info
verbose: true
- name: Attempt Debug APK Build
- run: flutter build apk --debug --dart-define=BASE_URL=${{ secrets.BASE_URL }}
\ No newline at end of file
+ run: flutter build apk --debug --dart-define=BASE_URL=${{ secrets.BASE_URL }}
diff --git a/.gitignore b/.gitignore
index cc1361c..929d44e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,4 +51,4 @@ app.*.map.json
*.gr.dart
#keystore
-*.jks
\ No newline at end of file
+*.jks
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 44d6229..37c53c1 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:effective_dart/analysis_options.yaml
+include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
@@ -8,13 +8,23 @@ analyzer:
errors:
missing_required_param: error
missing_return: error
+ prefer_const_constructors: error
+ prefer_const_constructors_in_immutables: error
+ todo: ignore
+ strong-mode:
+ implicit-casts: false
+ implicit-dynamic: false
linter:
rules:
+ prefer_single_quotes: true
directives_ordering: false
- public_member_api_docs: false #Enable at the end
+ prefer_double_quotes: false
+ use_key_in_widget_constructors: false
+ always_specify_types: false
+ unnecessary_final: false
+ public_member_api_docs: false
+ prefer_expression_function_bodies: false
avoid_classes_with_only_static_members: false
- prefer_const_constructors: true
- prefer_const_constructors_in_immutables: true
prefer_const_literals_to_create_immutables: true
lines_longer_than_80_chars: false
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 50d8166..9406ff7 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,7 +3,9 @@
+ android:icon="@mipmap/ic_launcher"
+ android:allowBackup="false"
+ android:fullBackupContent="false">
$file
+echo "// Helper file to make coverage work for all dart files\n" > $file
+echo "// ignore_for_file: unused_import" >> $file
+packageName="$(cat pubspec.yaml| grep '^name: ' | awk '{print $2}')"
+find lib '!' -path '*generated*/*' '!' -name '*.g.dart' '!' -name '*.freezed.dart' '!' -name '*.gr.dart' '!' -name '*.part.dart' -name '*.dart' | cut -c4- | awk -v package="$packageName" '{printf "import '\''package:%s%s'\'';\n", package, $1}' >> $file
+echo "\nvoid main(){}" >> $file
\ No newline at end of file
diff --git a/lib/enums/booking_status_enum.dart b/lib/enums/booking_status_enum.dart
index 6961e0f..00cf2cb 100644
--- a/lib/enums/booking_status_enum.dart
+++ b/lib/enums/booking_status_enum.dart
@@ -4,13 +4,13 @@ import 'package:freezed_annotation/freezed_annotation.dart';
/// A collection of statuses that bookings can have.
enum BookingStatus {
-@JsonValue("confirmed") CONFIRMED,
-@JsonValue("cancelled") CANCELLED,
-@JsonValue("reserved") RESERVED,
+@JsonValue('confirmed') CONFIRMED,
+@JsonValue('cancelled') CANCELLED,
+@JsonValue('reserved') RESERVED,
}
/// A utility with extensions for enum name and serialized value.
-extension ExtMovieType on BookingStatus{
+extension ExtBookingStatus on BookingStatus{
String get name => describeEnum(this);
String get toJson => name.toLowerCase();
diff --git a/lib/enums/movie_type_enum.dart b/lib/enums/movie_type_enum.dart
index 656761d..967996e 100644
--- a/lib/enums/movie_type_enum.dart
+++ b/lib/enums/movie_type_enum.dart
@@ -4,9 +4,9 @@ import 'package:freezed_annotation/freezed_annotation.dart';
/// A collection of types that movies can be.
enum MovieType {
- @JsonValue("now_showing") NOW_SHOWING,
- @JsonValue("coming_soon") COMING_SOON,
- @JsonValue("removed") REMOVED,
+ @JsonValue('now_showing') NOW_SHOWING,
+ @JsonValue('coming_soon') COMING_SOON,
+ @JsonValue('removed') REMOVED,
ALL_MOVIES,
}
diff --git a/lib/enums/payment_method_enum.dart b/lib/enums/payment_method_enum.dart
index c54ea80..ab2a910 100644
--- a/lib/enums/payment_method_enum.dart
+++ b/lib/enums/payment_method_enum.dart
@@ -6,13 +6,13 @@ import '../helper/extensions/string_extension.dart';
/// A collection of payment methods that a user can choose.
enum PaymentMethod {
-@JsonValue("cash") CASH,
-@JsonValue("cod") COD,
-@JsonValue("card") CARD,
+@JsonValue('cash') CASH,
+@JsonValue('cod') COD,
+@JsonValue('card') CARD,
}
/// A utility with extensions for enum name and serialized value.
-extension ExtRoleType on PaymentMethod {
+extension ExtPaymentMethod on PaymentMethod {
String get name => describeEnum(this);
String get toJson => name.toLowerCase();
String get inString => name.capitalize;
diff --git a/lib/enums/role_type_enum.dart b/lib/enums/role_type_enum.dart
index 37a12f2..f9a9a54 100644
--- a/lib/enums/role_type_enum.dart
+++ b/lib/enums/role_type_enum.dart
@@ -6,9 +6,9 @@ import '../helper/extensions/string_extension.dart';
/// A collection of roles that movie actors can have.
enum RoleType {
- @JsonValue("director") DIRECTOR,
- @JsonValue("producer") PRODUCER,
- @JsonValue("cast") CAST,
+ @JsonValue('director') DIRECTOR,
+ @JsonValue('producer') PRODUCER,
+ @JsonValue('cast') CAST,
}
/// A utility with extensions for enum name and serialized value.
diff --git a/lib/enums/show_status_enum.dart b/lib/enums/show_status_enum.dart
index ee44be2..6735fe7 100644
--- a/lib/enums/show_status_enum.dart
+++ b/lib/enums/show_status_enum.dart
@@ -6,9 +6,9 @@ import '../helper/extensions/string_extension.dart';
/// A collection of statuses that a show can have.
enum ShowStatus {
- @JsonValue("free") FREE,
- @JsonValue("almost_full") ALMOST_FULL,
- @JsonValue("full") FULL,
+ @JsonValue('free') FREE,
+ @JsonValue('almost_full') ALMOST_FULL,
+ @JsonValue('full') FULL,
}
/// A utility with extensions for enum name and serialized value.
diff --git a/lib/enums/show_type_enum.dart b/lib/enums/show_type_enum.dart
index 6db9a55..dffe5d2 100644
--- a/lib/enums/show_type_enum.dart
+++ b/lib/enums/show_type_enum.dart
@@ -4,15 +4,15 @@ import 'package:freezed_annotation/freezed_annotation.dart';
/// A collection of types that a show can be.
enum ShowType {
- @JsonValue("2D") i2D,
- @JsonValue("3D") i3D,
+ @JsonValue('2D') i2D,
+ @JsonValue('3D') i3D,
}
/// A utility with extensions for enum name and serialized value.
extension ExtShowType on ShowType{
String get name => describeEnum(this);
- String get toJson => name.substring(1).toLowerCase();
String get inString => name.substring(1); //removes i prefix
+ String get toJson => inString;
}
diff --git a/lib/enums/theater_type_enum.dart b/lib/enums/theater_type_enum.dart
index 9094254..8f69672 100644
--- a/lib/enums/theater_type_enum.dart
+++ b/lib/enums/theater_type_enum.dart
@@ -4,12 +4,12 @@ import 'package:freezed_annotation/freezed_annotation.dart';
/// A collection of types that a theater can be.
enum TheaterType {
-@JsonValue("normal") NORMAL,
-@JsonValue("royal") ROYAL,
+@JsonValue('normal') NORMAL,
+@JsonValue('royal') ROYAL,
}
/// A utility with extensions for enum name and serialized value.
-extension ExtMovieType on TheaterType{
+extension ExtTheaterType on TheaterType{
String get name => describeEnum(this);
String get toJson => name.toLowerCase();
diff --git a/lib/enums/user_role_enum.dart b/lib/enums/user_role_enum.dart
index 8d53bac..9ae2543 100644
--- a/lib/enums/user_role_enum.dart
+++ b/lib/enums/user_role_enum.dart
@@ -4,9 +4,9 @@ import 'package:freezed_annotation/freezed_annotation.dart';
/// A collection of roles that a user can be.
enum UserRole {
- @JsonValue("admin") ADMIN,
- @JsonValue("api_user") API_USER,
- @JsonValue("super_user") SUPER_USER,
+ @JsonValue('admin') ADMIN,
+ @JsonValue('api_user') API_USER,
+ @JsonValue('super_user') SUPER_USER,
}
/// A utility with extensions for enum name and serialized value.
diff --git a/lib/helper/extensions/string_extension.dart b/lib/helper/extensions/string_extension.dart
index 56ab290..f963307 100644
--- a/lib/helper/extensions/string_extension.dart
+++ b/lib/helper/extensions/string_extension.dart
@@ -28,5 +28,5 @@ extension StringExt on String {
String get capitalize => this[0].toUpperCase() + this.substring(1).toLowerCase();
/// An extension for replacing underscores in a String with spaces.
- String get removeUnderScore => this.replaceAll("_", " ");
+ String get removeUnderScore => this.replaceAll('_', ' ');
}
diff --git a/lib/helper/utils/assets_helper.dart b/lib/helper/utils/assets_helper.dart
index c96bc6a..86eb1f5 100644
--- a/lib/helper/utils/assets_helper.dart
+++ b/lib/helper/utils/assets_helper.dart
@@ -7,8 +7,8 @@ class AssetsHelper {
const AssetsHelper._();
/// The path for face id image asset
- static const String faceId = "assets/face_id.png";
+ static const String faceId = 'assets/face_id.png';
/// The path for Pakistani flag image asset
- static const String pkFlag = "assets/pk_flag.png";
+ static const String pkFlag = 'assets/pk_flag.png';
}
diff --git a/lib/helper/utils/constants.dart b/lib/helper/utils/constants.dart
index ed7ec33..6bc5510 100644
--- a/lib/helper/utils/constants.dart
+++ b/lib/helper/utils/constants.dart
@@ -125,22 +125,67 @@ class Constants {
);
/// The regular expression for validating contacts in the app.
- static RegExp contactRegex = RegExp(r"^(03|3)\d{9}$");
+ static RegExp contactRegex = RegExp(r'^(03|3)\d{9}$');
/// The regular expression for validating full names in the app.
- static RegExp fullNameRegex = RegExp(r"^[a-zA-Z ]+$");
+ static RegExp fullNameRegex = RegExp(r'^[a-zA-Z ]+$');
/// The regular expression for validating zip codes in the app.
- static RegExp zipCodeRegex = RegExp(r"^\\d{5}$");
+ static RegExp zipCodeRegex = RegExp(r'^\d{5}$');
/// The regular expression for validating credit card numbers in the app.
- static RegExp creditCardNumberRegex = RegExp(r"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$");
+ static RegExp creditCardNumberRegex = RegExp(r'^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$');
/// The regular expression for validating credit card CVV in the app.
- static RegExp creditCardCVVRegex = RegExp(r"^[0-9]{3}");
+ static RegExp creditCardCVVRegex = RegExp(r'^[0-9]{3}$');
/// The regular expression for validating credit card expiry in the app.
- static RegExp creditCardExpiryRegex = RegExp(r"(0[1-9]|10|11|12)/20[0-9]{2}$");
+ static RegExp creditCardExpiryRegex = RegExp(r'(0[1-9]|10|11|12)/20[0-9]{2}$');
- static T? toNull(_) => null;
+ /// The error message for invalid email input.
+ static const invalidEmailError = 'Please enter a valid email address';
+
+ /// The error message for empty email input.
+ static const emptyEmailInputError = 'Please enter an email';
+
+ /// The error message for empty password input.
+ static const emptyPasswordInputError = 'Please enter a password';
+
+ /// The error message for invalid confirm password input.
+ static const invalidConfirmPwError = "Passwords don't match";
+
+ /// The error message for invalid current password input.
+ static const invalidCurrentPwError = 'Invalid current password!';
+
+ /// The error message for invalid new password input.
+ static const invalidNewPwError = "Current and new password can't be same";
+
+ /// The error message for invalid full name input.
+ static const invalidFullNameError = 'Please enter a valid full name';
+
+ /// The error message for empty address input.
+ static const emptyAddressInputError = 'Please enter a address';
+
+ /// The error message for empty cinema branch input.
+ static const emptyBranchInputError = 'Please enter the branch name';
+
+ /// The error message for invalid contact input.
+ static const invalidContactError = 'Please enter a valid contact';
+
+ /// The error message for invalid zip code input.
+ static const invalidZipCodeError = 'Please enter a valid zip code';
+
+ /// The error message for invalid promo code input.
+ static const invalidPromoCodeError = 'Please enter a valid promo code';
+
+ /// The error message for invalid credit card number input.
+ static const invalidCreditCardNumberError = 'Invalid credit card number';
+
+ /// The error message for invalid credit card CVV input.
+ static const invalidCreditCardCVVError = 'Please enter a valid CVV';
+
+ /// The error message for invalid credit card expiry input.
+ static const invalidCreditCardExpiryError = 'Please enter a valid expiry date';
+
+ static T? toNull(dynamic _) => null;
}
diff --git a/lib/helper/utils/form_validator.dart b/lib/helper/utils/form_validator.dart
new file mode 100644
index 0000000..4c0e560
--- /dev/null
+++ b/lib/helper/utils/form_validator.dart
@@ -0,0 +1,107 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+//Helpers
+import '../../helper/extensions/string_extension.dart';
+import 'constants.dart';
+
+/// A utility class that holds methods for validating different textFields.
+/// This class has no constructor and all methods are `static`.
+@immutable
+class FormValidator{
+ const FormValidator._();
+
+ /// A method containing validation logic for email input.
+ static String? emailValidator(String? email){
+ if(email == null || email.isEmpty) {
+ return Constants.emptyEmailInputError;
+ } else if (!email.isValidEmail) {
+ return Constants.invalidEmailError;
+ }
+ return null;
+ }
+
+ /// A method containing validation logic for password input.
+ static String? passwordValidator(String? password) {
+ if (password!.isEmpty) return Constants.emptyPasswordInputError;
+ return null;
+ }
+
+ /// A method containing validation logic for confirm password input.
+ static String? confirmPasswordValidator(String? confirmPw, String inputPw) {
+ if (confirmPw == inputPw.trim()) return null;
+ return Constants.invalidConfirmPwError;
+ }
+
+ /// A method containing validation logic for current password input.
+ static String? currentPasswordValidator(String? inputPw, String currentPw) {
+ if (inputPw == currentPw) return null;
+ return Constants.invalidCurrentPwError;
+ }
+
+ /// A method containing validation logic for new password input.
+ static String? newPasswordValidator(String? newPw, String currentPw) {
+ if (newPw!.isEmpty) {
+ return Constants.emptyPasswordInputError;
+ }
+ else if(newPw == currentPw) {
+ return Constants.invalidNewPwError;
+ }
+ return null;
+ }
+
+ /// A method containing validation logic for full name input.
+ static String? fullNameValidator(String? fullName) {
+ if (fullName != null && fullName.isValidFullName) return null;
+ return Constants.invalidFullNameError;
+ }
+
+ /// A method containing validation logic for address input.
+ static String? addressValidator(String? address) {
+ if (address!.isEmpty) return Constants.emptyAddressInputError;
+ return null;
+ }
+
+ /// A method containing validation logic for contact number input.
+ static String? contactValidator(String? contact) {
+ if (contact != null && contact.isValidContact) return null;
+ return Constants.invalidContactError;
+ }
+
+ /// A method containing validation logic for zipcode input.
+ static String? zipCodeValidator(String? zipCode) {
+ if (zipCode != null && zipCode.isValidZipCode) return null;
+ return Constants.invalidZipCodeError;
+ }
+
+ /// A method containing validation logic for promo code input.
+ static String? promoCodeValidator(String? promoCode) {
+ if (promoCode != null && promoCode.length == 6) return null;
+ return Constants.invalidPromoCodeError;
+ }
+
+ /// A method containing validation logic for cinema branch name input.
+ static String? branchNameValidator(String? branchName) {
+ if (branchName!.isEmpty) return Constants.emptyBranchInputError;
+ return null;
+ }
+
+ /// A method containing validation logic for credit card number input.
+ static String? creditCardNumberValidator(String? ccNumber) {
+ if (ccNumber != null && ccNumber.isValidCreditCardNumber) return null;
+ return Constants.invalidCreditCardNumberError;
+ }
+
+ /// A method containing validation logic for credit card CVV input.
+ static String? creditCardCVVValidator(String? cvv) {
+ if (cvv != null && cvv.isValidCreditCardCVV) return null;
+ return Constants.invalidCreditCardCVVError;
+ }
+
+ /// A method containing validation logic for credit card expiry input.
+ static String? creditCardExpiryValidator(String? expiry) {
+ if (expiry != null && expiry.isValidCreditCardExpiry) return null;
+ return Constants.invalidCreditCardExpiryError;
+ }
+
+}
diff --git a/lib/main.dart b/lib/main.dart
index 3299f74..40e1e64 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:clock/clock.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -10,12 +11,12 @@ import 'helper/utils/custom_theme.dart';
import 'routes/app_router.gr.dart';
//Services
-import 'services/local_storage/prefs_base.dart';
+import 'services/local_storage/key_value_storage_base.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
debugPrint = setDebugPrint;
- await PrefsBase.init();
+ await KeyValueStorageBase.init();
runApp(MyApp());
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
@@ -24,10 +25,10 @@ void main() async {
}
void setDebugPrint(String? message, {int? wrapWidth}) {
- final date = DateTime.now();
- var msg = "${date.year}/${date.month}/${date.day}";
- msg += " ${date.hour}:${date.minute}:${date.second}";
- msg += " $message";
+ final date = clock.now();
+ var msg = '${date.year}/${date.month}/${date.day}';
+ msg += ' ${date.hour}:${date.minute}:${date.second}';
+ msg += ' $message';
debugPrintSynchronously(
msg,
wrapWidth: wrapWidth,
diff --git a/lib/models/booking_model.dart b/lib/models/booking_model.dart
index 40a4c86..f414794 100644
--- a/lib/models/booking_model.dart
+++ b/lib/models/booking_model.dart
@@ -39,7 +39,7 @@ class BookingModel with _$BookingModel {
seatNumber == null &&
price == null &&
bookingStatus == null &&
- bookingDatetime == null) return const {};
+ bookingDatetime == null) return const {};
return copyWith(
userId: userId,
showId: showId ?? this.showId,
diff --git a/lib/models/movie_model.dart b/lib/models/movie_model.dart
index 04be22f..1f8a13c 100644
--- a/lib/models/movie_model.dart
+++ b/lib/models/movie_model.dart
@@ -33,10 +33,10 @@ class MovieModel with _$MovieModel {
return MovieModel(
movieId: null,
year: 0,
- title: "",
- summary: "",
- trailerUrl: "",
- posterUrl: "",
+ title: '',
+ summary: '',
+ trailerUrl: '',
+ posterUrl: '',
genres: [],
movieType: MovieType.COMING_SOON,
);
@@ -58,7 +58,7 @@ class MovieModel with _$MovieModel {
posterUrl == null &&
rating == null &&
movieType == null
- ) return const {};
+ ) return const {};
return copyWith(
movieId: movieId,
year: year ?? this.year,
diff --git a/lib/models/movie_role_model.dart b/lib/models/movie_role_model.dart
index 13805bd..5f8befc 100644
--- a/lib/models/movie_role_model.dart
+++ b/lib/models/movie_role_model.dart
@@ -24,9 +24,9 @@ class MovieRoleModel with _$MovieRoleModel {
_$MovieRoleModelFromJson(json);
Map toCustomJson() {
- return {
- "role_id": role.roleId,
- "role_type": roleType.toJson,
+ return {
+ 'role_id': role.roleId,
+ 'role_type': roleType.toJson,
};
}
}
diff --git a/lib/models/payment_model.dart b/lib/models/payment_model.dart
index d74f6d1..cfb686e 100644
--- a/lib/models/payment_model.dart
+++ b/lib/models/payment_model.dart
@@ -34,7 +34,7 @@ class PaymentModel with _$PaymentModel {
showId == null &&
amount == null &&
paymentMethod == null &&
- paymentDatetime == null) return const {};
+ paymentDatetime == null) return const {};
return copyWith(
paymentId: paymentId,
showId: showId ?? this.showId,
diff --git a/lib/models/show_model.dart b/lib/models/show_model.dart
index 8454a31..9339887 100644
--- a/lib/models/show_model.dart
+++ b/lib/models/show_model.dart
@@ -1,3 +1,4 @@
+import 'package:clock/clock.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'show_time_model.dart';
@@ -18,7 +19,7 @@ class ShowModel with _$ShowModel {
factory ShowModel.initial(){
return ShowModel(
- date: DateTime.now(),
+ date: clock.now(),
movieId: 0,
showTimes: const []
);
diff --git a/lib/models/show_time_model.dart b/lib/models/show_time_model.dart
index d8152e5..3072cee 100644
--- a/lib/models/show_time_model.dart
+++ b/lib/models/show_time_model.dart
@@ -1,3 +1,4 @@
+import 'package:clock/clock.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../helper/utils/constants.dart';
@@ -23,7 +24,7 @@ class ShowTimeModel with _$ShowTimeModel {
}) = _ShowTimeModel;
factory ShowTimeModel.initial(){
- final dummyTime = DateTime.now();
+ final dummyTime = clock.now();
return ShowTimeModel(
startTime: dummyTime,
endTime: dummyTime,
diff --git a/lib/models/theater_model.dart b/lib/models/theater_model.dart
index c687482..b393822 100644
--- a/lib/models/theater_model.dart
+++ b/lib/models/theater_model.dart
@@ -37,7 +37,7 @@ class TheaterModel with _$TheaterModel {
theaterType == null &&
missing == null &&
blocked == null
- ) return const {};
+ ) return const {};
return copyWith(
theaterId: theaterId,
numOfRows: numOfRows ?? this.numOfRows,
diff --git a/lib/models/user_payment_model.dart b/lib/models/user_payment_model.dart
index 9840d53..55b7d2b 100644
--- a/lib/models/user_payment_model.dart
+++ b/lib/models/user_payment_model.dart
@@ -13,21 +13,22 @@ class UserPaymentModel with _$UserPaymentModel {
required double amount,
required DateTime paymentDatetime,
required PaymentMethod paymentMethod,
- required _UserPaymentMovieModel movie,
+ required UserPaymentMovieModel movie,
}) = _UserPaymentModel;
factory UserPaymentModel.fromJson(Map json) => _$UserPaymentModelFromJson(json);
}
@freezed
-class _UserPaymentMovieModel with _$_UserPaymentMovieModel {
+@visibleForTesting
+class UserPaymentMovieModel with _$UserPaymentMovieModel {
@JsonSerializable()
- const factory _UserPaymentMovieModel({
+ const factory UserPaymentMovieModel({
required String title,
required String posterUrl,
- }) = __UserPaymentMovieModel;
+ }) = _UserPaymentMovieModel;
- factory _UserPaymentMovieModel.fromJson(Map json) => _$_UserPaymentMovieModelFromJson(json);
+ factory UserPaymentMovieModel.fromJson(Map json) => _$UserPaymentMovieModelFromJson(json);
}
diff --git a/lib/providers/all_providers.dart b/lib/providers/all_providers.dart
index 6da8582..13d3979 100644
--- a/lib/providers/all_providers.dart
+++ b/lib/providers/all_providers.dart
@@ -1,10 +1,19 @@
-import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:dio/dio.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
-//services imports
-import '../services/local_storage/prefs_service.dart';
+//Service imports
+import '../services/networking/dio_service.dart';
+import '../services/local_storage/key_value_storage_service.dart';
import '../services/networking/api_service.dart';
+import '../services/networking/api_endpoint.dart';
-//repository imports
+//Interceptor imports
+import '../services/networking/interceptors/api_interceptor.dart';
+import '../services/networking/interceptors/logging_interceptor.dart';
+import '../services/networking/interceptors/refresh_token_interceptor.dart';
+
+//Repository imports
import '../services/repositories/auth_repository.dart';
import '../services/repositories/bookings_repository.dart';
import '../services/repositories/movies_repository.dart';
@@ -12,20 +21,46 @@ import '../services/repositories/payments_repository.dart';
import '../services/repositories/shows_repository.dart';
import '../services/repositories/theaters_repository.dart';
-//provider imports
+//Provider imports
import 'auth_provider.dart';
import 'bookings_provider.dart';
import 'movies_provider.dart';
import 'payments_provider.dart';
import 'shows_provider.dart';
-//states
+//State imports
import 'states/auth_state.dart';
import 'theaters_provider.dart';
-//service providers
-final _apiServiceProvider = Provider((ref) => ApiService());
-final _prefsServiceProvider = Provider((ref) => PrefsService());
+//Services
+final keyValueStorageServiceProvider = Provider(
+ (ref) => KeyValueStorageService(),
+);
+
+final _dioProvider = Provider((ref) {
+ final baseOptions = BaseOptions(
+ baseUrl: ApiEndpoint.baseUrl,
+ );
+ return Dio(baseOptions);
+});
+
+final _dioServiceProvider = Provider((ref) {
+ final _dio = ref.watch(_dioProvider);
+ // Order of interceptors very important
+ return DioService(
+ dioClient: _dio,
+ interceptors: [
+ ApiInterceptor(ref),
+ if (kDebugMode) LoggingInterceptor(),
+ RefreshTokenInterceptor(dioClient: _dio, ref: ref)
+ ],
+ );
+});
+
+final _apiServiceProvider = Provider((ref) {
+ final _dioService = ref.watch(_dioServiceProvider);
+ return ApiService(_dioService);
+});
//repositories providers
final _authRepositoryProvider = Provider((ref) {
@@ -61,11 +96,11 @@ final _paymentsRepositoryProvider = Provider((ref) {
//notifier providers
final authProvider = StateNotifierProvider((ref) {
final _authRepository = ref.watch(_authRepositoryProvider);
- final _prefsService = ref.watch(_prefsServiceProvider);
+ final _keyValueStorageService = ref.watch(keyValueStorageServiceProvider);
return AuthProvider(
reader: ref.read,
authRepository: _authRepository,
- prefsService: _prefsService,
+ keyValueStorageService: _keyValueStorageService,
);
});
@@ -88,11 +123,15 @@ final theatersProvider = ChangeNotifierProvider((ref) {
final bookingsProvider = Provider((ref) {
final _bookingsRepository = ref.watch(_bookingsRepositoryProvider);
return BookingsProvider(
- read: ref.read, bookingsRepository: _bookingsRepository);
+ read: ref.read,
+ bookingsRepository: _bookingsRepository,
+ );
});
final paymentsProvider = Provider((ref) {
final _paymentsRepository = ref.watch(_paymentsRepositoryProvider);
return PaymentsProvider(
- read: ref.read, paymentsRepository: _paymentsRepository);
+ read: ref.read,
+ paymentsRepository: _paymentsRepository,
+ );
});
diff --git a/lib/providers/auth_provider.dart b/lib/providers/auth_provider.dart
index c77fcac..c8bcd93 100644
--- a/lib/providers/auth_provider.dart
+++ b/lib/providers/auth_provider.dart
@@ -7,7 +7,7 @@ import '../enums/user_role_enum.dart';
import '../models/user_model.dart';
//Services
-import '../services/local_storage/prefs_service.dart';
+import '../services/local_storage/key_value_storage_service.dart';
import '../services/networking/network_exception.dart';
import '../services/repositories/auth_repository.dart';
import 'states/auth_state.dart';
@@ -22,17 +22,16 @@ final changePasswordStateProvider = StateProvider(
class AuthProvider extends StateNotifier {
late UserModel? _currentUser;
final AuthRepository _authRepository;
- final PrefsService _prefsService;
+ final KeyValueStorageService _keyValueStorageService;
final Reader _reader;
- String _token = "";
- String _password = "";
+ String _password = '';
AuthProvider({
required AuthRepository authRepository,
- required PrefsService prefsService,
+ required KeyValueStorageService keyValueStorageService,
required Reader reader,
}) : _authRepository = authRepository,
- _prefsService = prefsService,
+ _keyValueStorageService = keyValueStorageService,
_reader = reader,
super(const AuthState.unauthenticated()) {
init();
@@ -40,8 +39,6 @@ class AuthProvider extends StateNotifier {
int get currentUserId => _currentUser!.userId!;
- String get token => _token;
-
String get currentUserFullName => _currentUser!.fullName;
String get currentUserEmail => _currentUser!.email;
@@ -53,21 +50,19 @@ class AuthProvider extends StateNotifier {
String get currentUserPassword => _password;
void updateToken(String value) {
- _token = value;
- _prefsService.setAuthToken(value);
+ _keyValueStorageService.setAuthToken(value);
}
void _updatePassword(String value) {
_password = value;
- _prefsService.setAuthPassword(value);
+ _keyValueStorageService.setAuthPassword(value);
}
- void init() {
- final authenticated = _prefsService.getAuthState();
- _currentUser = _prefsService.getAuthUser();
- _password = _prefsService.getAuthPassword();
- _token = _prefsService.getAuthToken();
- if (!authenticated || _currentUser == null) {
+ void init() async {
+ final authenticated = _keyValueStorageService.getAuthState();
+ _currentUser = _keyValueStorageService.getAuthUser();
+ _password = await _keyValueStorageService.getAuthPassword();
+ if (!authenticated || _currentUser == null || _password.isEmpty) {
logout();
} else {
state = AuthState.authenticated(fullName: _currentUser!.fullName);
@@ -78,7 +73,7 @@ class AuthProvider extends StateNotifier {
required String email,
required String password,
}) async {
- final data = {"email": email, "password": password};
+ final data = {'email': email, 'password': password};
state = const AuthState.authenticating();
try {
_currentUser = await _authRepository.sendLoginData(
@@ -87,7 +82,7 @@ class AuthProvider extends StateNotifier {
);
state = AuthState.authenticated(fullName: _currentUser!.fullName);
_updatePassword(password);
- _updatePreferences();
+ _updateAuthProfile();
} on NetworkException catch (e) {
state = AuthState.failed(reason: e.message);
}
@@ -101,8 +96,8 @@ class AuthProvider extends StateNotifier {
required String address,
UserRole role = UserRole.API_USER,
}) async {
- if (contact.startsWith("0")) contact = contact.substring(1);
- contact = "+92$contact";
+ if (contact.startsWith('0')) contact = contact.substring(1);
+ contact = '+92$contact';
final user = UserModel(
userId: null,
fullName: fullName,
@@ -119,14 +114,14 @@ class AuthProvider extends StateNotifier {
);
state = AuthState.authenticated(fullName: _currentUser!.fullName);
_updatePassword(password);
- _updatePreferences();
+ _updateAuthProfile();
} on NetworkException catch (e) {
state = AuthState.failed(reason: e.message);
}
}
Future forgotPassword(String email) async {
- final data = {"email": email};
+ final data = {'email': email};
return await _authRepository.sendForgotPasswordData(data: data);
}
@@ -134,7 +129,7 @@ class AuthProvider extends StateNotifier {
required String email,
required String password,
}) async {
- final data = {"email": email, "password": password};
+ final data = {'email': email, 'password': password};
final result = await _authRepository.sendResetPasswordData(data: data);
if (result) _updatePassword(password);
return result;
@@ -142,9 +137,9 @@ class AuthProvider extends StateNotifier {
Future changePassword({required String newPassword}) async {
final data = {
- "email": currentUserEmail,
- "password": currentUserPassword,
- "new_password": newPassword,
+ 'email': currentUserEmail,
+ 'password': currentUserPassword,
+ 'new_password': newPassword,
};
final _changePasswordState = _reader(changePasswordStateProvider);
_changePasswordState.state = const FutureState.loading();
@@ -159,23 +154,21 @@ class AuthProvider extends StateNotifier {
Future verifyOtp({required String email, required int otp}) async {
final data = {
- "email": email,
- "OTP": otp,
+ 'email': email,
+ 'OTP': otp,
};
return await _authRepository.sendOtpData(data: data);
}
- void _updatePreferences() {
- _prefsService.setAuthState(state);
- _prefsService.setAuthUser(_currentUser!);
- _prefsService.setAuthToken(token);
+ void _updateAuthProfile() {
+ _keyValueStorageService.setAuthState(state);
+ _keyValueStorageService.setAuthUser(_currentUser!);
}
void logout() {
- _token = "";
_currentUser = null;
- _password = "";
+ _password = '';
state = const AuthState.unauthenticated();
- _prefsService.resetPrefs();
+ _keyValueStorageService.resetKeys();
}
}
diff --git a/lib/providers/bookings_provider.dart b/lib/providers/bookings_provider.dart
index c08ac3d..2d37cc9 100644
--- a/lib/providers/bookings_provider.dart
+++ b/lib/providers/bookings_provider.dart
@@ -1,3 +1,4 @@
+import 'package:clock/clock.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
//Enums
@@ -45,12 +46,12 @@ class BookingsProvider {
int? userId,
int? showId,
}) async {
- final Map? queryParams = {
- if (bookingStatus != null) "booking_status": bookingStatus.toJson,
+ final Map? queryParams = {
+ if (bookingStatus != null) 'booking_status': bookingStatus.toJson,
if (bookingDatetime != null)
- "booking_datetime": bookingDatetime.toString(),
- if (userId != null) "user_id": userId,
- if (showId != null) "show_id": showId,
+ 'booking_datetime': bookingDatetime.toString(),
+ if (userId != null) 'user_id': userId,
+ if (showId != null) 'show_id': showId,
};
return await _bookingsRepository.fetchFilteredBookings(
queryParameters: queryParams);
@@ -87,7 +88,7 @@ class BookingsProvider {
seatNumber: seat.seatNumber,
price: Constants.ticketPrice,
bookingStatus: BookingStatus.RESERVED,
- bookingDatetime: DateTime.now(),
+ bookingDatetime: clock.now(),
);
bookingIds.add(newBooking.bookingId!);
}
@@ -108,7 +109,7 @@ class BookingsProvider {
bookingId: null,
userId: userId,
showId: showId,
- seat: "$seatRow-$seatNumber",
+ seat: '$seatRow-$seatNumber',
price: price,
bookingStatus: bookingStatus,
bookingDatetime: bookingDatetime,
diff --git a/lib/providers/movies_provider.dart b/lib/providers/movies_provider.dart
index 294c711..3d40e02 100644
--- a/lib/providers/movies_provider.dart
+++ b/lib/providers/movies_provider.dart
@@ -48,7 +48,7 @@ class MoviesProvider {
MovieType? movieType,
}) async {
final Map? queryParams = {
- if (movieType != null) "movie_type": movieType.toJson,
+ if (movieType != null) 'movie_type': movieType.toJson,
};
return await _moviesRepository.fetchAll(queryParameters: queryParams);
}
@@ -91,7 +91,7 @@ class MoviesProvider {
movieRoles.map((movieRole) => movieRole.toCustomJson()).toList();
final data = {
...movie.toJson(),
- "roles": roles,
+ 'roles': roles,
};
final movieId = await _moviesRepository.create(data: data);
return movie.copyWith(movieId: movieId);
@@ -116,7 +116,7 @@ class MoviesProvider {
rating: rating,
movieType: movieType,
);
- if (data.isEmpty) return "Nothing to update!";
+ if (data.isEmpty) return 'Nothing to update!';
return await _moviesRepository.update(movieId: movie.movieId!, data: data);
}
diff --git a/lib/providers/payments_provider.dart b/lib/providers/payments_provider.dart
index fd8668b..7fa1d10 100644
--- a/lib/providers/payments_provider.dart
+++ b/lib/providers/payments_provider.dart
@@ -1,3 +1,4 @@
+import 'package:clock/clock.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
//Enums
@@ -45,7 +46,7 @@ class PaymentsProvider {
PaymentMethod? paymentMethod,
}) async {
final Map? queryParams = {
- if (paymentMethod != null) "payment_method": paymentMethod.toJson,
+ if (paymentMethod != null) 'payment_method': paymentMethod.toJson,
};
return await _paymentsRepository.fetchAll(queryParameters: queryParams);
}
@@ -65,7 +66,7 @@ class PaymentsProvider {
Future makePayment() async {
final _paymentStateProv = _reader(paymentStateProvider);
_paymentStateProv.state = const PaymentState.unprocessed();
- await Future.delayed(const Duration(seconds: 3)).then((_) {
+ await Future.delayed(const Duration(seconds: 3)).then((_) {
_paymentStateProv.state = const PaymentState.processing();
});
final _activePaymentMethod = _reader(activePaymentModeProvider).state;
@@ -107,7 +108,7 @@ class PaymentsProvider {
// userId: userId,
// showId: showId,
// amount: amount,
- // paymentDatetime: DateTime.now(),
+ // paymentDatetime: clock.now(),
// bookingIds: bookingIds,
// paymentMethod: _reader(activePaymentModeProvider).state,
// );
@@ -124,7 +125,7 @@ class PaymentsProvider {
userId: userId,
showId: showId,
amount: amount,
- paymentDatetime: DateTime.now(),
+ paymentDatetime: clock.now(),
bookingIds: bookingIds,
paymentMethod: PaymentMethod.CARD,
);
diff --git a/lib/providers/shows_provider.dart b/lib/providers/shows_provider.dart
index 338b70e..bfc9e43 100644
--- a/lib/providers/shows_provider.dart
+++ b/lib/providers/shows_provider.dart
@@ -47,8 +47,8 @@ class ShowsProvider {
Future> getAllShows({
required int movieId,
}) async {
- final Map? queryParams = {
- "movie_id": movieId,
+ final Map? queryParams = {
+ 'movie_id': movieId,
};
return await _showsRepository.fetchAll(queryParameters: queryParams);
}
@@ -70,13 +70,13 @@ class ShowsProvider {
}) async {
//TODO: Improve API for Show times and Show
final data = {
- "movie_id": movieId,
- "theater_id": theaterId,
- "start_time": startTime,
- "end_time": endTime,
- "date": date,
- "show_type": showType.toJson,
- "show_status": showStatus.toJson,
+ 'movie_id': movieId,
+ 'theater_id': theaterId,
+ 'start_time': startTime,
+ 'end_time': endTime,
+ 'date': date,
+ 'show_type': showType.toJson,
+ 'show_status': showStatus.toJson,
};
final showId = await _showsRepository.create(data: data);
final showTime = ShowTimeModel(
@@ -103,15 +103,15 @@ class ShowsProvider {
ShowStatus? showStatus,
}) async {
final data = {
- if (movieId != null) "movie_id": movieId,
- if (theaterId != null) "theater_id": theaterId,
- if (startTime != null) "start_time": startTime,
- if (endTime != null) "end_time": endTime,
- if (date != null) "date": date,
- if (showType != null) "show_type": showType.toJson,
- if (showStatus != null) "show_status": showStatus.toJson,
+ if (movieId != null) 'movie_id': movieId,
+ if (theaterId != null) 'theater_id': theaterId,
+ if (startTime != null) 'start_time': startTime,
+ if (endTime != null) 'end_time': endTime,
+ if (date != null) 'date': date,
+ if (showType != null) 'show_type': showType.toJson,
+ if (showStatus != null) 'show_status': showStatus.toJson,
};
- if (data.isEmpty) return "Nothing to update!";
+ if (data.isEmpty) return 'Nothing to update!';
return await _showsRepository.update(showId: showId, data: data);
}
diff --git a/lib/providers/theaters_provider.dart b/lib/providers/theaters_provider.dart
index 505b148..2b23f4f 100644
--- a/lib/providers/theaters_provider.dart
+++ b/lib/providers/theaters_provider.dart
@@ -17,7 +17,7 @@ import 'all_providers.dart';
//Providers
import 'shows_provider.dart';
-final selectedTheaterNameProvider = StateProvider((_) => "");
+final selectedTheaterNameProvider = StateProvider((_) => '');
/// Does not use `ref.maintainState = true` bcz we wanted to load theater seats
/// everytime because it can receive frequent updates.
@@ -53,7 +53,7 @@ class TheatersProvider extends ChangeNotifier {
UnmodifiableListView(_selectedSeats);
List get selectedSeatNames => _selectedSeats
- .map((seat) => "${seat.seatRow}-${seat.seatNumber}")
+ .map((seat) => '${seat.seatRow}-${seat.seatNumber}')
.toList();
TheatersProvider(this._theatersRepository);
@@ -73,7 +73,7 @@ class TheatersProvider extends ChangeNotifier {
TheaterType? theaterType,
}) async {
final Map? queryParams = {
- if (theaterType != null) "theater_type": theaterType.toJson,
+ if (theaterType != null) 'theater_type': theaterType.toJson,
};
final theaters = await _theatersRepository.fetchAll(queryParameters: queryParams);
for(var theater in theaters) {
@@ -133,7 +133,7 @@ class TheatersProvider extends ChangeNotifier {
missing: missing,
blocked: blocked,
);
- if (data.isEmpty) return "Nothing to update!";
+ if (data.isEmpty) return 'Nothing to update!';
return await _theatersRepository.update(
theaterId: theater.theaterId!, data: data);
}
diff --git a/lib/routes/app_router.dart b/lib/routes/app_router.dart
index 2b23d12..0357e02 100644
--- a/lib/routes/app_router.dart
+++ b/lib/routes/app_router.dart
@@ -1,4 +1,5 @@
import 'package:auto_route/annotations.dart';
+import 'package:flutter/material.dart';
import '../views/screens/app_startup_screen.dart';
import '../views/screens/login_screen.dart';
@@ -16,19 +17,19 @@ import '../views/screens/change_password_screen.dart';
@MaterialAutoRouter(
routes: [
- AutoRoute(page: AppStartupScreen, initial: true),
- AutoRoute(page: RegisterScreen),
- AutoRoute(page: LoginScreen),
- AutoRoute(page: MoviesScreen),
- AutoRoute(page: MovieDetailsScreen),
- AutoRoute(page: TrailerScreen),
- AutoRoute(page: ShowsScreen),
- AutoRoute(page: TheaterScreen),
- AutoRoute(page: TicketSummaryScreen),
- AutoRoute(page: PaymentScreen),
- AutoRoute(page: ConfirmationScreen),
- AutoRoute(page: UserBookingsScreen),
- AutoRoute(page: ChangePasswordScreen),
+ AutoRoute(page: AppStartupScreen, initial: true),
+ AutoRoute(page: RegisterScreen),
+ AutoRoute(page: LoginScreen),
+ AutoRoute(page: MoviesScreen),
+ AutoRoute(page: MovieDetailsScreen),
+ AutoRoute(page: TrailerScreen),
+ AutoRoute(page: ShowsScreen),
+ AutoRoute(page: TheaterScreen),
+ AutoRoute(page: TicketSummaryScreen),
+ AutoRoute(page: PaymentScreen),
+ AutoRoute(page: ConfirmationScreen),
+ AutoRoute(page: UserBookingsScreen),
+ AutoRoute(page: ChangePasswordScreen),
],
)
class $AppRouter{}
diff --git a/lib/services/local_storage/key_value_storage_base.dart b/lib/services/local_storage/key_value_storage_base.dart
new file mode 100644
index 0000000..35899bb
--- /dev/null
+++ b/lib/services/local_storage/key_value_storage_base.dart
@@ -0,0 +1,93 @@
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+
+/// Base class containing a unified API for key-value pairs' storage.
+/// This class provides low level methods for storing:
+/// - Sensitive keys using [FlutterSecureStorage]
+/// - Insensitive keys using [SharedPreferences]
+class KeyValueStorageBase{
+ /// Instance of shared preferences
+ static SharedPreferences? _sharedPrefs;
+
+ /// Instance of flutter secure storage
+ static FlutterSecureStorage? _secureStorage;
+
+ /// Singleton instance of KeyValueStorage Helper
+ static KeyValueStorageBase? _instance;
+
+ /// Private constructor
+ const KeyValueStorageBase._();
+
+ /// Get instance of this class
+ static KeyValueStorageBase get instance => _instance ?? const KeyValueStorageBase._();
+
+ /// Initializer for shared prefs and flutter secure storage
+ /// Should be called in main before runApp and
+ /// after WidgetsBinding.FlutterInitialized(), to allow for synchronous tasks
+ /// when possible.
+ static Future init() async {
+ _sharedPrefs ??= await SharedPreferences.getInstance();
+ _secureStorage ??= const FlutterSecureStorage();
+ }
+
+ /// Reads the value for the key from common preferences storage
+ T? getCommon(String key) {
+ try{
+ switch(T){
+ case String: return _sharedPrefs!.getString(key) as T?;
+ case int: return _sharedPrefs!.getInt(key) as T?;
+ case bool: return _sharedPrefs!.getBool(key) as T?;
+ case double: return _sharedPrefs!.getDouble(key) as T?;
+ default: return _sharedPrefs!.get(key) as T?;
+ }
+ } on Exception {
+ return null;
+ }
+ }
+
+ /// Reads the decrypted value for the key from secure storage
+ Future getEncrypted(String key) {
+ try {
+ return _secureStorage!.read(key: key);
+ } on PlatformException {
+ return Future.value(null);
+ }
+ }
+
+ /// Sets the value for the key to common preferences storage
+ Future setCommon(String key, T value) {
+ switch(T){
+ case String: return _sharedPrefs!.setString(key, value as String);
+ case int: return _sharedPrefs!.setInt(key, value as int);
+ case bool: return _sharedPrefs!.setBool(key, value as bool);
+ case double: return _sharedPrefs!.setDouble(key, value as double);
+ default: return _sharedPrefs!.setString(key, value as String);
+ }
+ }
+
+ /// Sets the encrypted value for the key to secure storage
+ Future setEncrypted(String key, String value) {
+ try {
+ _secureStorage!.write(key: key, value: value);
+ return Future.value(true);
+ } on PlatformException catch (_) {
+ return Future.value(false);
+ }
+ }
+
+ /// Erases common preferences keys
+ Future clearCommon() => _sharedPrefs!.clear();
+
+ /// Erases encrypted keys
+ Future clearEncrypted() async {
+ try {
+ await _secureStorage!.deleteAll();
+ return true;
+ } on PlatformException catch (_) {
+ return false;
+ }
+ }
+}
diff --git a/lib/services/local_storage/key_value_storage_service.dart b/lib/services/local_storage/key_value_storage_service.dart
new file mode 100644
index 0000000..a80ee35
--- /dev/null
+++ b/lib/services/local_storage/key_value_storage_service.dart
@@ -0,0 +1,90 @@
+import 'dart:convert';
+
+//services
+import 'key_value_storage_base.dart';
+
+//models
+import '../../models/user_model.dart';
+
+//states
+import '../../providers/states/auth_state.dart';
+
+/// A service class for providing methods to store and retrieve key-value data
+/// from common or secure storage.
+class KeyValueStorageService {
+
+ /// The name of auth token key
+ static const _authTokenKey = 'authToken';
+
+ /// The name of auth state key
+ static const _authStateKey = 'authStateKey';
+
+ /// The name of user password key
+ static const _authPasswordKey = 'authPasswordKey';
+
+ /// The name of user model key
+ static const _authUserKey = 'authUserKey';
+
+ /// Instance of key-value storage base class
+ final _keyValueStorage = KeyValueStorageBase.instance;
+
+ /// Returns logged in user password
+ Future getAuthPassword() async {
+ return await _keyValueStorage.getEncrypted(_authPasswordKey) ?? '';
+ }
+
+ /// Returns last authentication status
+ bool getAuthState() {
+ return _keyValueStorage.getCommon(_authStateKey) ?? false;
+ }
+
+ /// Returns last authenticated user
+ UserModel? getAuthUser() {
+ final user = _keyValueStorage.getCommon(_authUserKey);
+ if(user == null) return null;
+ return UserModel.fromJson(jsonDecode(user) as Map);
+ }
+
+ /// Returns last authentication token
+ Future getAuthToken() async {
+ return await _keyValueStorage.getEncrypted(_authTokenKey) ?? '';
+ }
+
+ /// Sets the authentication password to this value. Even though this method is
+ /// asynchronous, we don't care about it's completion which is why we don't
+ /// use `await` and let it execute in the background.
+ void setAuthPassword(String password) {
+ _keyValueStorage.setEncrypted(_authPasswordKey, password);
+ }
+
+ /// Sets the authentication status to this value. Even though this method is
+ /// asynchronous, we don't care about it's completion which is why we don't
+ /// use `await` and let it execute in the background.
+ void setAuthState(AuthState authState) {
+ if(authState is AUTHENTICATED) {
+ _keyValueStorage.setCommon(_authStateKey, true);
+ }
+ }
+
+ /// Sets the authenticated user to this value. Even though this method is
+ /// asynchronous, we don't care about it's completion which is why we don't
+ /// use `await` and let it execute in the background.
+ void setAuthUser(UserModel user) {
+ _keyValueStorage.setCommon(_authUserKey, jsonEncode(user.toJson()));
+ }
+
+ /// Sets the authentication token to this value. Even though this method is
+ /// asynchronous, we don't care about it's completion which is why we don't
+ /// use `await` and let it execute in the background.
+ void setAuthToken(String token) {
+ _keyValueStorage.setEncrypted(_authTokenKey, token);
+ }
+
+ /// Resets the authentication. Even though these methods are asynchronous, we
+ /// don't care about their completion which is why we don't use `await` and
+ /// let them execute in the background.
+ void resetKeys() {
+ _keyValueStorage.clearCommon();
+ _keyValueStorage.clearEncrypted();
+ }
+}
diff --git a/lib/services/local_storage/prefs_base.dart b/lib/services/local_storage/prefs_base.dart
deleted file mode 100644
index c61f56e..0000000
--- a/lib/services/local_storage/prefs_base.dart
+++ /dev/null
@@ -1,51 +0,0 @@
-import 'package:shared_preferences/shared_preferences.dart';
-
-///Base class for shared preferences methods
-///This class provides low level preferences methods
-class PrefsBase{
- ///Instance of shared preferences
- static SharedPreferences? _sharedPrefs;
-
- ///Singleton instance of Preferences Helper
- static PrefsBase? _instance;
-
- ///Private constructor
- const PrefsBase._();
-
- ///Get instance of this class
- static PrefsBase get instance => _instance ?? const PrefsBase._();
-
- ///Initializer for shared prefs
- ///Should be called in main before runApp and
- ///after WidgetsBinding.FlutterInitialized()
- static Future init() async {
- if(_sharedPrefs == null ) {
- _sharedPrefs = await SharedPreferences.getInstance();
- }
- }
-
- ///Loads value for the key from preferences
- T? get(String key) {
- switch(T){
- case String: return _sharedPrefs!.getString(key) as T?;
- case int: return _sharedPrefs!.getInt(key) as T?;
- case bool: return _sharedPrefs!.getBool(key) as T?;
- case double: return _sharedPrefs!.getDouble(key) as T?;
- default: return _sharedPrefs!.getString(key) as T?;
- }
- }
-
- ///Sets the value for the key to preferences
- Future set(String key, T value) {
- switch(T){
- case String: return _sharedPrefs!.setString(key, value as String);
- case int: return _sharedPrefs!.setInt(key, value as int);
- case bool: return _sharedPrefs!.setBool(key, value as bool);
- case double: return _sharedPrefs!.setDouble(key, value as double);
- default: return _sharedPrefs!.setString(key, value as String);
- }
- }
-
- ///Resets preferences
- void clear() => _sharedPrefs!.clear();
-}
diff --git a/lib/services/local_storage/prefs_service.dart b/lib/services/local_storage/prefs_service.dart
deleted file mode 100644
index a657384..0000000
--- a/lib/services/local_storage/prefs_service.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-import 'dart:convert';
-
-//services
-import 'prefs_base.dart';
-
-//models
-import '../../models/user_model.dart';
-
-//states
-import '../../providers/states/auth_state.dart';
-
-/// A service class for providing methods to store and retrieve data from
-/// shared preferences.
-class PrefsService {
-
- /// The name of auth token key
- static const _authTokenKey = "authToken";
-
- /// The name of auth state key
- static const _authStateKey = "authStateKey";
-
- /// The name of user password key
- static const _authPasswordKey = "authPasswordKey";
-
- /// The name of user model key
- static const _authUserKey = "authUserKey";
-
- ///Instance of prefs class
- final _prefs = PrefsBase.instance;
-
- ///Returns logged in user password
- String getAuthPassword() {
- return _prefs.get(_authPasswordKey) ?? '';
- }
-
- ///Returns last authentication status
- bool getAuthState() {
- return _prefs.get(_authStateKey) ?? false;
- }
-
- ///Returns last authenticated user
- UserModel? getAuthUser() {
- final user = _prefs.get(_authUserKey);
- if(user == null) return null;
- return UserModel.fromJson(jsonDecode(user));
- }
-
- ///Returns last authentication token
- String getAuthToken() {
- return _prefs.get(_authTokenKey) ?? '';
- }
-
- ///Sets the authentication password to this value
- void setAuthPassword(String password) {
- _prefs.set(_authPasswordKey, password);
- }
-
- ///Sets the authentication status to this value
- void setAuthState(AuthState authState) {
- if(authState is AUTHENTICATED) {
- _prefs.set(_authStateKey, true);
- }
- }
-
- ///Sets the authenticated user to this value
- void setAuthUser(UserModel user) {
- _prefs.set(_authUserKey, jsonEncode(user.toJson()));
- }
-
- ///Sets the authentication token to this value
- void setAuthToken(String token) {
- _prefs.set(_authTokenKey, token);
- }
-
- ///Resets the authentication
- void resetPrefs() {
- _prefs.clear();
- }
-}
diff --git a/lib/services/networking/api_endpoint.dart b/lib/services/networking/api_endpoint.dart
index 6a6f3f3..20990fa 100644
--- a/lib/services/networking/api_endpoint.dart
+++ b/lib/services/networking/api_endpoint.dart
@@ -9,17 +9,31 @@ import 'package:flutter/material.dart';
class ApiEndpoint {
const ApiEndpoint._();
+ /// The base url of our REST API, to which all the requests will be sent.
+ /// It is supplied at the time of building the apk or running the app:
+ /// ```
+ /// flutter build apk --debug --dart-define=BASE_URL=www.some_url.com
+ /// ```
+ /// OR
+ /// ```
+ /// flutter run --dart-define=BASE_URL=www.some_url.com
+ /// ```
+ static const baseUrl = String.fromEnvironment(
+ 'BASE_URL',
+ defaultValue: 'localhost:3000/api/v1',
+ );
+
/// Returns the path for an authentication [endpoint].
static String auth(AuthEndpoint endpoint) {
- var path = "/auth";
+ var path = '/auth';
switch (endpoint) {
- case AuthEndpoint.REGISTER: return "$path/register";
- case AuthEndpoint.LOGIN: return "$path/login";
- case AuthEndpoint.REFRESH_TOKEN: return "$path/token";
- case AuthEndpoint.FORGOT_PASSWORD: return "$path/password/forgot";
- case AuthEndpoint.RESET_PASSWORD: return "$path/password/reset";
- case AuthEndpoint.CHANGE_PASSWORD: return "$path/password/change";
- case AuthEndpoint.VERIFY_OTP: return "$path/password/otp";
+ case AuthEndpoint.REGISTER: return '$path/register';
+ case AuthEndpoint.LOGIN: return '$path/login';
+ case AuthEndpoint.REFRESH_TOKEN: return '$path/token';
+ case AuthEndpoint.FORGOT_PASSWORD: return '$path/password/forgot';
+ case AuthEndpoint.RESET_PASSWORD: return '$path/password/reset';
+ case AuthEndpoint.CHANGE_PASSWORD: return '$path/password/change';
+ case AuthEndpoint.VERIFY_OTP: return '$path/password/otp';
}
}
@@ -27,12 +41,12 @@ class ApiEndpoint {
///
/// Specify user [id] to get the path for a specific user.
static String users(UserEndpoint endpoint, {int? id}) {
- var path = "/users";
+ var path = '/users';
switch(endpoint){
case UserEndpoint.BASE: return path;
case UserEndpoint.BY_ID: {
- assert(id != null, "userId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'userId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
}
}
@@ -41,16 +55,16 @@ class ApiEndpoint {
///
/// Specify movie [id] for any endpoints involving a specific movie.
static String movies(MovieEndpoint endpoint, {int? id}) {
- var path = "/movies";
+ var path = '/movies';
switch (endpoint) {
case MovieEndpoint.BASE: return path;
case MovieEndpoint.BY_ID: {
- assert(id != null, "movieId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'movieId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
case MovieEndpoint.ROLES: {
- assert(id != null, "movieId is required for ROLES endpoint");
- return "$path/id/$id/roles";
+ assert(id != null, 'movieId is required for ROLES endpoint');
+ return '$path/id/$id/roles';
}
}
}
@@ -59,16 +73,16 @@ class ApiEndpoint {
///
/// Specify role [id] for any endpoints involving a specific role.
static String roles(RoleEndpoint endpoint, {int? id}) {
- var path = "/roles";
+ var path = '/roles';
switch (endpoint) {
case RoleEndpoint.BASE: return path;
case RoleEndpoint.BY_ID: {
- assert(id != null, "roleId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'roleId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
case RoleEndpoint.MOVIES: {
- assert(id != null, "roleId is required for MOVIES endpoint");
- return "$path/id/$id/movies";
+ assert(id != null, 'roleId is required for MOVIES endpoint');
+ return '$path/id/$id/movies';
}
}
}
@@ -77,13 +91,13 @@ class ApiEndpoint {
///
/// Specify show [id] for any endpoints involving an individual show.
static String shows(ShowEndpoint endpoint, {int? id}) {
- var path = "/shows";
+ var path = '/shows';
switch(endpoint){
case ShowEndpoint.BASE: return path;
- case ShowEndpoint.FILTERS: return "$path/filters";
+ case ShowEndpoint.FILTERS: return '$path/filters';
case ShowEndpoint.BY_ID: {
- assert(id != null, "showId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'showId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
}
}
@@ -92,12 +106,12 @@ class ApiEndpoint {
///
/// Specify theater [id] for any endpoints involving an individual theater.
static String theaters(TheaterEndpoint endpoint, {int? id}) {
- var path = "/theaters";
+ var path = '/theaters';
switch(endpoint){
case TheaterEndpoint.BASE: return path;
case TheaterEndpoint.BY_ID: {
- assert(id != null, "theaterId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'theaterId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
}
}
@@ -106,21 +120,21 @@ class ApiEndpoint {
///
/// Specify booking [id] for any endpoints involving an individual booking.
static String bookings(BookingEndpoint endpoint, {int? id}) {
- var path = "/bookings";
+ var path = '/bookings';
switch(endpoint){
case BookingEndpoint.BASE: return path;
- case BookingEndpoint.FILTERS: return "$path/filters";
+ case BookingEndpoint.FILTERS: return '$path/filters';
case BookingEndpoint.USERS: {
- assert(id != null, "bookingId is required for USERS endpoint");
- return "$path/users/$id";
+ assert(id != null, 'bookingId is required for USERS endpoint');
+ return '$path/users/$id';
}
case BookingEndpoint.SHOWS: {
- assert(id != null, "bookingId is required for SHOWS endpoint");
- return "$path/shows/$id";
+ assert(id != null, 'bookingId is required for SHOWS endpoint');
+ return '$path/shows/$id';
}
case BookingEndpoint.BY_ID: {
- assert(id != null, "bookingId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'bookingId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
}
}
@@ -129,16 +143,16 @@ class ApiEndpoint {
///
/// Specify payment [id] for any endpoints involving an individual payment.
static String payments(PaymentEndpoint endpoint, {int? id}) {
- var path = "/payments";
+ var path = '/payments';
switch(endpoint){
case PaymentEndpoint.BASE: return path;
case PaymentEndpoint.USERS: {
- assert(id != null, "paymentId is required for USERS endpoint");
- return "$path/users/$id";
+ assert(id != null, 'paymentId is required for USERS endpoint');
+ return '$path/users/$id';
}
case PaymentEndpoint.BY_ID: {
- assert(id != null, "paymentId is required for BY_ID endpoint");
- return "$path/id/$id";
+ assert(id != null, 'paymentId is required for BY_ID endpoint');
+ return '$path/id/$id';
}
}
}
diff --git a/lib/services/networking/api_service.dart b/lib/services/networking/api_service.dart
index c4497f9..40dda2b 100644
--- a/lib/services/networking/api_service.dart
+++ b/lib/services/networking/api_service.dart
@@ -10,15 +10,8 @@ class ApiService implements ApiInterface{
late final DioService _dioService;
/// A public constructor that is used to initialize the API service
- /// with [BaseOptions] and setup the underlying [_dioService].
- ApiService() {
- final options = BaseOptions(
- baseUrl: "https://ez-tickets-backend.herokuapp.com/api/v1",
- );
- _dioService = DioService(
- baseOptions: options,
- );
- }
+ /// and setup the underlying [_dioService].
+ ApiService(DioService dioService) : _dioService = dioService;
/// An implementation of the base method for requesting collection of data
/// from the [endpoint].
@@ -48,16 +41,16 @@ class ApiService implements ApiInterface{
//Entire map of response
final data = await _dioService.get(
endpoint: endpoint,
- options: Options(headers: {"requiresAuthToken": requiresAuthToken}),
+ options: Options(headers: {'requiresAuthToken': requiresAuthToken}),
queryParams: queryParams,
cancelToken: cancelToken,
);
//Items of table as json
- final List body = data['body'];
+ final List body = data['body'] as List;
//Returning the deserialized objects
- return body.map((dataMap) => converter(dataMap)).toList();
+ return body.map((dynamic dataMap) => converter(dataMap as Map)).toList();
}
/// An implementation of the base method for requesting a document of data
@@ -89,12 +82,12 @@ class ApiService implements ApiInterface{
final data = await _dioService.get(
endpoint: endpoint,
queryParams: queryParams,
- options: Options(headers: {"requiresAuthToken": requiresAuthToken}),
+ options: Options(headers: {'requiresAuthToken': requiresAuthToken}),
cancelToken: cancelToken,
);
//Returning the deserialized object
- return converter(data['body']);
+ return converter(data['body'] as Map);
}
/// An implementation of the base method for inserting [data] at
@@ -125,7 +118,7 @@ class ApiService implements ApiInterface{
final dataMap = await _dioService.post(
endpoint: endpoint,
data: data,
- options: Options(headers: {"requiresAuthToken": requiresAuthToken}),
+ options: Options(headers: {'requiresAuthToken': requiresAuthToken}),
cancelToken: cancelToken,
);
@@ -160,7 +153,7 @@ class ApiService implements ApiInterface{
final dataMap = await _dioService.patch(
endpoint: endpoint,
data: data,
- options: Options(headers: {"requiresAuthToken": requiresAuthToken}),
+ options: Options(headers: {'requiresAuthToken': requiresAuthToken}),
cancelToken: cancelToken,
);
@@ -195,7 +188,7 @@ class ApiService implements ApiInterface{
final dataMap = await _dioService.delete(
endpoint: endpoint,
data: data,
- options: Options(headers: {"requiresAuthToken": requiresAuthToken}),
+ options: Options(headers: {'requiresAuthToken': requiresAuthToken}),
cancelToken: cancelToken,
);
diff --git a/lib/services/networking/dio_service.dart b/lib/services/networking/dio_service.dart
index edfc9b9..87b0699 100644
--- a/lib/services/networking/dio_service.dart
+++ b/lib/services/networking/dio_service.dart
@@ -1,19 +1,13 @@
import 'dart:async';
import 'package:dio/dio.dart';
-import 'package:flutter/foundation.dart';
-//Interceptors
-import 'interceptors/api_interceptor.dart';
-import 'interceptors/logging_interceptor.dart';
-import 'interceptors/refresh_token_interceptor.dart';
//Exceptions
import 'network_exception.dart';
/// A service class that wraps the [Dio] instance and provides methods for
/// basic network requests.
class DioService {
-
/// An instance of [Dio] for executing network requests.
late final Dio _dio;
@@ -21,30 +15,13 @@ class DioService {
/// network requests.
late final CancelToken _cancelToken;
- /// A public constructor that is used to create the Dio service
- /// with [baseOptions].
+ /// A public constructor that is used to create a Dio service and initialize
+ /// the underlying [Dio] client.
///
- /// Calls [createDio()] to setup the underlying [_dio] instance.
- DioService({required BaseOptions baseOptions}) {
- createDio(baseOptions);
- }
-
- /// An method to create new instance of [Dio] with [baseOptions].
- ///
- /// Attaches any external [Interceptors] to [_dio].
- ///
- /// * [ApiInterceptor] handles token injection and response success validation
- /// * [LoggingInterceptor] performs logging of all network requests, only
- /// in debug mode
- /// * [RefreshTokenInterceptor] refreshes an expired token.
- void createDio(BaseOptions baseOptions) {
- _cancelToken = CancelToken();
- _dio = Dio(baseOptions);
- _dio.interceptors.addAll([
- ApiInterceptor(),
- if (kDebugMode) LoggingInterceptor(),
- RefreshTokenInterceptor(_dio),
- ]);
+ /// Attaches any external [Interceptor]s to the underlying [_dio] client.
+ DioService({required Dio dioClient, Iterable? interceptors})
+ : _dio = dioClient, _cancelToken = CancelToken() {
+ if (interceptors != null) _dio.interceptors.addAll(interceptors);
}
/// This method invokes the [cancel()] method on either the input
@@ -77,13 +54,13 @@ class DioService {
CancelToken? cancelToken,
}) async {
try {
- final response = await _dio.get(
+ final response = await _dio.get