Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display Dialog or SnackBar from Action's before method #21

Closed
jans-y opened this issue Sep 17, 2019 · 8 comments
Closed

Display Dialog or SnackBar from Action's before method #21

jans-y opened this issue Sep 17, 2019 · 8 comments

Comments

@jans-y
Copy link

jans-y commented Sep 17, 2019

Hi,

I really enjoy using your library. It makes it so much easier to use the Redux with Flutter. Thank you.

I would like to ask how can I get a correct context to be able to display Dialog or SnackBar from the before method inside an abstract class.

abstract class BarrierAction extends ReduxAction<AppState> {
  Future before() async {
    dispatch(SetLoadingAction(true));
    if (!hasConnection) {
      showDialog(
          context: --> ???,
          builder: /* check your internet connection modal */
       )
  }
}

I was hoping something like navigatorKey.currentState.context would work but I wasn't successful.

I have also tried passing current context to the Action itself -> store.dispatch(SomeAction(context)) but I don't know how to pass it into the before method without overriding it, which adds (maybe) unnecessary complexity.

Thank you for your advice.

@gadfly361
Copy link
Contributor

@jans-y
(Caution this may be bad advice 🤷‍♂)

I am personally trying to remove any reference to context whether it be directly (by passing context to an action), or indirectly (by using a global key such as navigatorKey and getting the context that way) to Actions.

Instead, I am leveraging Events to signal that something like that needs to be done, and then consuming the events in a didUpdateWidget method.

For example:

@override
void didUpdateWidget(MyWidget oldWidget) {
  super.didUpdateWidget(oldWidget);
  consumeEvents(context);
}

void consumeEvents(BuildContext context) {
  if (widget.showDialogEvt.consume()) { // code to showDialog here }
}

So you can have a setShowDialogAction that sets the showDialogEvt in its reduce method ... and then you can call setShowDialogAction in the before method of another action

@marcglasberg
Copy link
Owner

@gadfly361 is right. Usually Events are the way to go when you need to display dialogs. Why? Actions belong to the "business layer", and they should not know about Flutter widgets, let alone contexts, which are in the "client/UI layer". So you should use an Event to signal to some widget that it needs to open a dialog.

Just to complement Matthew's answer:

abstract class BarrierAction extends ReduxAction<AppState> {
  Future before() async {
    dispatch(SetLoadingAction(true));
    if (!hasConnection) return state(hasNoConnectionEvt: Event());
    else return null;    
  }
}

One of your widgets should be listening to store.state.hasNoConnectionEvt, and will consume the Event and show a dialog, as Matthew explained.

However, if all you want is a dialog with some text that displays an error message, then there is no need to do any of that. You can just throw an UserException:

abstract class BarrierAction extends ReduxAction<AppState> {
  Future before() async {
    dispatch(SetLoadingAction(true));
    if (!hasConnection) throw UserException("Please check your internet connection.");
    else return null;    
  }
}

This UserException will be caught by AsyncRedux, which will display a dialog with the message. For more details on how to setup AsyncRedux to display these dialogs, see this: https://pub.dev/packages/async_redux#user-exceptions

@jans-y
Copy link
Author

jans-y commented Sep 22, 2019

Thank you for your guidance.

This library is evolving so rapidly that I have must missed the UserException.

It works ok, but I will probably end up building my own "UserException" class which will show Cupertino dialog on iOS and possibly a SnackBar.

@marcglasberg
Copy link
Owner

@jans-y That surely is a good idea of improving the UserExceptionDialog class. Please share your implementation here if you can. Thanks.

@jans-y
Copy link
Author

jans-y commented Dec 5, 2019

Hi,

I forked it locally and did this:

    BuildContext context,
    UserException userException,
  ) {
    if (Platform.isIOS) {
      showCupertinoDialog(
        context: context,
        builder: (BuildContext context) => CupertinoAlertDialog(
          title: Text(userException.dialogTitle()),
          content: Text(userException.dialogContent()),
          actions: [
            CupertinoDialogAction(
              child: Text("OK"),
              onPressed: () => Navigator.of(context).pop(),
            )
          ],
        ),
      );
    } else {
      showDialog(
        context: context,
        builder: (BuildContext context) => AlertDialog(
          title: Text(userException.dialogTitle()),
          content: Text(userException.dialogContent()),
          actions: [
            FlatButton(
              child: Text("OK"),
              onPressed: () => Navigator.of(context).pop(),
            )
          ],
        ),
      );
    }
  }```

@marcglasberg
Copy link
Owner

@jans-y Added this to version 2.3.3.

@jans-y
Copy link
Author

jans-y commented Jan 11, 2020

Hi,

Thank you for adding the code I proposed before. I have started to experiment with Flutter for web and found out that it has to be update to not break web compilation:

➕add import
import 'package:flutter/foundation.dart';

Change condition 📱
if (Platform.isIOS) {
⬇️
if (!kIsWeb && (Platform.isMacOS || Platform.isIOS)) {

I would create PR but I never did it on GitHub on someone's others project.

Thank you.

@marcglasberg
Copy link
Owner

Done: version 2.4.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants