Flutter应用链接处理插件app_links的使用

发布于 1周前 作者 vueper 来自 Flutter

Flutter应用链接处理插件app_links的使用

app_links简介

app_links 是一个用于处理Android App Links、Deep Links、iOS Universal Links以及自定义URL方案(包括Linux、macOS和Windows桌面平台)的Flutter插件。它允许你的应用程序从以下方式打开:

  • HTTPS URL,而不是在浏览器中打开。
  • 自定义方案。

开始使用

在使用插件之前,需要为每个目标平台进行设置。所有配置都可以在示例项目中找到。具体配置文档如下:

对于Web平台,无需特别设置,仅提供初始链接。

使用AppLinks

确保在应用程序早期实例化 AppLinks 以捕获冷启动时的第一个链接。

final appLinks = AppLinks(); // AppLinks是单例

// 订阅所有事件(初始链接和后续链接)
final sub = appLinks.uriLinkStream.listen((uri) {
    // 执行某些操作(导航等)
});

特性矩阵

Feature Android iOS web Windows macOS linux
web (https://) ✔️ ✔️ ✔️* ✔️ ✔️ ?
custom scheme (foo://) ✔️ ✔️ ✔️ ✔️ ✔️
  • : Web平台仅提供首次调用。主要是为了简化特定设置的需求。

示例代码

以下是完整的示例代码,展示了如何使用 app_links 插件来处理深度链接和自定义URL方案。

import 'dart:async';

import 'package:app_links/app_links.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

const kWindowsScheme = 'sample';

void main() {
  // 注册我们的协议仅在Windows平台上
  // registerProtocolHandler(kWindowsScheme);

  runApp(const MyApp());
}

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

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

class _MyAppState extends State<MyApp> {
  final _navigatorKey = GlobalKey<NavigatorState>();
  late AppLinks _appLinks;
  StreamSubscription<Uri>? _linkSubscription;

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

    initDeepLinks();
  }

  @override
  void dispose() {
    _linkSubscription?.cancel();

    super.dispose();
  }

  Future<void> initDeepLinks() async {
    _appLinks = AppLinks();

    // 处理链接
    _linkSubscription = _appLinks.uriLinkStream.listen((uri) {
      debugPrint('onAppLink: $uri');
      openAppLink(uri);
    });
  }

  void openAppLink(Uri uri) {
    _navigatorKey.currentState?.pushNamed(uri.fragment);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: _navigatorKey,
      initialRoute: "/",
      onGenerateRoute: (RouteSettings settings) {
        Widget routeWidget = defaultScreen();

        // 模拟Web路由
        final routeName = settings.name;
        if (routeName != null) {
          if (routeName.startsWith('/book/')) {
            // 导航到 /book/:id
            routeWidget = customScreen(
              routeName.substring(routeName.indexOf('/book/')),
            );
          } else if (routeName == '/book') {
            // 导航到 /book 无其他参数
            routeWidget = customScreen("None");
          }
        }

        return MaterialPageRoute(
          builder: (context) => routeWidget,
          settings: settings,
          fullscreenDialog: true,
        );
      },
    );
  }

  Widget defaultScreen() {
    return Scaffold(
      appBar: AppBar(title: const Text('Default Screen')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const SelectableText('''
            Launch an intent to get to the second screen.

            On web:
            http://localhost:<port>/#/book/1 for example.

            On windows & macOS, open your browser:
            sample://foo/#/book/hello-deep-linking

            This example code triggers new page from URL fragment.
            '''),
            const SizedBox(height: 20),
            buildWindowsUnregisterBtn(),
          ],
        ),
      ),
    );
  }

  Widget customScreen(String bookId) {
    return Scaffold(
      appBar: AppBar(title: const Text('Second Screen')),
      body: Center(child: Text('Opened with parameter: $bookId')),
    );
  }

  Widget buildWindowsUnregisterBtn() {
    if (defaultTargetPlatform == TargetPlatform.windows) {
      return TextButton(
          onPressed: () => unregisterProtocolHandler(kWindowsScheme),
          child: const Text('Remove Windows protocol registration'));
    }

    return const SizedBox.shrink();
  }
}

