Search code examples
angularjswebviewfluttercamerawebrtc

How to fix cannot access video stream (NotAllowedError) in flutter webview to use html5+webRTC camera api?


I am using instascan.min js library to scan QR Codes in my web angular js application. but in my flutter web view, I got an error - Cannot access video stream (NotAllowedError), And I am unable to fix it. I don't have too much knowledge of flutter.

I have tried to give permission to camera access but it's not working.

//# My Flutter Code

import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';


void main () => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
  title: 'FOTOFACE WALLET',
  debugShowCheckedModeBanner: false,
  home: Home(),
);
}
}

class Home extends StatefulWidget{
@override
 _HomeState createState() => _HomeState();
}



class _HomeState extends State<Home>{
@override
Widget build(BuildContext context) {
 return WebviewScaffold(
  appBar: PreferredSize(
      preferredSize: Size.fromHeight(0),
      child: AppBar(
        automaticallyImplyLeading: true, // hides leading widget
        backgroundColor: new Color(0xFF404E67),
      )
  ),

  url: "https://fotofacewallet.com",
  initialChild: Center(
    child: CircularProgressIndicator(),
  ),
);
}
}

//# this is my scanner code in angular js controller

$scope.scan = () => {
var overlay = $('.overlay'),
close = $('<div class="close" id="closescanbtn">close</div>');
overlay.append(close);
let scanner = new Instascan.Scanner({
   video: document.getElementById('preview')
});

scanner.addListener('scan', function (content) {
scanner.stop();
$('.overlay').fadeOut();
$('.overlay').hide();
$scope.scanpayProcess(content);
});
Instascan.Camera.getCameras().then(function (cameras) {

if (cameras.length > 0) {
    if(cameras[1]){
        scanner.start(cameras[1]);
    } else {
        scanner.start(cameras[0]);
    } 
} else {
  alert('No cameras found.');
}


}).catch(function (e) {
alert(e);
});
$('.overlay').show();
}

I am expecting a camera view in a flutter web view.


