将 UniLinks 与 Flutter 集成(安卓 AppLinks + iOS UniversalLinks)

让我们使用 Flutter Mobile 和 Flutter Web 集成 UniLinks。

一步一步的指导!

我是 Pedro Dionísio,是葡萄牙 InspireIT 公司的 Flutter 开发人员,我写这个 UniLinks 教程的座右铭是:

  1. Firebase DynamicLinks 已被弃用,就像 Firebase 在其文档中所说,不应再实现(我正在使用它,由于它有一些错误并且已被弃用,我决定开始将这种类型的 Deeplink 迁移到 UniLinks);
  2. 这种 Deeplink 方法被 TikTok、Instagram、Facebook 等大公司使用……
  3. 我在某些特定的 Android 设备上实现它时遇到了一些问题(尝试打开并将数据传递给应用程序)。

因此,我将把所有步骤讲得一清二楚,并且解释一切,不仅适用于 Flutter Android 和 iOS,还适用于 Flutter Web 和 Firebase WebHosting,以免错过任何步骤。让我们开始吧!

Deep Linking 介绍

什么是 Deep Linking?

Deep Linking(深层链接)就像有一个指向应用程序某些部分的快捷方式。

这是一种特殊的网络链接,它不仅可以打开您的应用程序,还可以将您带到应用程序内的特定位置。就像打开一本书,直接翻到您想阅读的页面一样。

它是如何工作的?

假设您在应用程序中发现了一篇很棒的文章,并且想与朋友分享。您可以向他们发送一个特殊的链接,将他们直接带到该文章,而不是将他们发送到应用程序的主页并要求他们查找该文章。这就像给他们送了一条秘密通道。

最酷的部分是什么?

最酷的是,您还可以通过此链接发送特殊说明或代码。例如,如果应用程序中有折扣码或隐藏的惊喜,您可以将其包含在链接中。所以,你不仅能很快到达正确的地方,还能得到一些额外的好处。

如果应用程序已经打开会发生什么?

有时,当您单击深层链接时,您的应用程序可能已经打开。不用担心!当应用程序已经运行时,深度链接甚至可以工作。这就像切换到您正在阅读的书中的正确页面。

关于 UniLinks 的一些最后说明

在本教程中,我将向您展示如何使用名为“uni_links”的工具使深度链接变得超级简单。

重要的是,在这种类型的深层链接中,必须在网站中分配 2 个配置文件(一个用于 Android,一个用于 iOS)。其含义是因为这些文件存储有关您的应用程序的重要信息,并且通过它们,您的网络浏览器可以准确地知道在手机内重定向到哪里。

说到这里,我将向您展示如何创建 Flutter Web 项目并将这些文件放置在正确的位置。

完全不用担心!这将很容易实施!让我们开始吧!📱🚀

为您的移动应用创建 Flutter 项目

Android 配置

转到项目的 android/app/src/main/AndroidManifest.xml 文件。

在这里,我们需要更改一些内容,首先将 android:launchMode="singleTop" 替换为 android:launchMode="singleTask" ,因为我们只希望在手机中打开 APP 的一个实例。

应该会出现这样的内容:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"><application ...><activityandroid:name=".MainActivity"android:exported="true"android:launchMode="singleTask" <!-- <----HERE---- -->...>

之后,在同一个文件中,您需要配置您的“APP 入口”,该入口将通过特定的 UniLink 进行。

例如我们希望通过这个链接打开 APP: https://mypage.web.app/promos/?promo-id=ABC1 。

因此,在 activity 内,您将添加一个 intent-filter ,如下所示:

<manifest ...><application ...><activity ...>...<!-- App Links --><intent-filter android:autoVerify="true"><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><dataandroid:scheme="https"android:host="mypage.web.app"android:pathPrefix="/promos/" /></intent-filter>...</activity></application>
</manifest>

iOS 配置

使用相同的示例,我们希望通过此链接打开应用程序: https://mypage.web.app/promos/?promo-id=ABC1 。

转到项目的 ios/Runner/Runner.entitlements 文件并添加以下 keyarray 标记:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>...<key>com.apple.developer.associated-domains</key><array><string>applinks:mypage.web.app</string></array>...
</dict>
</plist>

您不需要这样做,但如果您愿意,您也可以通过 XCode 进行此配置:

  • 双击 ios/Runner.xcworkspace 文件打开 Xcode;
  • 转到项目导航器 (Cmd+1) 并选择最顶部的 Runner 根项目;
  • 选择 Runner 目标,然后选择 Signing & Capabilities 选项卡;
  • 单击 + Capability (加号)按钮添加新功能;
  • 输入 associated domains 并选择该项目;
  • 双击域列表中的第一项,将其从 webcredentials:example.com 更改为:applinks:mypage.web.app
  • 将创建一个名为 Runner.entitlements 的文件并将其添加到项目中。

