You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中通过QR Code扫描判断APP安装状态并实现跳转

实现基于Flutter的QR Code扫描跳转方案

一、已安装APP时:内置扫描器+内置浏览器实现

1. 依赖选择

使用qr_code_scanner实现QR码扫描,webview_flutter实现内置浏览器,二者都是Flutter生态中稳定常用的包。

2. 核心实现步骤

  • 集成QR扫描组件,监听扫描结果并解析URL
  • 用WebView加载解析后的URL,替代系统浏览器跳转

3. 示例代码

首先在pubspec.yaml中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  qr_code_scanner: ^1.0.1
  webview_flutter: ^4.4.0

然后实现扫描页面与WebView页面:

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

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'QR Scanner Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const QRScannerPage(),
    );
  }
}

class QRScannerPage extends StatefulWidget {
  const QRScannerPage({super.key});

  @override
  State<QRScannerPage> createState() => _QRScannerPageState();
}

class _QRScannerPageState extends State<QRScannerPage> {
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  QRViewController? controller;
  bool _isScanned = false;

  @override
  void reassemble() {
    super.reassemble();
    controller?.pauseCamera();
    controller?.resumeCamera();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('内置QR扫描器')),
      body: _isScanned 
          ? const SizedBox.shrink()
          : QRView(
              key: qrKey,
              onQRViewCreated: _onQRViewCreated,
            ),
    );
  }

  void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen((scanData) {
      if (!_isScanned) {
        _isScanned = true;
        controller.pauseCamera();
        final url = scanData.code;
        if (url != null && Uri.tryParse(url) != null) {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => WebViewPage(url: url)),
          ).then((_) {
            _isScanned = false;
            controller.resumeCamera();
          });
        } else {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('扫描到的内容不是有效URL')),
          );
          _isScanned = false;
          controller.resumeCamera();
        }
      }
    });
  }

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

class WebViewPage extends StatefulWidget {
  final String url;
  const WebViewPage({super.key, required this.url});

  @override
  State<WebViewPage> createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  late WebViewController webViewController;

  @override
  void initState() {
    super.initState();
    webViewController = WebViewController()
      ..loadRequest(Uri.parse(widget.url));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('内置浏览器')),
      body: WebViewWidget(controller: webViewController),
    );
  }
}

二、未安装APP时:QR码引导下载方案

1. 核心思路

生成包含**深度链接(Deeplink)**的QR码,用户用系统工具扫描后:

  • 若APP已安装,系统唤起APP并传递目标URL参数
  • 若未安装,跳转到预设的引导页面(如官网,放置Play/App Store下载按钮)

Android配置

在android/app/src/main/AndroidManifest.xml的<activity>标签内添加:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
        android:scheme="yourapp"
        android:host="scanner" />
</intent-filter>

iOS配置

在ios/Runner/Info.plist中添加:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourapp</string>
        </array>
        <key>CFBundleURLName</key>
        <string>scanner</string>
    </dict>
</array>

3. QR码内容格式

使用类似以下格式的链接:

yourapp://scanner?url=https://example.com

同时需在服务器端配置跳转逻辑:访问该链接时优先尝试唤起APP,失败则跳转至下载引导页面。

使用uni_links包监听Deeplink,唤起APP后直接打开目标URL:
先添加依赖:

uni_links: ^0.5.1

修改MyApp为StatefulWidget并添加监听逻辑:

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Uri? _initialUri;

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

  Future<void> _initUniLinks() async {
    try {
      final initialUri = await getInitialUri();
      if (initialUri != null) _handleUri(initialUri);
      uriLinkStream.listen(_handleUri, onError: (err) {});
    } catch (e) {}
  }

  void _handleUri(Uri uri) {
    final targetUrl = uri.queryParameters['url'];
    if (targetUrl != null && Uri.tryParse(targetUrl) != null) {
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => WebViewPage(url: targetUrl)),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'QR Scanner Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const QRScannerPage(),
    );
  }
}

三、注意事项

  • Android测试Deeplink可使用命令:adb shell am start -W -a android.intent.action.VIEW -d "yourapp://scanner?url=https://example.com" com.your.package.name
  • iOS可通过Xcode Scheme测试或在Safari中输入链接
  • 内置WebView需配置网络权限:Android在Manifest中添加<uses-permission android:name="android.permission.INTERNET" />,iOS若访问HTTP链接需在Info.plist中配置NSAppTransportSecurity

内容的提问来源于stack exchange,提问作者user2297679

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:54:39