如何修复无法访问颤振网页视图中的视频流(不允许错误)以使用 html5+webRTC 相机 api



我正在使用instascan.min js库在我的web angular js应用程序中扫描QR码。 但是在我的颤振网络视图中,我遇到了一个错误 - 无法访问视频流(不允许错误(,并且我无法修复它。我对颤振没有太多了解。

我试图授予相机访问的权限,但它不起作用。

/

/# 我的颤振代码

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(),
  ),
);
}
}
/

/# 这是我在 Angular JS 控制器中的扫描仪代码

$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();
}

我期待颤振 Web 视图中的相机视图。

我做过这样的事情

在我的索引.html页面上或您在角度边中创建的文件

我添加了一个按钮:

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

并按如下方式处理其点击事件,我们将消息发布为"扫描">

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

在我的 pubspec.yaml 文件中添加了此软件包

flutter_barcode_scanner: ^0.1.5+1

运行flutter pub get以更新依赖项

然后在我的主.dart文件上

我已导入

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

在 main.dart 文件中添加了以下代码,以处理在 displayMsg(( 函数上触发的事件 - "扫描"消息

  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);   }

这就是我完整的 main.dart 文件现在的样子

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'))
          ],
        ),
      ),
    );
  }
}

检查并测试,让我知道这是否适合您,这在我的最后工作正常。打印将在浏览器中生成错误,您需要在安卓或iOS中进行测试。希望这有帮助。

您会收到Cannot access video stream (NotAllowedError)错误,因为您需要授予对 Web 视图的正确权限。

对于安卓,在 AndroidManifest.xml 中,您需要添加以下权限:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />

但这还不够!要请求有关摄像头和麦克风的权限,您可以使用permission_handler插件。

因此,对于 webview,您可以使用我的插件flutter_inappwebview并使用 Android 的 androidOnPermissionRequest 事件,即当 WebView 请求访问指定资源的权限时触发的事件(即 Android 本机 WebChromeClient.onPermissionRequest 事件(。

使用适用于Android的WebRTC的示例:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Permission.camera.request();
  await Permission.microphone.request();
  runApp(MyApp());
}
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}
class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}
class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController _webViewController;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: Container(
            child: Column(children: <Widget>[
              Expanded(
                child: Container(
                  child: InAppWebView(
                      initialUrl: "https://appr.tc/r/158489234",
                      initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          mediaPlaybackRequiresUserGesture: false,
                          debuggingEnabled: true,
                        ),
                      ),
                      onWebViewCreated: (InAppWebViewController controller) {
                        _webViewController = controller;
                      },
                      androidOnPermissionRequest: (InAppWebViewController controller, String origin, List<String> resources) async {
                        return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT);
                      }
                  ),
                ),
              ),
            ]))
    );
  }
}

此示例使用 https://appr.tc/上的聊天室158489234,即基于 WebRTC (https://github.com/webrtc/apprtc( 的视频聊天演示应用程序。要使其正常工作,您需要设置选项mediaPlaybackRequiresUserGesturefalse并实现(适用于Android(onPermissionRequest事件。

最新更新