Flutter 实现

我通常使用模块化的方法来组织一切,但对于这个示例项目,我将进行混合,使一切变得简单直观。

让我们首先在此处获取最新版本的 uni_links 包:https://pub.dev/packages/uni_links 并将其粘贴到项目的 pubspec.yaml 文件中,如下所示:

---
dependencies:flutter:sdk: fluttercupertino_icons: ^1.0.2uni_links: ^0.5.1 # <----------------

保存并执行 flutter pun get 以更新您的项目依赖项。

然后添加三个用户界面文件: 主屏幕、绿色宣传屏幕和红色宣传屏幕。

主屏幕文件 lib/screens/home_screen.dart

import 'package:flutter/material.dart';class HomeScreen extends StatelessWidget {const HomeScreen({super.key});Widget build(BuildContext context) {return Scaffold(body: Container(alignment: Alignment.center,child: const Text("Home Screen",style: TextStyle(fontSize: 24,fontWeight: FontWeight.bold,),),),);}
}

绿色促销屏幕文件 lib/screens/green_promo_screen.dart

import 'package:flutter/material.dart';
import 'package:unilinkproject/common/uni_links/core/services/uni_links_service.dart';class GreenPromoScreen extends StatelessWidget {const GreenPromoScreen({super.key});Widget build(BuildContext context) {return Scaffold(body: Container(alignment: Alignment.center,decoration: const BoxDecoration(gradient: LinearGradient(colors: [Colors.green,Colors.greenAccent,],begin: Alignment.topRight,end: Alignment.bottomLeft,),),child: Text("!!! Green Promo !!!\nCode: ${UniLinksService.promoId}",textAlign: TextAlign.center,style: const TextStyle(fontSize: 24,fontWeight: FontWeight.bold,),),),);}
}

红色促销屏幕 lib/screens/red_promo_screen.dart

import 'package:flutter/material.dart';
import 'package:unilinkproject/common/uni_links/core/services/uni_links_service.dart';class RedPromoScreen extends StatelessWidget {const RedPromoScreen({super.key});Widget build(BuildContext context) {return Scaffold(body: Container(alignment: Alignment.center,decoration: const BoxDecoration(gradient: LinearGradient(colors: [Colors.red,Colors.redAccent,],begin: Alignment.topRight,end: Alignment.bottomLeft,),),child: Text("!!! Red Promo !!!\nCode: ${UniLinksService.promoId}",textAlign: TextAlign.center,style: const TextStyle(fontSize: 24,fontWeight: FontWeight.bold,),),),);}
}

为什么是 3 个屏幕?这是因为我们要测试 3 种情况:

  • APP 正常打开时显示主屏幕;
  • 当我们收到 Unilink https://mypage.web.app/promos/?promo-id=ABC1 时,会显示绿色促销屏幕;
  • 当我们收到 UniLink https://mypage.web.app/promos/?promo-id=ABC2 时,会显示红色促销屏幕。

现在让我们添加一个我在项目中经常使用的重要实用程序文件。有了它我们就可以在 APP 的任何地方访问最新的 BuildContext

添加此文件 lib/common/global_context/utils/contect_utility.dart

import 'package:flutter/material.dart';class ContextUtility {static final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(debugLabel: 'ContextUtilityNavigatorKey');static GlobalKey<NavigatorState> get navigatorKey => _navigatorKey;static bool get hasNavigator => navigatorKey.currentState != null;static NavigatorState? get navigator => navigatorKey.currentState;static bool get hasContext => navigator?.overlay?.context != null;static BuildContext? get context => navigator?.overlay?.context;
}

