如何用flutter_custom_tabs插件在Flutter中使用自定义Chrome标签

546 阅读16分钟

移动应用程序开发人员经常需要在他们的应用程序中显示网页。显示网页的最简单方法是在用户的默认网络浏览器中打开特定的网页,但这有一些明显的缺点。

例如,当用户通过你的应用程序在浏览器中打开一个URL时,这个特定的动作会突然切换当前的应用程序上下文并启动浏览器应用程序,所以这对用户并不友好--而且浏览器的UI也是不可定制的。平台特有的webview组件也让开发者渲染网络内容,但webview通常不与其他网络浏览器共享浏览状态,也不包括最新的网络API。

Chromium开源浏览器在2015年通过提供自定义标签功能解决了这个URL导航问题。现在,许多主要的安卓浏览器都实现了自定义标签的协议。

什么是自定义标签页功能?

自定义标签功能让移动应用开发者在一个可定制的浏览器实例中启动网页,该实例与原始浏览器应用共享相同的cookie jar和权限模型。flutter_custom_tabs 包提供了一个跨平台的解决方案,用于在Android上实现Chrome自定义标签,以及在iOS上实现基于Safari视图控制器的类似自定义标签的功能。

在这篇文章中,我们将通过以下几个部分讨论flutter_custom_tabs 在Android平台上提供的每一个功能。

flutter_custom_tabs 特点

flutter_custom_tabs 包可以让开发者在Android的Chrome自定义标签页和iOS的Safari视图控制器上启动一个URL。该软件包包括许多令人印象深刻的功能,我们将在本节中回顾这些功能。

灵活、全功能和最小的API

这个包提供了一个简单的API函数,就像url_launcher包一样,使用Custom Tabs的配置对象非常灵活,几乎涵盖了所有原生的Android Custom Tabs功能。你可以通过库的配置对象轻松配置浏览器工具栏、活动启动器动画和一些浏览器功能。

跨平台支持

Chrome自定义标签是为Android平台引入的功能,但这个包通过原生Safari视图控制器API在iOS平台上提供了类似的功能。flutter_custom_tabs ,甚至可以通过url_launch_package打开一个新的浏览器标签作为基于Web的自定义标签的替代品,与Flutter网络平台一起工作。

错误处理

Chrome自定义标签需要Chrome浏览器、另一个支持自定义标签的浏览器,或者至少是一个浏览器应用来启动网页。当系统中没有浏览器存在时,这个库就无法完成任务。你可以用Dart的try-catch块轻松处理此类错误。

Flutter Chrome自定义标签教程

现在我们知道了flutter_custom_tabs 包的突出特点。让我们把它安装到Flutter项目中并尝试所有支持的功能。

设置flutter_custom_tabs

您可以用一个新的Flutter项目来测试即将到来的代码示例,或者直接在现有项目中使用这些代码示例。

如果您打算创建一个新的项目,请用以下命令创建一个。

flutter create customtabs
cd customtabs

现在我们可以将flutter_custom_tabs 添加到pubspec.yaml 文件的依赖列表中,并通过运行以下命令链接外部包。

flutter pub get flutter_custom_tabs 

确保你的依赖列表包括新链接的包。

dependencies:
  flutter:
    sdk: flutter
  flutter_custom_tabs: ^1.0.4

  # --- Other dependencies ---

接下来,用flutter run 命令运行项目,安装新链接的包,并运行应用程序。

如果插件安装成功,一旦运行flutter run ,你将看到默认的Flutter应用程序。由于Flutter的热重载功能,您可以让应用程序运行并测试即将到来的代码片段。

用基本语法启动一个URL

让我们用Chrome自定义标签启动一个URL,不需要额外的配置就可以开始使用这个包。在lib/main.dart 文件中添加以下代码。

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Tabs Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue
      ),
      home: Home()
    );
  }
}

class Home extends StatelessWidget {
  const Home({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Custom Tabs Demo'),
      ),
      body: Center(
        child: TextButton(
          child: const Text('Open GitHub', style: TextStyle(fontSize: 20)),
          onPressed: () => _launchURL(),
        ),
      ),
    );
  }

  void _launchURL() async {
    try {
      launch('https://github.com');
    }
    catch(e) {
      debugPrint(e.toString());
    }
  }
}

在这里,我们使用flutter_custom_tabs 包中的launch 异步函数,通过自定义标签功能打开一个Chrome实例。我们使用了典型的Dart try-catch错误处理方法来检测Custom Tabs的初始化问题。

一旦你运行上述代码并点击材料文本按钮(我们的是Open GitHub),应用程序将在Custom Tabs中打开GitHub网站,如下图所示。

Using github in flutter custom tabs

默认配置下的自定义标签

