feat: legacy mobile apps created
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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'],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user