feat: implement emergency contact management with BLoC, including event and state handling
This commit is contained in:
@@ -1,80 +1,14 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/arguments/get_emergency_contacts_arguments.dart';
|
||||
import '../../domain/arguments/save_emergency_contacts_arguments.dart';
|
||||
import '../../domain/usecases/get_emergency_contacts_usecase.dart';
|
||||
import '../../domain/usecases/save_emergency_contacts_usecase.dart';
|
||||
import 'emergency_contact_event.dart';
|
||||
import 'emergency_contact_state.dart';
|
||||
|
||||
// Events
|
||||
abstract class EmergencyContactEvent extends Equatable {
|
||||
const EmergencyContactEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class EmergencyContactsLoaded extends EmergencyContactEvent {}
|
||||
|
||||
class EmergencyContactAdded extends EmergencyContactEvent {}
|
||||
|
||||
class EmergencyContactRemoved extends EmergencyContactEvent {
|
||||
final int index;
|
||||
|
||||
const EmergencyContactRemoved(this.index);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index];
|
||||
}
|
||||
|
||||
class EmergencyContactUpdated extends EmergencyContactEvent {
|
||||
final int index;
|
||||
final EmergencyContact contact;
|
||||
|
||||
const EmergencyContactUpdated(this.index, this.contact);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index, contact];
|
||||
}
|
||||
|
||||
class EmergencyContactsSaved extends EmergencyContactEvent {}
|
||||
|
||||
// State
|
||||
enum EmergencyContactStatus { initial, loading, success, saving, failure }
|
||||
|
||||
class EmergencyContactState extends Equatable {
|
||||
final EmergencyContactStatus status;
|
||||
final List<EmergencyContact> contacts;
|
||||
final String? errorMessage;
|
||||
|
||||
const EmergencyContactState({
|
||||
this.status = EmergencyContactStatus.initial,
|
||||
this.contacts = const [],
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
EmergencyContactState copyWith({
|
||||
EmergencyContactStatus? status,
|
||||
List<EmergencyContact>? contacts,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return EmergencyContactState(
|
||||
status: status ?? this.status,
|
||||
contacts: contacts ?? this.contacts,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
bool get isValid {
|
||||
if (contacts.isEmpty) return false;
|
||||
// Check if at least one contact is valid (or all?)
|
||||
// Usually all added contacts should be valid.
|
||||
return contacts.every((c) => c.name.isNotEmpty && c.phone.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, contacts, errorMessage];
|
||||
}
|
||||
export 'emergency_contact_event.dart';
|
||||
export 'emergency_contact_state.dart';
|
||||
|
||||
// BLoC
|
||||
class EmergencyContactBloc
|
||||
@@ -106,7 +40,7 @@ class EmergencyContactBloc
|
||||
const GetEmergencyContactsArguments(),
|
||||
);
|
||||
emit(state.copyWith(
|
||||
status: EmergencyContactStatus.success,
|
||||
status: EmergencyContactStatus.loaded,
|
||||
contacts: contacts.isNotEmpty
|
||||
? contacts
|
||||
: [EmergencyContact.empty()],
|
||||
@@ -157,7 +91,7 @@ class EmergencyContactBloc
|
||||
contacts: state.contacts,
|
||||
),
|
||||
);
|
||||
emit(state.copyWith(status: EmergencyContactStatus.success));
|
||||
emit(state.copyWith(status: EmergencyContactStatus.saved));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: EmergencyContactStatus.failure,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
abstract class EmergencyContactEvent extends Equatable {
|
||||
const EmergencyContactEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class EmergencyContactsLoaded extends EmergencyContactEvent {}
|
||||
|
||||
class EmergencyContactAdded extends EmergencyContactEvent {}
|
||||
|
||||
class EmergencyContactRemoved extends EmergencyContactEvent {
|
||||
final int index;
|
||||
|
||||
const EmergencyContactRemoved(this.index);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index];
|
||||
}
|
||||
|
||||
class EmergencyContactUpdated extends EmergencyContactEvent {
|
||||
final int index;
|
||||
final EmergencyContact contact;
|
||||
|
||||
const EmergencyContactUpdated(this.index, this.contact);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index, contact];
|
||||
}
|
||||
|
||||
class EmergencyContactsSaved extends EmergencyContactEvent {}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
enum EmergencyContactStatus { initial, loading, loaded, saving, saved, failure }
|
||||
|
||||
class EmergencyContactState extends Equatable {
|
||||
final EmergencyContactStatus status;
|
||||
final List<EmergencyContact> contacts;
|
||||
final String? errorMessage;
|
||||
|
||||
const EmergencyContactState({
|
||||
this.status = EmergencyContactStatus.initial,
|
||||
this.contacts = const [],
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
EmergencyContactState copyWith({
|
||||
EmergencyContactStatus? status,
|
||||
List<EmergencyContact>? contacts,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return EmergencyContactState(
|
||||
status: status ?? this.status,
|
||||
contacts: contacts ?? this.contacts,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
bool get isValid {
|
||||
if (contacts.isEmpty) return false;
|
||||
// Check if at least one contact is valid (or all?)
|
||||
// Usually all added contacts should be valid.
|
||||
return contacts.every((c) => c.name.isNotEmpty && c.phone.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, contacts, errorMessage];
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class EmergencyContactScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
EmergencyContactSaveButton(state: state),
|
||||
const EmergencyContactSaveButton(),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -4,41 +4,59 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../blocs/emergency_contact_bloc.dart';
|
||||
|
||||
class EmergencyContactSaveButton extends StatelessWidget {
|
||||
final EmergencyContactState state;
|
||||
const EmergencyContactSaveButton({super.key});
|
||||
|
||||
const EmergencyContactSaveButton({super.key, required this.state});
|
||||
void _onSave(BuildContext context) {
|
||||
context.read<EmergencyContactBloc>().add(EmergencyContactsSaved());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
border: Border(top: BorderSide(color: UiColors.border)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: UiButton.primary(
|
||||
fullWidth: true,
|
||||
onPressed: state.isValid
|
||||
? () => context
|
||||
.read<EmergencyContactBloc>()
|
||||
.add(EmergencyContactsSaved())
|
||||
: null,
|
||||
child: state.status == EmergencyContactStatus.saving
|
||||
? SizedBox(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(UiColors.primaryForeground),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'Save & Continue',
|
||||
),
|
||||
),
|
||||
),
|
||||
return BlocConsumer<EmergencyContactBloc, EmergencyContactState>(
|
||||
listenWhen: (previous, current) => previous.status != current.status,
|
||||
listener: (context, state) {
|
||||
if (state.status == EmergencyContactStatus.saved) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Emergency contacts saved successfully',
|
||||
style: UiTypography.body2r.textPrimary,
|
||||
),
|
||||
backgroundColor: UiColors.iconSuccess,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
final isLoading = state.status == EmergencyContactStatus.saving;
|
||||
return Container(
|
||||
padding: EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
border: Border(top: BorderSide(color: UiColors.border)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: UiButton.primary(
|
||||
fullWidth: true,
|
||||
onPressed: state.isValid && !isLoading
|
||||
? () => _onSave(context)
|
||||
: null,
|
||||
child: isLoading
|
||||
? SizedBox(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
UiColors.primaryForeground,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Text('Save & Continue'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user