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:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import '../../domain/arguments/get_emergency_contacts_arguments.dart';
|
import '../../domain/arguments/get_emergency_contacts_arguments.dart';
|
||||||
import '../../domain/arguments/save_emergency_contacts_arguments.dart';
|
import '../../domain/arguments/save_emergency_contacts_arguments.dart';
|
||||||
import '../../domain/usecases/get_emergency_contacts_usecase.dart';
|
import '../../domain/usecases/get_emergency_contacts_usecase.dart';
|
||||||
import '../../domain/usecases/save_emergency_contacts_usecase.dart';
|
import '../../domain/usecases/save_emergency_contacts_usecase.dart';
|
||||||
|
import 'emergency_contact_event.dart';
|
||||||
|
import 'emergency_contact_state.dart';
|
||||||
|
|
||||||
// Events
|
export 'emergency_contact_event.dart';
|
||||||
abstract class EmergencyContactEvent extends Equatable {
|
export 'emergency_contact_state.dart';
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
// BLoC
|
// BLoC
|
||||||
class EmergencyContactBloc
|
class EmergencyContactBloc
|
||||||
@@ -106,7 +40,7 @@ class EmergencyContactBloc
|
|||||||
const GetEmergencyContactsArguments(),
|
const GetEmergencyContactsArguments(),
|
||||||
);
|
);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
status: EmergencyContactStatus.success,
|
status: EmergencyContactStatus.loaded,
|
||||||
contacts: contacts.isNotEmpty
|
contacts: contacts.isNotEmpty
|
||||||
? contacts
|
? contacts
|
||||||
: [EmergencyContact.empty()],
|
: [EmergencyContact.empty()],
|
||||||
@@ -157,7 +91,7 @@ class EmergencyContactBloc
|
|||||||
contacts: state.contacts,
|
contacts: state.contacts,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
emit(state.copyWith(status: EmergencyContactStatus.success));
|
emit(state.copyWith(status: EmergencyContactStatus.saved));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
status: EmergencyContactStatus.failure,
|
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';
|
import '../blocs/emergency_contact_bloc.dart';
|
||||||
|
|
||||||
class EmergencyContactSaveButton extends StatelessWidget {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return BlocConsumer<EmergencyContactBloc, EmergencyContactState>(
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
listenWhen: (previous, current) => previous.status != current.status,
|
||||||
decoration: BoxDecoration(
|
listener: (context, state) {
|
||||||
color: UiColors.bgPopup,
|
if (state.status == EmergencyContactStatus.saved) {
|
||||||
border: Border(top: BorderSide(color: UiColors.border)),
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
),
|
SnackBar(
|
||||||
child: SafeArea(
|
content: Text(
|
||||||
child: UiButton.primary(
|
'Emergency contacts saved successfully',
|
||||||
fullWidth: true,
|
style: UiTypography.body2r.textPrimary,
|
||||||
onPressed: state.isValid
|
),
|
||||||
? () => context
|
backgroundColor: UiColors.iconSuccess,
|
||||||
.read<EmergencyContactBloc>()
|
),
|
||||||
.add(EmergencyContactsSaved())
|
);
|
||||||
: null,
|
}
|
||||||
child: state.status == EmergencyContactStatus.saving
|
},
|
||||||
? SizedBox(
|
builder: (context, state) {
|
||||||
height: 20.0,
|
final isLoading = state.status == EmergencyContactStatus.saving;
|
||||||
width: 20.0,
|
return Container(
|
||||||
child: CircularProgressIndicator(
|
padding: EdgeInsets.all(UiConstants.space4),
|
||||||
strokeWidth: 2,
|
decoration: BoxDecoration(
|
||||||
valueColor:
|
color: UiColors.bgPopup,
|
||||||
AlwaysStoppedAnimation<Color>(UiColors.primaryForeground),
|
border: Border(top: BorderSide(color: UiColors.border)),
|
||||||
),
|
),
|
||||||
)
|
child: SafeArea(
|
||||||
: Text(
|
child: UiButton.primary(
|
||||||
'Save & Continue',
|
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