I am new to Flutter development and i am build an application where i am usign the Bloc library with states and events. I have a page where the user can entre his email address in TextFormField. After that he needs to press the send button where the applicaiton will send an api call throught the Bloc. My issue is with showing a circle progress dialog while the api call is runnign and later hiding the circle loading indicator and showing a success dialog or error message.
I am using clean architecutre so the applicaiton is structures like UI - Bloc - Usecas - Repository - RemoteDataSource. Below is the code for the UI and Bloc which i have used and currenty the app displays a dialog howerve i am not sure if this is the correct way of updating the UI based on the state which i receive from the Bloc. Furthermore i am not sure how display a dialog when the state is Loaded or Error as with the current implementation i need to return a widget. Any help will be greatly appreciated.
class ResetPasswordPage extends StatefulWidget {
@override
_ResetPasswordPageState createState() => _ResetPasswordPageState();
}
class _ResetPasswordPageState extends State<ResetPasswordPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: WebitAppBar(
leading: GestureDetector(
child: Image(
image: new AssetImage('assets/images/toolbar_logo.png'),
height: 10,
width: 10,
),
),
title: Text(
AppLocalizations.of(context)
.translate('reset_password_toolbar_text'),
style: AppTheme.toolbarTitle),
),
body: SingleChildScrollView(
child: buildBody(context),
),
);
}
buildBody(BuildContext context) {
return BlocProvider(
create: (_) => injectionContainer<ResetPasswordBloc>(),
child: Padding(
padding: EdgeInsets.only(
left: 60,
right: 60,
bottom: 0,
),
child: Stack(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ResetPasswordInitialViewState(
isLoading: true,
),
],
),
_buildBlocState(),
],
),
),
);
}
Widget _buildBlocState() {
return BlocBuilder<ResetPasswordBloc, ResetPasswordState>(
builder: (context, state) {
if (state is Empty) {
return Container(
height: 0,
width: 0,
);
} else if (state is Loading) {
return LoadingWidget();
} else if (state is Loaded) {
return Container(
height: 0,
width: 0,
);
} else if (state is Error) {
return Container(
height: 0,
width: 0,
);
}
},
);
}
}
///Error message when text is not supplied
const String INVALID_INPUT_FAILURE_MESSAGE = 'Invalid Input - The text should not be empty';
/// Error message when network call fails
const String SERVER_FAILURE_MESSAGE = 'Server Failure';
class ResetPasswordBloc extends Bloc<ResetPasswordEvent, ResetPasswordState> {
///Property to store the use-case for resetting user password
final ResetPasswordUseCase resetPasswordUseCase;
/// Property to store InputConverter used for validating user input
final InputConverter inputConverter;
ResetPasswordBloc({
@required ResetPasswordUseCase resetPasswordUseCase,
@required InputConverter inputConverter,
}) : assert(resetPasswordUseCase != null),
assert(inputConverter != null),
resetPasswordUseCase = resetPasswordUseCase,
inputConverter = inputConverter;
///Method to supply the initial state of the screen.
@override
ResetPasswordState get initialState => Empty();
///Method called when passing event from the view
@override
Stream<ResetPasswordState> mapEventToState(ResetPasswordEvent event) async* {
if (event is ResetPassword) {
final inputEither = inputConverter.checkIfInputIsEmptry(event.email);
yield* inputEither.fold(
(failure) async* {
yield Error(message: INVALID_INPUT_FAILURE_MESSAGE);
},
(integer) async* {
yield Loading();
final failureOrTrivia =
await resetPasswordUseCase(Params(email: integer));
yield* _eitherLoadedOrErrorState(failureOrTrivia);
},
);
}
}
Stream<ResetPasswordState> _eitherLoadedOrErrorState(
Either<Failure, ResetPasswordEntity> failureOrTrivia,
) async* {
yield failureOrTrivia.fold(
(failure) => Error(message: _mapFailureToMessage(failure)),
(resetPasswordResponse) => Loaded(response: resetPasswordResponse),
);
}
String _mapFailureToMessage(Failure failure) {
switch (failure.runtimeType) {
case ServerFailure:
return SERVER_FAILURE_MESSAGE;
default:
return 'Unexpected error';
}
}
}
@immutable
abstract class ResetPasswordState extends Equatable {
@override
List<Object> get props => [];
}
class Empty extends ResetPasswordState {}
class Loading extends ResetPasswordState {}
class Loaded extends ResetPasswordState {
final ResetPasswordEntity response;
Loaded({@required this.response});
@override
List<Object> get props => [response];
}
class Error extends ResetPasswordState {
final String message;
Error({@required this.message});
@override
List<Object> get props => [message];
}