Solution

  • I have done something like this

    on my index.html page or the file that you have created in your angular side

    I have added one button :

    <button type="button" onclick="displayMsg()" class="btn btn-default btn-login">Scan</button>
    

    and handled its click event as below and we will post message as 'scan'

    <script type="text/javascript">
                function displayMsg(){
                    Print.postMessage("scan");
                }
            </script>
    

    added this package in my pubspec.yaml file

    flutter_barcode_scanner: ^0.1.5+1

    do run flutter pub get to update dependencies

    then on my main.dart file

    I have imported

    import 'dart:async';
    import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
    

    in main.dart file added below code to handle the event triggered on displayMsg() function - 'scan' message

      final Set<JavascriptChannel> jsChannels = [
          JavascriptChannel(
              name: 'Print',
              onMessageReceived: (JavascriptMessage message) {
                if(message.message == 'scan'){
    
                  print(message.message);
                  sBarcode(MyApp());
                }
              }),
        ].toSet(); 
    
    
    
      sBarcode(someVal) async {  
     String bCode = await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true);   print(bCode);
       someVal.enterBarcode(bCode); // to get the scanned barcode    
    return; }
    
    enterBarcode(barc) {
        flutterWebViewPlugin.evalJavascript("document.getElementById('yourtextboxid').value="
    + barc);   }
    

    This is how my complete main.dart file looks now

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
    import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
    
    const kAndroidUserAgent =
        'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36';
    
    
    String selectedUrl = 'add_your_url_here';
    
    // ignore: prefer_collection_literals
    final Set<JavascriptChannel> jsChannels = [
      JavascriptChannel(
          name: 'Print',
          onMessageReceived: (JavascriptMessage message) {
            if(message.message == 'scan'){
              //MyApp.startBarcode();
              print(message.message);
              sBarcode(MyApp());
            }
          }),
    ].toSet();
    
    sBarcode(someVal) async {
      String bCode = await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true);
      print(bCode);
      someVal.enterBarcode(bCode);
      return;
    }
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      final flutterWebViewPlugin = FlutterWebviewPlugin();
    
      enterBarcode(barc) {
        flutterWebViewPlugin.evalJavascript("document.getElementById('barcodenumber').value=" + barc);
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter WebView Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          routes: {
           // '/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
            '/': (_) {
              return WebviewScaffold(
                url: selectedUrl,
                javascriptChannels: jsChannels,
                withZoom: true,
                withLocalStorage: true,
                withJavascript: true,
                hidden: true,
                initialChild: Container(
                  color: Colors.white,
                  child: const Center(
                    child: Text('Loading...'),
                  ),
                ),
              );
            },
          },
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      // Instance of WebView plugin
      final flutterWebViewPlugin = FlutterWebviewPlugin();
    
      // On destroy stream
      StreamSubscription _onDestroy;
    
      // On urlChanged stream
      StreamSubscription<String> _onUrlChanged;
    
      // On urlChanged stream
      StreamSubscription<WebViewStateChanged> _onStateChanged;
    
      StreamSubscription<WebViewHttpError> _onHttpError;
    
      StreamSubscription<double> _onProgressChanged;
    
      StreamSubscription<double> _onScrollYChanged;
    
      StreamSubscription<double> _onScrollXChanged;
    
      final _urlCtrl = TextEditingController(text: selectedUrl);
    
      final _codeCtrl = TextEditingController(text: 'window.navigator.userAgent');
    
      final _scaffoldKey = GlobalKey<ScaffoldState>();
    
      final _history = [];
    
      @override
      void initState() {
        super.initState();
    
        flutterWebViewPlugin.close();
    
        _urlCtrl.addListener(() {
          selectedUrl = _urlCtrl.text;
        });
    
        // Add a listener to on destroy WebView, so you can make came actions.
        _onDestroy = flutterWebViewPlugin.onDestroy.listen((_) {
          if (mounted) {
            // Actions like show a info toast.
            _scaffoldKey.currentState.showSnackBar(
                const SnackBar(content: const Text('Webview Destroyed')));
          }
        });
    
        // Add a listener to on url changed
        _onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url) {
          if (mounted) {
            setState(() {
              _history.add('onUrlChanged: $url');
            });
          }
        });
    
        _onProgressChanged =
            flutterWebViewPlugin.onProgressChanged.listen((double progress) {
          if (mounted) {
            setState(() {
              _history.add('onProgressChanged: $progress');
            });
          }
        });
    
        _onScrollYChanged =
            flutterWebViewPlugin.onScrollYChanged.listen((double y) {
          if (mounted) {
            setState(() {
              _history.add('Scroll in Y Direction: $y');
            });
          }
        });
    
        _onScrollXChanged =
            flutterWebViewPlugin.onScrollXChanged.listen((double x) {
          if (mounted) {
            setState(() {
              _history.add('Scroll in X Direction: $x');
            });
          }
        });
    
        _onStateChanged =
            flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state) {
              print(state.type);
          if (mounted) {
            setState(() {
              _history.add('onStateChanged: ${state.type} ${state.url}');
            });
          }
        });
    
        _onHttpError =
            flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error) {
          if (mounted) {
            setState(() {
              _history.add('onHttpError: ${error.code} ${error.url}');
            });
          }
        });
      }
    
      @override
      void dispose() {
        // Every listener should be canceled, the same should be done with this stream.
        _onDestroy.cancel();
        _onUrlChanged.cancel();
        _onStateChanged.cancel();
        _onHttpError.cancel();
        _onProgressChanged.cancel();
        _onScrollXChanged.cancel();
        _onScrollYChanged.cancel();
    
        flutterWebViewPlugin.dispose();
    
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          key: _scaffoldKey,
          appBar: AppBar(
            title: const Text('Plugin example app'),
          ),
          body: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Container(
                  padding: const EdgeInsets.all(24.0),
                  child: TextField(controller: _urlCtrl),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.launch(
                      selectedUrl,
                      rect: Rect.fromLTWH(
                          0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
                      userAgent: kAndroidUserAgent,
                      invalidUrlRegex:
                          r'^(https).+(twitter)', // prevent redirecting to twitter when user click on its icon in flutter website
                    );
                  },
                  child: const Text('Open Webview (rect)'),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.launch(selectedUrl, hidden: true);
                  },
                  child: const Text('Open "hidden" Webview'),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.launch(selectedUrl);
                  },
                  child: const Text('Open Fullscreen Webview'),
                ),
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).pushNamed('/widget');
                  },
                  child: const Text('Open widget webview'),
                ),
                Container(
                  padding: const EdgeInsets.all(24.0),
                  child: TextField(controller: _codeCtrl),
                ),
                RaisedButton(
                  onPressed: () {
                    final future =
                        flutterWebViewPlugin.evalJavascript(_codeCtrl.text);
                    future.then((String result) {
                      setState(() {
                        _history.add('eval: $result');
                      });
                    });
                  },
                  child: const Text('Eval some javascript'),
                ),
                RaisedButton(
                  onPressed: () {
                    setState(() {
                      _history.clear();
                    });
                    flutterWebViewPlugin.close();
                  },
                  child: const Text('Close'),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.getCookies().then((m) {
                      setState(() {
                        _history.add('cookies: $m');
                      });
                    });
                  },
                  child: const Text('Cookies'),
                ),
                Text(_history.join('\n'))
              ],
            ),
          ),
        );
      }
    }
    

    check and test and let me know if that works for you or not, This is working fine at my end. Print will generate error in browser you need to test this in android or ios. Hope this helps.