Flutter自定义标签页插件flutter_custom_tabs的使用

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

Flutter自定义标签页插件flutter_custom_tabs的使用

简介

flutter_custom_tabs 是一个用于在移动应用程序中以自定义标签页的方式启动URL的Flutter插件。它允许您将浏览器提供的Custom Tabs体验添加到您的移动应用程序中。从2.0版本开始,该插件扩展了对启动URL的支持:

  • 启动外部浏览器中的URL。
  • 启动深度链接URL。
平台 支持版本
Android SDK 19+
iOS 11.0+
Web Any

实现方式分别为:

开始使用

添加依赖

首先,在您的pubspec.yaml文件中添加flutter_custom_tabs作为依赖项:

dependencies:
  flutter_custom_tabs: ^2.1.0

重要提示:v2.0.0包含了一些破坏性的更改,请参考migration guide进行更新。

对于Android项目,确保满足以下要求:

  • Android Gradle Plugin v7.4.0及以上版本。
  • Kotlin v1.7.0及以上版本。

配置如下:

// your-project/android/build.gradle
buildscript {
    ext.kotlin_version = '1.7.0' // and above if explicitly depending on Kotlin.

    dependencies {
        classpath 'com.android.tools.build:gradle:7.4.0' // and above.
    }
}

使用方法

您可以像使用url_launcher一样启动一个网页URL,并通过指定选项来自定义外观和行为。

基本用法

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

