Listen to changes in stream provider and call some method in Flutter

1.3k views Asked by At

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.

0

There are 0 answers