接下来我们添加负责处理 UniLinks lib/common/global_context/utils/context_utility.dart 的文件:

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:uni_links/uni_links.dart';
import 'package:unilinkproject/common/global_context/utils/context_utility.dart';
import 'package:unilinkproject/screens/green_promo_screen.dart';
import 'package:unilinkproject/screens/red_promo_screen.dart';class UniLinksService {static String _promoId = '';static String get promoId => _promoId;static bool get hasPromoId => _promoId.isNotEmpty;static void reset() => _promoId = '';static Future<void> init({checkActualVersion = false}) async {// 这用于以下情况:应用程序未运行,用户单击链接。try {final Uri? uri = await getInitialUri();_uniLinkHandler(uri: uri);} on PlatformException {if (kDebugMode) print("(PlatformException) Failed to receive initial uri.");} on FormatException catch (error) {if (kDebugMode) print("(FormatException) Malformed Initial URI received. Error: $error");}// 这用于以下情况:应用程序已经在运行,用户单击链接。uriLinkStream.listen((Uri? uri) async {_uniLinkHandler(uri: uri);}, onError: (error) {if (kDebugMode) print('UniLinks onUriLink error: $error');});}static Future<void> _uniLinkHandler({required Uri? uri}) async {if (uri == null || uri.queryParameters.isEmpty) return;Map<String, String> params = uri.queryParameters;String receivedPromoId = params['promo-id'] ?? '';if (receivedPromoId.isEmpty) return;_promoId = receivedPromoId;if (_promoId == 'ABC1') {ContextUtility.navigator?.push(MaterialPageRoute(builder: (_) => const GreenPromoScreen()),);}if (_promoId == 'ABC2') {ContextUtility.navigator?.push(MaterialPageRoute(builder: (_) => const RedPromoScreen()),);}}
}

最后我们将 main.dart 文件更改为:

import 'package:flutter/material.dart';
import 'package:unilinkproject/common/uni_links/core/services/uni_links_service.dart';
import 'package:unilinkproject/common/global_context/utils/context_utility.dart';
import 'package:unilinkproject/screens/green_promo_screen.dart';
import 'package:unilinkproject/screens/home_screen.dart';
import 'package:unilinkproject/screens/red_promo_screen.dart';void main() async {WidgetsFlutterBinding.ensureInitialized();await UniLinksService.init();runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(navigatorKey: ContextUtility.navigatorKey,debugShowCheckedModeBanner: false,title: 'UniLinks Project',routes: {'/': (_) => const HomeScreen(),'/green-promo': (_) => const GreenPromoScreen(),'/red-promo': (_) => const RedPromoScreen(),},);}
}

我们就完成了!

您可以测试正常打开 APP,查看是否出现主屏幕。


原文:https://medium.com/@pedrostick3/integrate-unilinks-with-flutter-android-applinks-ios-universallinks-c9a1542d6625

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习笔记 - 感知器的数学表达

一、假设前提 感知机(或称感知器,Perceptron)是Frank Rosenblatt在1957年就职于Cornell航空实验室(Cornell Aeronautical Laboratory)时所发明的一种人工神经网络。 它可以被视为一种最简单形式的前馈神经网络,是一种二元线性分类模型,其输入为实例的特征向量,输出为实…

同为科技(TOWE)自动断电倒计时定时桌面PDU插排

在每个家庭中&#xff0c;插排插座都是必不可少的电源设备。随着各种电器的普及应用和生活节奏的加快&#xff0c;人们对插排也有着多样化的需求&#xff0c;比如在插排中加入定时开关、自动断电、断电记忆、倒计时等等功能&#xff0c;让原本不支持智能家居的用电器秒变智能。…

Mysql高级——Mysql8一主一从,多主多从搭建

修改 /etc/hosts文件 ip地址 master1 ip地址 master2 ip地址 slave1 ip地址 slave2一主一从 create database master1db;create table master1db.master1tab(name char(50));insert into master1db.master1tab VALUES(1111);insert into master1db.master1tab VALUES(2222);m…

Linux命令--mkdir创建目录的方法

原文网址&#xff1a;Linux命令--mkdir创建目录的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Linux创建目录命令--mkdir的用法。 格式 mkdir [选项] 目录… -m, –mode模式&#xff0c;设定权限<模式> (类似 chmod)&#xff0c;而不是 rwxrwxrwx 减 umask-p, --p…

按键开发环境搭建

雷电模拟器 创建虚拟机 2.设置root权限 打开按键精灵连接虚拟机 开启悬浮 mumu模拟器操作 查找端口方法 adb connect 127.0.0.1:16416 设置-应用-所有应用-按键精灵-开启悬浮 步骤二&#xff1a;开启root 处理未root&#xff1a;中途如果有如下未root的情况&#x…

基于SSM的网吧计费管理系统(有报告)。Javaee项目,ssm项目。

演示视频&#xff1a; 基于SSM的网吧计费管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通…

Monarch Mixer:一种性能比Transformer更强的网络架构

六年前&#xff0c;谷歌团队在arXiv上发表了革命性的论文《Attention is all you need》。作为一种优势的机器学习网络架构&#xff0c;Transformer技术迅速席卷全球。Transformer一直是现代基础模型背后的主力架构&#xff0c;并且在不同的应用程序中取得了令人印象深刻的成功…

OCS2工具箱

实时系统优化控制工具箱 参考视频&#xff1a;ETH 最优控制/MPC 实时求解器 OCS2 使用入门 参考文档&#xff1a;OCS2 求解器入门 选择OCS2 OCS2 是一个 MPC 实时求解器 (SLQ/iLQR)&#xff0c;依赖 Pinocchio 构建机器人动力学模型&#xff0c;采用 RViz 或者 RaiSim 验证 (…

