I try to use a QR Code Scanner from qr_code_scanner in conjunction with a Webview component webview_flutter.

Everything works fine on iOS but on Android devices it doesn't work, the QR scanner doesn't show and I get a repeated console print.

D/mali_winsys(30667): EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, EGLBoolean) returns 0x3000 

I've tried this on two Android devices (Android 10, v29 and Android 7, v24) with same results.

Below is a minimal app that reproduce the issue. It requires the following dependencies:

qr_code_scanner: ^0.3.5
webview_flutter: ^2.0.2 

The code below shows a full-screen webview with a button on-top. Press the button and the QR scanner will/should show up...

import 'package:flutter/material.dart';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _showQr = false;

  @override
  void initState() {
    super.initState();
    // Enable hybrid composition.
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  void closeQr() {
    setState(() {
      _showQr = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Stack(
          children: [
            WebView(
              initialUrl: 'https://flutter.dev',
            ),
            Center(
              child: TextButton(
                onPressed: () {
                  setState(() {
                    _showQr = !_showQr;
                  });
                },
                child: Text('Show QR Scanner'),
                style: TextButton.styleFrom(
                  primary: Colors.white,
                  backgroundColor: Colors.teal,
                  onSurface: Colors.grey,
                ),
              ),
            ),
          ],
        ),
        Center(
          child: (_showQr) ? QRWidget(onClose: closeQr) : null,
        ),
      ],
    );
  }
}

class QRWidget extends StatefulWidget {
  const QRWidget({
    Key key,
    this.onClose,
  }) : super(key: key);

  final Function onClose;

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

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

  // In order to get hot reload to work we need to pause the camera if the platform
  // is android, or resume the camera if the platform is iOS.
  @override
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      controller.pauseCamera();
    }
    controller.resumeCamera();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          _buildQrView(context),
          Container(
            alignment: Alignment.bottomCenter,
            padding: EdgeInsets.only(bottom: 60.0),
            child: Row(
              children: <Widget>[
                Expanded(
                  child: RawMaterialButton(
                    onPressed: () {
                      setState(() {
                        widget.onClose();
                      });
                    },
                    elevation: 2.0,
                    fillColor: Colors.white,
                    child: Icon(
                      Icons.close_sharp,
                      color: Color(0xff459d44),
                      size: 40.0,
                    ),
                    padding: EdgeInsets.all(8.0),
                    shape: CircleBorder(),
                  ),
                ),
              ],
            ),
          )
        ],
      ),
    );
  }

  Widget _buildQrView(BuildContext context) {
    // For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
    var scanArea = (MediaQuery.of(context).size.width < 400 ||
            MediaQuery.of(context).size.height < 400)
        ? 150.0
        : 300.0;
    // To ensure the Scanner view is properly sizes after rotation
    // we need to listen for Flutter SizeChanged notification and update controller
    return QRView(
      key: qrKey,
      onQRViewCreated: _onQRViewCreated,
      overlay: QrScannerOverlayShape(
          borderColor: Color(0xff459d44),
          borderRadius: 10,
          borderLength: 30,
          borderWidth: 10,
          cutOutSize: scanArea),
    );
  }

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

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

Why doesn't it work on Android?


Solution 1: Trend74X

did you add the permission in AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />

if its webview, why dont you use flutter_inappwebview . Its good to use and has a lot of additional features you may want later. it still needs permission on androidmanifest. Following is the example if you decide to choose flutter_inappwebview.

class _HomePageState extends State<HomePage> {
  InAppWebViewController webView;
  String url = "";

  @override
  void initState(){
    checkPermissions();
    super.initState();
  }

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

    checkPermissions() async{
    await [
      Permission.camera,
      Permission.storage,
    ].request();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: SafeArea(
        child: Container(
          child: Column(children: <Widget>[
            Expanded(
              child: Container(
                child: InAppWebView(
                  initialUrl: 'https://flutter.dev',
                  initialHeaders: {},
                  initialOptions: InAppWebViewGroupOptions(
                    crossPlatform: InAppWebViewOptions(
                        debuggingEnabled: true,
                    )
                  ),
                  onWebViewCreated: (InAppWebViewController controller) {
                    webView = controller;
                  },
                  onLoadStart: (InAppWebViewController controller, String url) {
                    setState(() {
                      this.url = url;
                    });
                  },
                  onLoadStop: (InAppWebViewController controller, String url) async {
                    setState(() {
                      this.url = url;
                    });
                  },
                  /// this is the important one to pass the permission
                  androidOnPermissionRequest: (InAppWebViewController controller, String origin, List<String> resources) async {
                    return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT);
                  },
                ),
              ),
            ),
          ])
        ),
      ),
    );
  }
}

dont forget to add this permission_handler in your pubspec.yaml