如上图预览所示,我不用登录就得到了GitHub的仪表盘,因为我已经通过Chrome浏览器登录了GitHub网站--由于共享浏览器的存储模式(即cookies、设置等),你看到所有Custom Tabs启动的网站都和你在Chrome浏览器中看到的一样。你可以通过点击左侧的关闭按钮快速回到你的应用程序,或者你可以根据你的浏览深度,多次按下返回按钮。

通过Android Intent定制工具栏

即使Custom Tabs使用相同的已安装的Chrome浏览器,它也能让开发者定制主要的UI元素,如工具栏。自定义标签页API通过著名的Android Intent对象将这些配置细节传递给Chrome。launch 函数接受一个可选的配置对象,用于自定义标签页的定制。

例如,我们可以通过toolbarColor 属性来改变工具条的背景颜色。用下面的代码片断更新你的_launchURL 函数源。

void _launchURL() async {
  try {
    launch('https://github.com',
      customTabsOption: CustomTabsOption(
        toolbarColor: Colors.blue
      )
    );
  }
  catch(e) {
    debugPrint(e.toString());
  }
}

现在你会得到一个蓝色的Chrome实例的工具条,如以下预览所示。

creating blue toolbar in flutter custom tabs

带有硬编码工具栏颜色的自定义标签页

在这里,我们为工具栏的颜色硬编码了Colors.blue ,但你总是可以通过context 引用Theme.of() 函数调用来应用你的应用程序主题的主色调,如下图所示。

void _launchURL(BuildContext context) async {
    try {
      launch('https://github.com',
        customTabsOption: CustomTabsOption(
          toolbarColor: Theme.of(context).primaryColor,
        )
      );
    }
    catch(e) {
      debugPrint(e.toString());
    }
  }

然后,你还需要从按下按钮的回调中传递context 参考。

onPressed: () => _launchURL(context)

这里是为自定义标签使用你的主题主色的完整源代码。

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Tabs Demo',
      theme: ThemeData(
        primarySwatch: Colors.teal
      ),
      home: Home()
    );
  }
}

class Home extends StatelessWidget {
  const Home({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Custom Tabs Demo'),
      ),
      body: Center(
        child: TextButton(
          child: const Text('Open GitHub', style: TextStyle(fontSize: 20)),
          onPressed: () => _launchURL(context),
        ),
      ),
    );
  }
  void _launchURL(BuildContext context) async {
    try {
      launch('https://github.com',
        customTabsOption: CustomTabsOption(
          toolbarColor: Theme.of(context).primaryColor,
        )
      );
    }
    catch(e) {
      debugPrint(e.toString());
    }
  }
}

一旦你运行上述代码,你就会看到我们在自定义标签工具栏中选择的茶色主题主色,如以下预览所示。

Using flutter custom tabs theme primary color

Flutter自定义选项卡正在使用应用程序主题的主色调

showPageTitle 属性让您控制工具栏上网页标题的可见性。下面的配置对象要求Custom Tabs显示网页标题。

customTabsOption: CustomTabsOption(
        // --- Other options ---
        // ...
        showPageTitle: true,
      )

现在我们可以看到网页标题,如下所示。

custom chrome tab showing website title

带有网页标题的Flutter Custom Tabs

这个软件包为工具栏相关的定制提供了另外两个配置属性。enableDefaultShareenableUrlBarHiding 属性分别帮助显示或隐藏共享菜单项和滚动时显示/隐藏工具栏。

定制安卓活动的动画

安卓平台通常会在每次活动启动时播放一个系统动画。Chrome自定义标签也默认继承了相同的系统动画。flutter_custom*_*tabs包提供了一种非常灵活的方式,可以通过CustomTabsSystemAnimationCustomTabsAnimation 类来定制Chrome自定义标签的活动动画。

这个包提供了两个内置的活动动画:slideInfade 。你可以用下面的代码片段激活slideIn 预建的动画。

customTabsOption: CustomTabsOption(
          // --- Other options ---
          // ....
          animation: CustomTabsSystemAnimation.slideIn(),
        )

现在你将看到slideIn ,如以下预览所示的动画。

Using the custom chrome slidein

带有slideIn过渡动画的Flutter自定义标签

我们可以通过添加animation: CustomTabsSystemAnimation.fade() 配置来使用fade 动画。

Using the custom fade animation

带有渐变过渡动画的Flutter自定义标签

这个包非常灵活--它可以让你使用任何Android框架的核心动画,而不是预建的库动画。例如,你可以通过使用以下配置对象建立一个自定义动画。

import 'package:flutter_custom_tabs_platform_interface/flutter_custom_tabs_platform_interface.dart'; // Import CustomTabsAnimation
customTabsOption: CustomTabsOption(
          // --- Other options ---
          // ...
          animation: const CustomTabsAnimation(
            startEnter: 'android:anim/screen_rotate_minus_90_enter',
            startExit: 'android:anim/fade_out',
            endEnter: 'android:anim/screen_rotate_minus_90_enter',
            endExit: 'slide_down/fade_out',
          ),
        )

