I want to listen to some changes to the stream's event. Now for that, I am doing it with the provider's StreamProvider.value(). It is an API call. Now what I want is that if the stream's event comes as loading state I want to show loading, If there is any failure I want to call the failure method. If there is a success then I want to call the success method.
I have created a separate widget button which is Consumer that changes according to the stream's event. I have added SchedulerBinding.instance.addPostFrameCallback() inside the consumer's builder method, but it is getting called many times as widget rebuilds and it fires the last event, which is undesirable.
So my question is on stream's event change how can I call those methods or show toast and update UI also?
ConsumerButtonWithProgress
class ConsumerButtonWithProgress<T> extends StatelessWidget {
final void Function(BuildContext context) onTap;
final void Function(T result) onSuccess;
final void Function() onError;
final void Function() onNoInternet;
ConsumerButtonWithProgress(
this.onTap,
this.onSuccess, {
this.onError,
this.onNoInternet,
}) : assert(T != dynamic);
@override
Widget build(BuildContext context) {
return Consumer<APIResult<T>>(
builder: (BuildContext context, APIResult<T> value, Widget child) {
SchedulerBinding.instance.addPostFrameCallback((_) {
if (value != null) {
if (value.apiResultType == APIResultType.NO_INTERNET) {
showSnackBar(context, value.message);
if (onNoInternet != null) Function.apply(onNoInternet, []);
} else if (value.apiResultType == APIResultType.FAILURE) {
showSnackBar(context, value.message);
if (onError != null) Function.apply(onError, []);
} else if (value.apiResultType == APIResultType.SUCCESS) {
showSnackBar(context, value.message);
Function.apply(onSuccess, [value.data]);
}
}
});
return Container(
width: MediaQuery.of(context).size.width,
height: Dimensions.h50,
child: OutlineButton(
color: LocalColors.PRIMARY_COLOR,
textColor: LocalColors.PRIMARY_COLOR,
disabledBorderColor: LocalColors.PRIMARY_COLOR,
highlightedBorderColor: LocalColors.PRIMARY_COLOR,
borderSide: BorderSide(color: LocalColors.PRIMARY_COLOR),
child: APIResult.isLoading(value)
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: Dimensions.h25,
height: Dimensions.h25,
child: CircularProgressIndicator(
strokeWidth: 3,
)),
],
)
: Text(
"Button",
style: TextStyle(
fontSize: Dimensions.sp15,
fontWeight: FontAsset.DEMI_BOLD,
),
),
onPressed: () {
if (onTap != null &&
(value == null ||
value.apiResultType != APIResultType.LOADING))
Function.apply(onTap, [context]);
}),
);
},
);
}
}
And this is how I use that button in my widget
class ForgotPasswordScreen extends StatefulWidget {
@override
_ForgotPasswordScreenState createState() => _ForgotPasswordScreenState();
}
class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
ForgotPasswordProvider _forgotPasswordProvider;
@override
void initState() {
super.initState();
_forgotPasswordProvider = ForgotPasswordProvider();
}
@override
Widget build(BuildContext mainContext) {
return Scaffold(body: Builder(builder: (BuildContext context) {
return StreamProvider.value(
lazy: true,
value: _forgotPasswordProvider.forgotPasswordStream,
child: ConsumerButtonWithProgress<SignInResponseEntity>(
_onSendLinkClick,
(result) {},
),
);
}));
}
void _onSendLinkClick(BuildContext context) {
if (_isDataValid(context)) {
_forgotPasswordProvider.callForgotPasswordAPI();
}
}
}
So is there any way in which I can call methods and show toast from the common widget after update in the stream's event and also update the UI? Just like the executing the code inside the SchedulerBinding.instance.addPostFrameCallback() block.