void _launchURL(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),      
      customTabsOptions: CustomTabsOptions(
        colorSchemes: CustomTabsColorSchemes.defaults(
          toolbarColor: theme.colorScheme.surface,
        ),
        shareState: CustomTabsShareState.on,
        urlBarHidingEnabled: true,
        showTitle: true,
        closeButton: CustomTabsCloseButton(
          icon: CustomTabsCloseButtonIcons.back,
        ),
      ),                    
      safariVCOptions: SafariViewControllerOptions(
        preferredBarTintColor: theme.colorScheme.surface,
        preferredControlTintColor: theme.colorScheme.onSurface,
        barCollapsingEnabled: true,
        dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,        
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

轻量级版本用法

轻量级版本适用于不需要太多自定义的用户:

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

void _launchURL(BuildContext context) async {
    final theme = Theme.of(context);
    try {
      await launchUrl(
        Uri.parse('https://flutter.dev'),
        options: LaunchOptions(
          barColor: theme.colorScheme.surface,
          onBarColor: theme.colorScheme.onSurface,
          barFixingEnabled: false,
        ),
      );
    } catch (e) {
      debugPrint(e.toString());
    }
}

深度链接

支持启动深度链接URL(如果安装了响应深度链接的应用程序,则直接启动它;否则,启动自定义标签页):

Future<void> _launchDeepLinkURL(BuildContext context) async {
  final theme = Theme.of(context);
  await launchUrl(
    Uri.parse('https://www.google.com/maps/@35.6908883,139.7865242,13z'),
    prefersDeepLink: true,
    customTabsOptions: CustomTabsOptions(
      colorSchemes: CustomTabsColorSchemes.defaults(
        toolbarColor: theme.colorScheme.surface,
      ),
    ),
    safariVCOptions: SafariViewControllerOptions(
      preferredBarTintColor: theme.colorScheme.surface,
      preferredControlTintColor: theme.colorScheme.onSurface,
    ),
  );
}

在外部浏览器中启动

默认情况下,如果没有指定平台特定选项,URL将在外部浏览器中启动:

Future<void> _launchInExternalBrowser() async {
  await launchUrl(Uri.parse('https://flutter.dev'));
}

以底部弹出的形式显示

Future<void> _launchURLInBottomSheet(BuildContext context) async {
  final theme = Theme.of(context);
  final mediaQuery = MediaQuery.of(context);    
  await launchUrl(
    Uri.parse('https://flutter.dev'),
    customTabsOptions: CustomTabsOptions.partial(
      configuration: PartialCustomTabsConfiguration(
        initialHeight: mediaQuery.size.height * 0.7,
      ),
      colorSchemes: CustomTabsColorSchemes.defaults(
        toolbarColor: theme.colorScheme.surface,
      ),
    ),
    safariVCOptions: SafariViewControllerOptions.pageSheet(
      configuration: const SheetPresentationControllerConfiguration(
        detents: {
          SheetPresentationControllerDetent.large,
          SheetPresentationControllerDetent.medium,
        },
        prefersScrollingExpandsWhenScrolledToEdge: true,
        prefersGrabberVisible: true,
        prefersEdgeAttachedInCompactHeight: true,
      ),
      preferredBarTintColor: theme.colorScheme.surface,
      preferredControlTintColor: theme.colorScheme.onSurface,
      dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
    ),
  );
}

优先选择默认浏览器而非Chrome

Future<void> _launchURLInDefaultBrowserOnAndroid() async {  
  await launchUrl(
    Uri.parse('https://flutter.dev'),
    customTabsOptions: CustomTabsOptions(
      browser: const CustomTabsBrowserConfiguration(
        prefersDefaultBrowser: true,
      ),
    ),
  );
}

手动关闭Custom Tabs

Future<void> _closeCustomTabsManually() async {
  await closeCustomTabs();
}

完整示例代码

以下是完整的示例代码,展示了如何在一个Flutter应用中使用flutter_custom_tabs插件:

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs_lite.dart' as lite;

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Custom Tabs Example',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.blue,
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.blue,
        brightness: Brightness.dark,
      ),
      themeMode: ThemeMode.system,
      home: Builder(
        builder: (context) => Scaffold(
          appBar: AppBar(
            title: const Text('Flutter Custom Tabs Example'),
          ),
          body: ListView(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            children: <Widget>[
              FilledButton(
                onPressed: () => _launchUrl(context),
                child: Text(
                  Platform.isAndroid
                      ? 'Show flutter.dev (Chrome)'
                      : 'Show flutter.dev',
                ),
              ),
              if (Platform.isAndroid)
                FilledButton(
                  onPressed: () => _launchURLInDefaultBrowserOnAndroid(context),
                  child:
                      const Text('Show flutter.dev (prefer default browser)'),
                ),
              FilledButton(
                onPressed: () => _launchUrlLite(context),
                child: const Text('Show flutter.dev (lite ver)'),
              ),
              FilledButton(
                onPressed: () => _launchDeepLinkURL(context),
                child: const Text('Deep link to platform maps'),
              ),
              FilledButton(
                onPressed: () => _launchUrlInBottomSheet(context),
                child: const Text('Show flutter.dev in bottom sheet'),
              ),
              FilledButton(
                onPressed: () => _launchWithCustomCloseButton(context),
                child: const Text('Show flutter.dev with custom close button'),
              ),
              FilledButton(
                onPressed: () => _launchWithCustomAnimation(context),
                child: const Text('Show flutter.dev with custom animation'),
              ),
              FilledButton(
                onPressed: () => _launchAndCloseManually(context),
                child: const Text('Show flutter.dev + close after 5 seconds'),
              ),
              FilledButton(
                onPressed: () => _launchInExternalBrowser(),
                child: const Text('Show flutter.dev in external browser'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Future<void> _launchUrl(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),
      customTabsOptions: CustomTabsOptions(
        colorSchemes: CustomTabsColorSchemes.defaults(
          toolbarColor: theme.colorScheme.surface,
          navigationBarColor: theme.colorScheme.surface,
        ),
        shareState: CustomTabsShareState.on,
        urlBarHidingEnabled: true,
        showTitle: true,
      ),
      safariVCOptions: SafariViewControllerOptions(
        preferredBarTintColor: theme.colorScheme.surface,
        preferredControlTintColor: theme.colorScheme.onSurface,
        barCollapsingEnabled: true,
        entersReaderIfAvailable: false,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchURLInDefaultBrowserOnAndroid(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),
      customTabsOptions: CustomTabsOptions(
        colorSchemes: CustomTabsColorSchemes.defaults(
          toolbarColor: theme.colorScheme.surface,
          navigationBarColor: theme.colorScheme.surface,
        ),
        urlBarHidingEnabled: true,
        showTitle: true,
        browser: const CustomTabsBrowserConfiguration(
          prefersDefaultBrowser: true,
        ),
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchUrlLite(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    await lite.launchUrl(
      Uri.parse('https://flutter.dev'),
      options: lite.LaunchOptions(
        barColor: theme.colorScheme.surface,
        onBarColor: theme.colorScheme.onSurface,
        barFixingEnabled: false,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchDeepLinkURL(BuildContext context) async {
  final theme = Theme.of(context);
  final uri = Platform.isIOS
      ? 'https://maps.apple.com/?q=tokyo+station'
      : 'https://www.google.co.jp/maps/@35.6908883,139.7865242,13.53z';
  try {
    await launchUrl(
      Uri.parse(uri),
      prefersDeepLink: true,
      customTabsOptions: CustomTabsOptions(
        colorSchemes: CustomTabsColorSchemes.defaults(
          toolbarColor: theme.colorScheme.surface,
          navigationBarColor: theme.colorScheme.surface,
        ),
        urlBarHidingEnabled: true,
        showTitle: true,
      ),
      safariVCOptions: SafariViewControllerOptions(
        preferredBarTintColor: theme.colorScheme.surface,
        preferredControlTintColor: theme.colorScheme.onSurface,
        barCollapsingEnabled: true,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchUrlInBottomSheet(BuildContext context) async {
  final theme = Theme.of(context);
  final mediaQuery = MediaQuery.of(context);
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),
      customTabsOptions: CustomTabsOptions.partial(
        configuration: PartialCustomTabsConfiguration(
          initialHeight: mediaQuery.size.height * 0.7,
        ),
        colorSchemes: CustomTabsColorSchemes.defaults(
          colorScheme: theme.brightness.toColorScheme(),
          toolbarColor: theme.colorScheme.primary,
        ),
        showTitle: true,
      ),
      safariVCOptions: SafariViewControllerOptions.pageSheet(
        configuration: const SheetPresentationControllerConfiguration(
          detents: {
            SheetPresentationControllerDetent.large,
            SheetPresentationControllerDetent.medium,
          },
          prefersScrollingExpandsWhenScrolledToEdge: true,
          prefersGrabberVisible: true,
          prefersEdgeAttachedInCompactHeight: true,
          preferredCornerRadius: 16.0,
        ),
        preferredBarTintColor: theme.colorScheme.primary,
        preferredControlTintColor: theme.colorScheme.onPrimary,
        entersReaderIfAvailable: true,
        dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchWithCustomCloseButton(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),
      customTabsOptions: CustomTabsOptions(
          colorSchemes: CustomTabsColorSchemes.defaults(
            toolbarColor: theme.colorScheme.surface,
          ),
          showTitle: true,
          closeButton: CustomTabsCloseButton(
            icon: CustomTabsCloseButtonIcons.back,
          )),
      safariVCOptions: SafariViewControllerOptions(
        preferredBarTintColor: theme.colorScheme.surface,
        preferredControlTintColor: theme.colorScheme.onSurface,
        dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchWithCustomAnimation(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),
      customTabsOptions: CustomTabsOptions(
        colorSchemes: CustomTabsColorSchemes.defaults(
          toolbarColor: theme.colorScheme.surface,
        ),
        showTitle: true,
        animations: CustomTabsSystemAnimations.fade(),
      ),
      safariVCOptions: SafariViewControllerOptions(
        preferredBarTintColor: theme.colorScheme.surface,
        preferredControlTintColor: theme.colorScheme.onSurface,
        modalPresentationStyle: ViewControllerModalPresentationStyle.automatic,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchAndCloseManually(BuildContext context) async {
  final theme = Theme.of(context);
  try {
    Timer(const Duration(seconds: 5), () {
      closeCustomTabs();
    });

    await launchUrl(
      Uri.parse('https://flutter.dev'),
      customTabsOptions: CustomTabsOptions(
        colorSchemes: CustomTabsColorSchemes.defaults(
          toolbarColor: theme.colorScheme.surface,
        ),
        showTitle: true,
      ),
      safariVCOptions: SafariViewControllerOptions(
        preferredBarTintColor: theme.colorScheme.surface,
        preferredControlTintColor: theme.colorScheme.onSurface,
      ),
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

Future<void> _launchInExternalBrowser() async {
  try {
    await launchUrl(
      Uri.parse('https://flutter.dev'),
      prefersDeepLink: false,
    );
  } catch (e) {
    debugPrint(e.toString());
  }
}

以上是关于flutter_custom_tabs插件的详细介绍和使用方法,希望对您有所帮助!


更多关于Flutter自定义标签页插件flutter_custom_tabs的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义标签页插件flutter_custom_tabs的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用flutter_custom_tabs插件来实现自定义标签页的示例代码。flutter_custom_tabs插件允许你在Flutter应用中打开自定义的Chrome自定义标签页(Custom Tabs),这对于需要在应用内打开网页的场景非常有用。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_custom_tabs: ^1.0.0  # 请确保使用最新版本

然后,运行flutter pub get来安装依赖。

接下来,你可以在你的Flutter项目中使用这个插件。以下是一个简单的示例,展示了如何打开一个自定义标签页:

import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:url_launcher/url_launcher.dart';  // 用于检查URL是否能被打开

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Custom Tabs Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Custom Tabs Demo'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: _launchCustomTab,
          child: Text('Open Custom Tab'),
        ),
      ),
    );
  }

  Future<void> _launchCustomTab() async {
    // 定义要打开的URL
    final url = 'https://flutter.dev';

    // 检查URL是否可以被打开
    if (await canLaunch(url)) {
      // 使用FlutterCustomTabsPlugin打开自定义标签页
      // 这里可以传递一些自定义选项,比如工具栏颜色、动作按钮等
      try {
        await FlutterCustomTabsPlugin.withNewEngine(url)
          .setCustomTabsOptions(
            CustomTabsOptions(
              toolbarColor: Colors.blue.withOpacity(0.9),
              enableUrlBarHiding: true,
              showPageTitle: true,
              // 添加自定义动作按钮(可选)
              actionButton: CustomTabsActionButton(
                icon: BitmapFactory.decodeResource(
                  Resources(),
                  android.R.drawable.ic_menu_share,
                ),
                description: "Share",
                pendingIntent: PendingIntent.getBroadcast(
                  // 你需要在这里提供合适的Context和Intent
                  // 由于这是一个Flutter示例,这里省略了具体实现
                  null, 
                  0, 
                  Intent(), 
                  0
                ),
              ),
              // 更多自定义选项...
            )
          )
          .launch();
      } catch (e) {
        // 处理异常
        print("Failed to launch URL: $e");
      }
    } else {
      // 如果无法打开URL,显示错误消息
      throw UnsupportedError('Could not launch $url');
    }
  }
}

注意

  1. 在上面的代码中,CustomTabsActionButtonPendingIntent等Android特有的实现需要一些Android原生代码的支持,这超出了Flutter的纯粹范围。为了简化示例,这些部分被省略了。在实际应用中,你可能需要在你的Android原生代码中实现这些功能,并通过平台通道与Flutter进行通信。

  2. 由于flutter_custom_tabs插件在iOS上通常使用Safari View Controller,因此许多自定义选项(如工具栏颜色)在iOS上可能不适用。

  3. 确保你的AndroidManifest.xml和iOS配置允许打开外部链接。

  4. 在实际项目中,请根据你的需求调整URL和自定义选项。

希望这个示例能帮助你在Flutter项目中成功使用flutter_custom_tabs插件!

回到顶部