现在你会看到一个不同于之前预建库动画的旋转过渡动画。

Using user-defined rotation transition animation

带有用户定义的旋转过渡动画的Flutter自定义标签

您可以为CustomTabsAnimation 构造函数提供Android框架核心动画标识符,并浏览所有Android框架核心动画的提供。

每个动画构造函数参数都有以下定义。

  • startEnter:为自定义标签输入动画
  • startExit:启动自定义标签的应用程序活动的退出动画
  • endEnter:为启动自定义标签的应用程序输入动画
  • endExit:自定义标签页的退出动画

试着从Android框架的核心动画中制作你自己的过渡动画吧!只要确保在实现你的动画时不对用户体验产生负面或巨大的影响。

高级自定义标签的配置

以上是每个Flutter开发者需要配置Chrome自定义标签的常见功能。flutter_custom_tabs 包还支持一些高级配置,以满足特定需求。例如,如果用户的设备上没有Chrome浏览器,您可以通过extraCustomTabs 阵列优先考虑后备浏览器。

下面的配置是在用户的设备上没有Chrome浏览器的情况下,通过参考安卓包的名称,优先考虑Mozilla Firefox,然后是Microsoft Edge。

customTabsOption: CustomTabsOption(
          extraCustomTabs: const <String>[
            'org.mozilla.firefox',
            'com.microsoft.emmx'
           ],
        )

另外,这个包可以让你通过enableInstantApps 布尔配置属性在自定义标签页中启用和禁用Google PlayInstant Apps。例如,下面的配置激活了自定义标签页上的即时应用程序功能。

>customTabsOption: CustomTabsOption(
          enableInstantApps: true
        )

这个包还可以让你在HTTP请求中发送一些头值,如下图所示。

customTabsOption: CustomTabsOption(
          // --- Other options ---
          // ...
          headers: {
            'content-type': 'text/plain'
          }
        )

注意,这些头值需要是CORS安全列表的请求头--否则,你需要用你的域名配置谷歌数字资产链接来发送任意的请求头。

iOS和网页支持配置

flutter_custom_>tabs包支持安卓、iOS和网络平台。在Android上,它通过底层的CustomTabsLauncher平台特定的Kotlin库使用Chrome或其他后备浏览器。

在iOS上,它通过Flutter方法通道API直接使用系统包含的SFSafariViewController类来打开一个自定义的Safari实例。在网络平台上,它通过url_launcher_web包打开一个新的浏览器标签。

网页版的flutter_custom_tabs是不可定制的,因为我们不能用客户端的JavaScript改变浏览器的UI。但是,我们可以用与自定义标签有点类似的方式来定制iOS上的Safari实例。看看下面这个配置对象。

>customTabsOption: CustomTabsOption(
        // --- Other options ---
        // ...
        safariVCOption: SafariViewControllerOption(
          preferredBarTintColor: Colors.blue,
          preferredControlTintColor: Colors.white,
          barCollapsingEnabled: true,
          entersReaderIfAvailable: true,
          dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,        
        ),
      )

上面的配置设置了以下Safari UI的定制。

  • preferredBarTintColor 属性为工具条背景使用蓝色
  • preferredControlTintColor 属性为工具条元素使用白色
  • 使用barCollapsingEnabled 属性,当用户滚动时,自动折叠工具条
  • 使用entersReaderIfAvailable 属性自动激活读者模式
  • 通过dismissButtonStyle 属性,使用关闭按钮返回到应用程序。

自定义标签页 vs. 默认浏览器 vs. Webview

自定义选项卡并不是在Flutter应用程序上呈现网页内容的唯一方式--我们也有Flutter包来打开默认浏览器和实现应用程序内的webviews。让我们讨论一下启动默认浏览器和使用webview的利弊,将每个选项与Custom Tabs解决方案进行比较。

比较的要点自定义标签默认浏览器网络视图
初始化提供最快的初始化时间,具有原生的Android和Chrome Custom Tabs性能优化功能网页初始化很慢,因为默认网页浏览器没有像Custom Tabs那样使用特定的初始化性能优化。网页初始化可以通过各种性能调整来提升,但webview组件通常不像web浏览器那样支持完整的web API和浏览器功能
可定制性能够根据Flutter应用程序的主题定制UI(工具栏和菜单)。不能定制UI,因为URL启动过程是由Android系统控制的,而不是由开发者明确控制的如果webview位于开发者创建的活动中,则可高度定制。
用户体验/在浏览器和应用程序之间进行导航在应用程序和自定义标签之间有内置的、用户友好的导航支持与Flutter应用程序的用户体验相比,默认浏览器可能会显示为一个完全不同的应用程序,用户通常需要找到关闭浏览器应用程序的方法来再次返回到您的应用程序开发人员可以通过各种 UI 和内部 Web 视图功能提供与应用程序其他本地部分相同的用户体验,因为他们可以根据自己的需要定制 Web 视图
Cookie和权限共享共享cookie罐和权限模型,因此用户可以在Chrome和Custom Tabs实例上看到完全相同的网站状态使用相同的cookie罐和权限模型,因为URL是在系统中同一个默认浏览器中启动的浏览器和webview组件之间都不共享cookie罐和权限模型。用户可能需要从浏览器和webview中登录两次网页,这与自定义标签页不同。
可用性可供Flutter开发人员通过flutter_custom_tabs和flutter_web_browser插件使用。Flutter开发者可通过url_launcher插件获得。通过 url_launcher 插件(非常有限的定制)和 webview_flutter 插件(灵活的定制),Flutter 开发人员可以使用。

