Search code examples
flutterdartqr-code

Flutter how to pass data from qr_code_scanner back to the previous page and keep the context


I am trying to add the qr_code_scanner to a flutter project.

What I need to do is to press a Scan button on the MainPage, open the QRScanner page and then pass the result back to the MainPage in a TextEditingController.

I managed to do all that. However, when I pass the QR Scan result back to the MainPage I am replacing the entire MainPage context. I am also resetting the values for all other controllers.

So once I finally do Navigator.pop(context); I am redirected back to the QRScanner, page instead of the Dashboard, which is what I need to do.

My question is, how can I pass the QR Scan result back without replacing the MainPage context? (At least I think this is the problem).

Main Page:

class MainPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _MainPageState();
}

class _MainPageState extends State<MainPage> {
  late Store<AppState> store;
  final TextEditingController _name = new TextEditingController();
  late final TextEditingController _qrText = new TextEditingController();

  late QRScan? args;

  @override
  initState() {
    super.initState();

    //In order to use the ModalRoute.of(context)?.settings.arguments outside build method
    final widgetsBinding = WidgetsBinding.instance;
    widgetsBinding.addPostFrameCallback((callback) async {
      if (ModalRoute.of(context)?.settings.arguments != null) {
        args = ModalRoute.of(context)!.settings.arguments as QRScan?;
        _qrText.text = args!.result!;
      }
    });
  }

  void nextButton() async {
    String name = _name.text;
    String uniqueId = _qrText.text;

    final response = await http.get(Uri.parse('url' ));

    if (response.statusCode == 200) {
      //Do something
      Navigator.pop(context); //Here I get redirected back to /qrScanner Page instead of the Dashboard    
    } else {
      //Throw error message
    }
  }

  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, StoreModel>(
        onInit: (str) => str.dispatch({ store = str, }),
        converter: (Store<AppState> store) => StoreModel.create(store),
        builder: (BuildContext context, StoreModel storeModel) => SafeArea(
            child: Scaffold(
              appBar: new AppBar(),
              body: new Container(
                  child: ListView(shrinkWrap: true, children: <Widget>[
                    new Column(
                      children: <Widget>[
                        _buildTextFields()
                      ],
                    )
                  ])),
            )));
  }

  Widget _buildTextFields() {
    return new Container(
      child: new Column(
        children: <Widget>[
           new TextField(
              controller: _name,
            ),
            new TextField(
              controller: _qrText,
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, "/qrScanner");
              },
              child: Text('Scan'),
            ),
            ElevatedButton(
              onPressed: () {
                nextButton(); //Here I get redirected back to QR Scan page instead of dashboard
              },
              child: Text('Insert'),
              )
        ],
      ),
    );
  }

}

QR Scan Page:

class QRScanner extends StatefulWidget {
  const QRScanner({ Key ? key,  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => _QRScannerState();
}

class _QRScannerState extends State<QRScanner> {
  Barcode? result;
  QRViewController? controller;
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');

  @override
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      controller!.pauseCamera();
    }
    controller!.resumeCamera();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(flex: 4, child: _buildQrView(context)),
        ],
      ),
    );
  }

  Widget _buildQrView(BuildContext context) {
    return QRView(
      key: qrKey,
      onQRViewCreated: _onQRViewCreated,
    );
  }

  void _onQRViewCreated(QRViewController controller) {
    setState(() {
      this.controller = controller;
    });
    controller.scannedDataStream.listen((scanData) {
      setState(() {
        result = scanData;
        controller.pauseCamera();
        controller.resumeCamera();
        passToPreviousPage();
      });
    });
  }

  passToPreviousPage() {
    controller?.pauseCamera();

    Navigator.pushNamed(context, "/mainPage",
        arguments: QRScan(result!.code));
  }

  @override
  void dispose() {
    controller!.dispose();
    super.dispose();
  }
}

class QRScan {
  final String? result;
  QRScan(this.result);
}

Solution

  • You can push a route and use await to get the results. Then from the pushed page you can return the result with Navigator.of(context).pop().

    To do so, use this code when you push the route where you scan the QR code:

    onPressed: () async {
      final qrCode = await Navigator.pushNamed(context, "/qrScanner");
    },
    

    Then in the page where you scan the QR code, return the scanned code with:

    Navigator.of(context).pop(result!.code);
    

    You can also set the type that will be returned by the route:

    final qrCode = await Navigator.pushNamed<Your_Type_Here>(context, "/qrScanner");