[LeetCode] 2.两数相加

一、题目描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个…

【前端笔记】ant-design-vue 3.x使用modal.method()自定义content内容小记

在一次编写业务代码时&#xff0c;碰到了一种既想要Modal.success样式&#xff0c;有想要定制其content内容的情况。 大部分情况下&#xff0c;使用Modal.method()这种方式时&#xff0c;可能content内容固定都是字符串&#xff0c;那如果想要做更高级的交互怎么办&#xff1f…

1.计算机系统概述

目录 一. 计算机的发展 二. 计算机硬件的基本组成 三. 各个硬件的工作原理 &#xff08;1&#xff09;主存储器 &#xff08;2&#xff09;运算器 &#xff08;3&#xff09;控制器 &#xff08;4&#xff09;一个例子 四. 计算机系统的层次结构 五. 计算机的性能指标…

【Mysql】Mysql中表连接的原理

连接简介 在实际工作中&#xff0c;我们需要查询的数据很可能不是放在一张表中&#xff0c;而是需要同时从多张表中获取。下面我们以简单的两张表为例来进行说明。 连接的本质 为方便测试说明&#xff0c;&#xff0c;先创建两个简单的表并给它们填充一点数据&#xff1a; …

SpringBoot项目从resources目录读取文件

SpringBoot 从 resources 读取文件 使用 Spring 给我们提供的工具类来进行读取 File file org.springframework.util.ResourceUtils.getFile("classpath:人物模板.docx");可能读取失败&#xff0c;出现如下错误&#xff1a; java.io.FileNotFoundException: clas…

C语言实现九九乘法表

学习C语言后&#xff0c;我们会发现打印九九乘法表是很简单的&#xff0c;话不多说&#xff0c;我们上代码&#xff01; 目录 1.函数代码 2.运行结果 1.函数代码 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int i 1;int j 1;int n 0;//行数in…

Instant-NGP论文笔记

文章目录 论文笔记 论文笔记 instant-ngp的nerf模型与vanilla nerf的模型架构相同。 instant-ngp的nerf模型包含两个MLP&#xff0c;第一个MLP就两个全连接&#xff0c;输入维度是32&#xff08;16层分辨率x2&#xff09;&#xff0c;输出是16&#xff08;用于预测密度&#x…

【LeetCode每日一题合集】2023.9.18-2023.9.24(⭐拓扑排序⭐设计数据结构:LRU缓存实现 LinkedHashMap⭐)

文章目录 337. 打家劫舍 III&#xff08;树形DP&#xff09;2560. 打家劫舍 IV&#xff08;二分查找动态规划&#xff09;LCP 06. 拿硬币&#xff08;简单贪心模拟&#xff09;2603. 收集树中金币⭐思路——拓扑排序删边 2591. 将钱分给最多的儿童&#xff08;分类讨论&#xf…

记一次pdjs时安装glob出现,npm ERR! code ETARGET和npm ERR! code ELIFECYCLE

如往常一样&#xff0c;我使用pdjs来编译proto文件&#xff0c;但出现了以下报错&#xff1a; 大致就是pdjs的util在尝试执行npm install glob^7.2.1 escodegen^1.13.0时出错了 尝试手动执行安装&#xff0c;escodegen被正确安装&#xff0c;但glob^7.2.1出错 npm ERR! code E…

Redis那些事儿(三)

文章目录 1. 前言2. 常用api介绍3. 需求假设&#xff08;获取离我最近的停车场&#xff09;4. 代码示例 1. 前言 接着上一篇Redis那些事儿&#xff08;二&#xff09; &#xff0c;这一篇主要介绍Redis基于Geo数据结构实现的地理服务&#xff0c;它提供了一种方便的方式来存储和…

库房管理软件采购申请流程代码实现解析

300rmb掏了个javavue2的小系统&#xff0c;学习代码&#xff0c;调整下申请流程。 原有的入库流程是&#xff0c;库管&#xff08;admin&#xff09;提出采购申请给采购员&#xff08;caigou&#xff09;&#xff0c;采购员采购入库时点击入库完成采购入库流程。 想弄清后端代…

非关系型数据库Redis的安装【Linux】及常用命令

前言 Redis&#xff08;Remote Dictionary Server&#xff09;是一种开源的内存数据库管理系统&#xff0c;它以键值存储方式来存储数据&#xff0c;并且支持多种数据结构&#xff0c;如字符串、哈希、列表、集合、有序集合等。Redis最初由Salvatore Sanfilippo开发&#xff0c…