注意事项

  • 确保按照各平台的配置文档完成必要的设置。
  • 在测试时,可以通过不同的方式触发链接,例如:
    • 在Android模拟器上使用ADB命令:adb shell am start -a android.intent.action.VIEW -d "sample://open.my.app/#/book/hello-world"
    • 在Windows和macOS上,通过浏览器打开:sample://foo/#/book/hello-world2
    • 在Windows上,通过邮件或其他应用打开:https://example.com/#/book/hello-world2

希望这个示例能帮助你更好地理解和使用 app_links 插件!


更多关于Flutter应用链接处理插件app_links的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter应用链接处理插件app_links的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在处理Flutter应用中的链接(deep linking)时,app_links 插件(虽然严格来说没有一个官方名为 app_links 的Flutter插件,但这里我们可以理解为处理应用链接的通用方法,通常涉及到 flutter_universal_linksurl_launcher 等插件)是非常有用的。下面我将展示如何使用 flutter_universal_links 插件来处理应用链接。

首先,你需要在你的 pubspec.yaml 文件中添加 flutter_universal_links 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_universal_links: ^0.5.0  # 请检查最新版本号

然后运行 flutter pub get 来获取依赖。

接下来,你需要进行一些平台特定的配置:

iOS 配置

  1. 在你的 ios/Runner/Info.plist 文件中添加以下内容来配置你的应用支持的 URL schemes:
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourappscheme</string>
        </array>
    </dict>
</array>
  1. 确保你的应用有权限处理 universal links。在 Xcode 中,选择你的项目目标,然后在 “Signing & Capabilities” 标签页下,添加 “Associated Domains” 能力。

  2. 在你的应用的 Apple Developer 账号中,为你的应用配置一个关联的域名,并在该域名的服务器上配置一个 apple-app-site-association 文件,指向你的应用 ID 和路径。

Android 配置

  1. 在你的 android/app/src/main/AndroidManifest.xml 文件中添加以下内容来配置你的应用支持的 Intent Filters:
<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="yourappscheme" android:host="*" />
</intent-filter>
  1. 确保你的应用有权限处理 Android App Links。在你的应用的网站上配置一个 assetlinks.json 文件,指向你的应用包名和签名哈希。

Flutter 代码实现

现在,你可以在 Flutter 中使用 flutter_universal_links 插件来处理接收到的链接。以下是一个简单的示例:

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    _initUniversalLinks();
  }

  Future<void> _initUniversalLinks() async {
    final FlutterUniversalLinks flutterUniversalLinks = FlutterUniversalLinks();

    // 监听传入的链接
    flutterUniversalLinks.getInitialLink().then((initialLink) {
      if (initialLink != null) {
        _handleIncomingLink(initialLink);
      }
    }).catchError((onError) {
      print("getInitialLink error: $onError");
    });

    flutterUniversalLinks.onLink.listen((onLink) {
      _handleIncomingLink(onLink);
    }).onError((onError) {
      print("onLink error: $onError");
    });
  }

  void _handleIncomingLink(String link) {
    print("Received link: $link");
    // 在这里处理接收到的链接
    // 例如,根据链接导航到应用内的特定页面
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Universal Links Example'),
        ),
        body: Center(
          child: Text('Open a link from your associated domain to see it handled here.'),
        ),
      ),
    );
  }
}

这个示例展示了如何初始化 flutter_universal_links 插件,并监听传入的链接。当应用接收到一个链接时,它会打印出链接,并可以在 _handleIncomingLink 方法中根据需要进行处理。

请注意,上述代码和配置步骤可能会随着插件版本和Flutter框架的更新而有所变化,因此请务必查阅最新的文档和示例。

回到顶部