让我们根据您的应用开发需求来总结以上的对比表。

如果你只需要显示一个网页,可以考虑使用自定义标签,而不是启动默认浏览器,以获得更好的用户体验。如果你需要用本机-网络、通信桥一样的高级定制来展示你自己的网页内容,那么面向网络视图的方法给了你完全的灵活性和支持。

在使用默认浏览器选项之前,请三思而行--它确实改变了应用程序的上下文,影响了用户体验。

实施自定义标签的最佳实践

开发的最佳实践总是帮助我们建立高质量的软件系统,让用户欣赏并可能推荐给其他用户。正如您已经体验到的,使用flutter_custom*_*tabs包实现自定义标签是非常容易的。即使实现起来并不复杂,我们也需要思考最佳实践,以实现更好的应用质量。

当您在Flutter中使用自定义标签时,请考虑以下最佳实践。

  • 尽量在自定义标签中提供与应用程序相同的外观和感觉--包括与您的应用程序屏幕相似的动画和颜色
  • 始终测试后退选项。Chrome和其他一些流行的浏览器支持自定义标签,但在某些情况下,用户可能不使用Chrome或支持自定义标签的浏览器--或者用户可能没有网络浏览器应用程序。因此,请检查你的应用程序在这种情况下是否会使用默认浏览器或webview实现
  • 遵循DRY编程原则,不要在很多地方重新定义同一个CustomTabsOption 对象--实现一个共享函数来启动所有的自定义标签,就像我们之前创建的_launchURL 函数一样
  • 增加跨平台支持并在真实设备上测试。请确保通过使用safariVCOption 配置属性为iOS配置flutter_custom*_*tabs

看一下下面的完整配置,以了解最后的要点。

void _launchURL(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    launch('https://github.com',
      // Android Custom Tabs config
      customTabsOption: CustomTabsOption(
        showPageTitle: true,
        toolbarColor: theme.primaryColor,
        animation: CustomTabsSystemAnimation.fade(),
        // fallback options
        extraCustomTabs: const <String>[
          'org.mozilla.firefox',
          'com.microsoft.emmx',
        ],
        headers: {
          'content-type': 'text/plain'
        }
      ),
      // iOS Safari View Controller config
      safariVCOption: SafariViewControllerOption(
        preferredBarTintColor: theme.primaryColor,
        preferredControlTintColor: Colors.white,
        barCollapsingEnabled: true,
        entersReaderIfAvailable: false,
        dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
      ),
    );
  }
  catch(e) {
    // Handle the URL launching failure here
    debugPrint(e.toString());
  }
}

您可以从我的GitHub仓库下载完整的Flutter自定义标签项目源代码样本。

总结

在本教程中,我们已经讨论了flutter_custom_tabs提供的几乎所有功能。这个包提供了一个简单的功能,可以在Flutter应用程序中集成自定义标签。尽管它为处理自定义标签提供了一个最小的Dart函数,但它可以让您在Flutter中使用Chrome自定义标签进行几乎所有需要的自定义操作。

然而,flutter_custom_tabs有几个问题需要修复。例如,一些配置选项,如enableDefaultShare ,不能按预期工作。另外,没有选项可以为某个特定的自定义标签添加额外的动作/菜单项,尽管原生的Android API支持自定义标签中的自定义动作/菜单项。我还注意到,自定义标签的关闭动画没有正常工作。由于这些问题,Flutter开发者开始支持一些替代插件,如flutter_web_browser

但是,总的来说,flutter_custom_tabs包仍然是最好的Flutter自定义标签插件,它为开发者提供了一个灵活和功能齐全的界面。它甚至为你提供了类似于Flutter网页和iOS的自定义标签的实现。这个包在Dart包注册表上有很高的人气分数,并且有一个结构良好的代码库--因此,我们可以在生产应用中毫无问题地使用flutter_custom_tabs

包的维护者将很快添加缺少的功能并修复现有的问题,以支持整个Flutter开发者社区。