Francesco Pagano
Francesco Pagano

Reputation: 653

AlertDialog without context in Flutter

I want to show an AlertDialog when a http get fails. The function showDialog (https://api.flutter.dev/flutter/material/showDialog.html) has the parameter "@required BuildContext context", but I want to call the AlertDialog from my async function getNews(), which hasn't a context value.

By analogy with Java, where I use null for dialog without an owner, I tried to put context value to null, but it is not accepted.

This is my code:

  Future<dynamic> getNews() async {
    dynamic retVal;
    try {
      var response = await http.get(url));
      if (response.statusCode == HttpStatus.ok) {
        retVal = jsonDecode(response.body);
      }
    } catch (e) {
      alertDlg(?????????, 'Error', e.toString());
  }
    return
    retVal;
  }

  static Future<void> alertDlg(context, String titolo, String messaggio) async {
    return showDialog<void>(
        context: context,
        barrierDismissible: false, // user must tap button!
        builder: (BuildContext context) {
        return AlertDialog(
              title: Text(titolo),
        ...
    );
  }

Upvotes: 65

Views: 62958

Answers (6)

jesus gilbert
jesus gilbert

Reputation: 17

  1. Install GetX with: flutter pub add get
  2. create a dialog with Get.dialog
  3. add these line
Get.dialog(
      Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 40),
            child: Container(
              decoration: const BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.all(
                  Radius.circular(20),
                ),
              ),
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Material(
                  child: Column(
                    children: [
                      const Text("Dialog with GetX without context")
                      Row(
                        children: [
                          const SizedBox(width: 10),
                          Expanded(
                            child: ElevatedButton(
                              style: ElevatedButton.styleFrom(
                                minimumSize: const Size(0, 45),
                                primary: Colors.green,
                                onPrimary: const Color(0xFFFFFFFF),
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              onPressed: () {
                                Navigator.pop(Get.overlayContext!, true);
                              },
                              child: const Text(
                                'Cerrar',
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );

done!

Upvotes: -2

user3107831
user3107831

Reputation: 85

Easiest way to show alert anywhere: Use this package link and enter these lines wherever you want to show alert:

QuickAlert.show( context: context, type: QuickAlertType.error, text: 'Error Message');

Upvotes: -5

jonatas Borges
jonatas Borges

Reputation: 2237

The dialog only needs the context to access it through an inherited navigateState. You can make your own modified dialog, or use my lib to do this.

https://pub.dev/packages/get

With it you can open dialog from anywhere in your code without context by doing this:

Get.dialog(SimpleDialog());

Upvotes: 10

Igor Kharakhordin
Igor Kharakhordin

Reputation: 9933

Solution without third-party libraries or storing BuildContext:

final navigatorKey = GlobalKey<NavigatorState>();

void main() => runApp(
  MaterialApp(
    home: HomePage(),
    navigatorKey: navigatorKey, // Setting a global key for navigator
  ),
);

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Center(
          child: Text('test')
        )
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: showMyDialog, // Calling the function without providing it BuildContext
      ),
    );
  }
}

void showMyDialog() {
  showDialog(
    context: navigatorKey.currentContext,
    builder: (context) => Center(
      child: Material(
        color: Colors.transparent,
        child: Text('Hello'),
      ),
    )
  );
}

After setting a global key for navigator (navigatorKey parameter of MaterialApp class), its current state becomes accessible from any part of the code. We can pass its context to the showDialog function. Make sure not to use it before the first frame is built, otherwise the state will be null.

Basically, dialogs are just another type of routes like MaterialPageRoute or CupertinoPageRoute - they are all derived from the ModalRoute class and their instances are pushed into NavigatorState. Context is only needed to get the current navigator's state. A new route can be pushed without having a context if we have a global navigator key:

navigatorKey.currentState.push(route)

Unfortunately, _DialogRoute class (...\flutter\lib\src\widgets\routes.dart) used in showDialog function is private and unaccessible, but you make your own dialog route class and push it into the navigator's stack.

UPDATE: Navigator.of method has been updated and it's no longer needed to pass subtree context.

Upvotes: 133

Kirill Karmazin
Kirill Karmazin

Reputation: 6771

So you need a BuildContext to create a dialog but you don't have access to it. That's a common problem, you can refer to this StackOverflow question for one of the approaches to solve it (create a static dialog and show it from wherever you need).

Another approach you may consider is to pass the context when creating an async method or object as an argument. Make sure you null it when you're done.

Or you can make a flag (boolean) which becomes 'true' under a certain condition, and in one of the build() methods you always check that flag, and if it's 'true' - do your thing (show a dialog for instance).

Upvotes: 1

ZeRj
ZeRj

Reputation: 1708

Catch the exception where you make the getNews call if you use await, else use the catchError property of the Future.

Upvotes: 1

Related Questions