feat: Refactor code structure and optimize performance across multiple modules

This commit is contained in:
Achintha Isuru
2025-11-17 23:29:28 -05:00
parent 831570f2e0
commit a64cbd9edf
1508 changed files with 105319 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
import 'package:injectable/injectable.dart';
import 'package:krow/core/data/models/staff/full_address_model.dart';
import 'package:krow/features/profile/address/data/address_api_provider.dart';
import 'package:krow/features/profile/address/data/address_repository.dart';
@Singleton(as: AddressRepository)
class AddressRepositoryImpl implements AddressRepository {
final AddressApiProvider _apiProvider;
AddressRepositoryImpl({required AddressApiProvider apiProvider})
: _apiProvider = apiProvider;
@override
Stream<FullAddress?> getStaffAddress() {
return _apiProvider.getStaffAddress();
}
@override
Future<void> putAddress(FullAddress address) {
return _apiProvider.putAddress(address);
}
}

View File

@@ -0,0 +1,62 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/models/staff/full_address_model.dart';
import 'package:krow/core/data/enums/state_status.dart';
import 'package:krow/features/profile/address/data/address_repository.dart';
import 'package:krow/features/profile/address/domain/google_places_service.dart';
part 'address_event.dart';
part 'address_state.dart';
class AddressBloc extends Bloc<AddressEvent, AddressState> {
AddressBloc() : super(const AddressState()) {
on<InitializeAddressEvent>(_onInitialize);
on<SubmitAddressEvent>(_onSubmit);
on<AddressQueryChangedEvent>(_onQueryChanged);
on<AddressSelectEvent>(_onSelect);
}
void _onInitialize(
InitializeAddressEvent event, Emitter<AddressState> emit) async {
emit(state.copyWith(status: StateStatus.loading));
await for (var address in getIt<AddressRepository>().getStaffAddress()) {
emit(state.copyWith(
fullAddress: address,
status: StateStatus.idle,
));
}
}
void _onQueryChanged(
AddressQueryChangedEvent event, Emitter<AddressState> emit) async {
try {
final googlePlacesService = GooglePlacesService();
final suggestions =
await googlePlacesService.fetchSuggestions(event.query);
emit(state.copyWith(suggestions: suggestions));
} catch (e) {
if (kDebugMode) print(e);
}
}
void _onSelect(AddressSelectEvent event, Emitter<AddressState> emit) async {
final googlePlacesService = GooglePlacesService();
final fullAddress =
await googlePlacesService.getPlaceDetails(event.place.placeId);
FullAddress address = FullAddress.fromGoogle(fullAddress);
emit(state.copyWith(suggestions: [], fullAddress: address));
}
void _onSubmit(SubmitAddressEvent event, Emitter<AddressState> emit) async {
emit(state.copyWith(status: StateStatus.loading));
try {
await getIt<AddressRepository>().putAddress(state.fullAddress!);
emit(state.copyWith(status: StateStatus.success));
} catch (e) {
emit(state.copyWith(status: StateStatus.error));
}
}
}

View File

@@ -0,0 +1,24 @@
part of 'address_bloc.dart';
@immutable
sealed class AddressEvent {}
class InitializeAddressEvent extends AddressEvent {
InitializeAddressEvent();
}
class AddressQueryChangedEvent extends AddressEvent {
final String query;
AddressQueryChangedEvent(this.query);
}
class SubmitAddressEvent extends AddressEvent {
SubmitAddressEvent();
}
class AddressSelectEvent extends AddressEvent {
final MapPlace place;
AddressSelectEvent(this.place);
}

View File

@@ -0,0 +1,26 @@
part of 'address_bloc.dart';
@immutable
class AddressState {
final StateStatus status;
final FullAddress? fullAddress;
final List<MapPlace> suggestions;
const AddressState({
this.status = StateStatus.idle,
this.fullAddress,
this.suggestions = const [],
});
AddressState copyWith({
StateStatus? status,
FullAddress? fullAddress,
List<MapPlace>? suggestions,
}) {
return AddressState(
status: status ?? this.status,
suggestions: suggestions ?? this.suggestions,
fullAddress: fullAddress ?? this.fullAddress,
);
}
}

View File

@@ -0,0 +1,98 @@
import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
import 'package:injectable/injectable.dart';
@singleton
class GooglePlacesService {
Future<List<MapPlace>> fetchSuggestions(String query) async {
final String apiKey = dotenv.env['GOOGLE_MAP']!;
const String baseUrl =
'https://maps.googleapis.com/maps/api/place/autocomplete/json';
final Uri uri =
Uri.parse('$baseUrl?input=$query&key=$apiKey&types=geocode');
final response = await http.get(uri);
if (response.statusCode == 200) {
final data = json.decode(response.body);
final List<dynamic> predictions = data['predictions'];
return predictions.map((prediction) {
return MapPlace.fromJson(prediction);
}).toList();
} else {
throw Exception('Failed to fetch place suggestions');
}
}
Future<Map<String, dynamic>> getPlaceDetails(String placeId) async {
final String apiKey = dotenv.env['GOOGLE_MAP']!;
final String url =
'https://maps.googleapis.com/maps/api/place/details/json?place_id=$placeId&key=$apiKey';
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final data = json.decode(response.body);
final result = data['result'];
final location = result['geometry']['location'];
Map<String, dynamic> addressDetails = {
'lat': location['lat'], // Latitude
'lng': location['lng'], // Longitude
'formatted_address': result['formatted_address'], // Full Address
};
for (var component in result['address_components']) {
List types = component['types'];
if (types.contains('street_number')) {
addressDetails['street_number'] = component['long_name'];
}
if (types.contains('route')) {
addressDetails['street'] = component['long_name'];
}
if (types.contains('locality')) {
addressDetails['city'] = component['long_name'];
}
if (types.contains('administrative_area_level_1')) {
addressDetails['state'] = component['long_name'];
}
if (types.contains('country')) {
addressDetails['country'] = component['long_name'];
}
if (types.contains('postal_code')) {
addressDetails['postal_code'] = component['long_name'];
}
}
return addressDetails;
} else {
throw Exception('Failed to fetch place details');
}
}
}
class MapPlace {
final String description;
final String placeId;
MapPlace({required this.description, required this.placeId});
toJson() {
return {
'description': description,
'place_id': placeId,
};
}
factory MapPlace.fromJson(Map<String, dynamic> json) {
return MapPlace(
description: json['description'],
placeId: json['place_id'],
);
}
}