flutter_custom_tabs
pluginMobile application developers often need to display webpages within their applications. The simplest way to show a webpage is to open the particular webpage in the user’s default web browser, but this has some obvious drawbacks.
For example, when the user opens a URL in a browser via your application, the particular action suddenly switches the current application context and launches the browser application, so it’s not user-friendly — and the browser UI is not customizable, either. Platform-specific webview components also let developers render web content, but webviews typically don’t share browsing states with the other web browsers and don’t include the latest web APIs.
The Chromium open-source browser solved this URL navigation problem by offering the Custom Tabs feature in 2015. Now, many major Android browsers implement the Custom Tabs protocol.
The Custom Tabs feature lets mobile app developers launch a webpage in a customizable browser instance that shares the same cookie jar and permissions model with the original browser application. The flutter_custom_tabs
package offers a cross-platform solution for implementing Chrome Custom Tabs on Android and a Safari View Controller-based, Custom Tabs-like feature on iOS.
In this post, we will discuss every feature that flutter_custom_tabs
offers on the Android platform via the following sections:
flutter_custom_tab
featuresflutter_custom_tabs
packageflutter_custom_tabs
featuresThe flutter_custom_tabs
package lets developers launch a URL on Chrome Custom Tabs on Android and Safari View Controller on iOS. The package includes many impressive features, which we’ll review in this section.
This package offers a simple API function like the url_launcher package to use Custom Tabs with a very flexible configuration object that covers almost all native Android Custom Tabs features. You can easily configure the browser toolbar, activity launcher animations, and some browser features via the library configuration object.
Chrome Custom Tabs is a feature introduced for the Android platform, but this package offers a similar feature on the iOS platform via the native Safari View Controller API. flutter_custom_tabs
even works with the Flutter web platform by opening a new browser tab as a web-based Custom Tabs alternative via the url_launch_package.
Chrome Custom Tabs requires the Chrome browser, another Custom Tabs-supported browser, or at least a browser app to launch a webpage. When there is no browser present in the system, this library cannot accomplish its task. You can handle such errors easily with Dart try-catch blocks.
Now we know the flutter_custom_tabs
package’s highlighted features. Let’s install it into a Flutter project and try all supported features.
flutter_custom_tabs
packageYou can either test the upcoming code examples with a new Flutter project or use the code examples directly in an existing project.
If you plan to create a new project, create one with the following command:
flutter create customtabs cd customtabs
Now we can add flutter_custom_tabs
to the dependencies list of the pubspec.yaml
file and link the external package by running the following command:
flutter pub get flutter_custom_tabs
Make sure that your dependencies list includes the newly linked package:
dependencies: flutter: sdk: flutter flutter_custom_tabs: ^1.0.4 # --- Other dependencies ---
Next, run the project with the flutter run
command to install the newly linked package and run the app.
You will see the default Flutter app once you run flutter run
if the plugin installation was successful. You can leave the application running and test the upcoming code snippets, thanks to Flutter’s hot reloading feature.
Let’s launch a URL with Chrome Custom Tabs with no extra configuration to get started with this package. Add the following code to the lib/main.dart
file.
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()); } } }
Here, we used the launch
async function from the flutter_custom_tabs
package to open a Chrome instance via the Custom Tabs feature. We used a typical Dart try-catch error-handling approach to detect Custom Tabs initialization issues.
Once you run the above code and click on the Material text button (ours reads Open GitHub), the app will open the GitHub website in Custom Tabs, as shown below.
As shown in the above preview, I got the GitHub dashboard without signing in, since I already signed in to the GitHub website via Chrome  —  you will see all Custom-Tabs-launched websites the same as you see them in your Chrome browser because of the shared browser storage model (i.e., cookies, settings, etc.). You can go back to your app quickly with a single tap on the left side close button, or you can press the back button several times, depending on your browsing depth.
Even though Custom Tabs use the same installed Chrome browser, it lets developers customize the primary UI elements, such as the toolbar. The Custom Tabs API passes these configuration details to Chrome via the well-known Android Intent object. The launch
function accepts an optional configuration object for Custom Tabs customization.
For example, we can change the toolbar background color with the toolbarColor
property. Update your _launchURL
function source with the following code snippet:
void _launchURL() async { try { launch('https://github.com', customTabsOption: CustomTabsOption( toolbarColor: Colors.blue ) ); } catch(e) { debugPrint(e.toString()); } }
Now you will get a blue-color toolbar for the Chrome instance, as shown in the following preview:
Here, we hard-coded the Colors.blue
value for the toolbar color, but you can always apply your app theme’s primary color from the context
reference with the Theme.of()
function call, as shown below:
void _launchURL(BuildContext context) async { try { launch('https://github.com', customTabsOption: CustomTabsOption( toolbarColor: Theme.of(context).primaryColor, ) ); } catch(e) { debugPrint(e.toString()); } }
Then, you also need to pass the context
reference from the button press callback:
onPressed: () => _launchURL(context)
Here is the complete source code for using your theme’s primary color for Custom Tabs:
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()); } } }
Once you run the above code, you will see the teal primary theme color we chose in the Custom Tab toolbar, as shown in the following preview:
The showPageTitle
property lets you control the visibility of the webpage title on the toolbar. The following configuration object asks Custom Tabs to show the webpage title:
customTabsOption: CustomTabsOption( // --- Other options --- // ... showPageTitle: true, )
Now we can see the webpage title as follows:
This package offers two more configuration properties for toolbar-related customizations. The enableDefaultShare
and enableUrlBarHiding
properties help show or hide the sharing menu item and show/hide the toolbar while scrolling, respectively.
The Android platform typically plays a system animation during every activity launch. Chrome Custom Tabs also inherits the same system animation by default. The flutter_custom_tabs package offers a very flexible way to customize the activity animation of the Chrome Custom Tab via the CustomTabsSystemAnimation
and CustomTabsAnimation
classes.
This package offers two inbuilt activity animations: slideIn
and fade
. You can activate the slideIn
pre-built animation with the following code snippet:
customTabsOption: CustomTabsOption( // --- Other options --- // .... animation: CustomTabsSystemAnimation.slideIn(), )
Now you will see the slideIn
animation as shown in the following preview:
We can use the fade
animation by adding the animation: CustomTabsSystemAnimation.fade()
configuration.
This package is so flexible  —  it lets you use any Android framework core animation instead of the pre-built library animations. For example, you can build a custom animation by using the following configuration object:
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', ), )
Now you will see a different rotating transition animation than the previous pre-built library animations:
You can provide Android framework core animation identifiers for the CustomTabsAnimation
constructor, and browse all Android framework core animations on offer.
Each animation constructor parameter has the following definition:
startEnter
: Enter animation for Custom TabsstartExit
: Exit animation for the application activity that launched Custom TabsendEnter
: Enter animation for the application that launched Custom TabsendExit
: Exit animation for Custom TabsTry to make your own transition animations from Android framework core animations! Just make sure to implement your animations without negatively or drastically affecting the user experience.
The above are common features that every Flutter developer needs to configure Chrome Custom Tabs. The flutter_custom_tabs
package also supports some advanced configurations for specific requirements. For example, you can prioritize fallback browsers if Chrome is not present on the user’s device with the extraCustomTabs
array.
The following configuration prioritizes Mozilla Firefox and then Microsoft Edge if Chrome is not present on the user’s device by referring Android package names:
customTabsOption: CustomTabsOption( extraCustomTabs: const <String>[ 'org.mozilla.firefox', 'com.microsoft.emmx' ], )
Also, this package lets you enable and disable Google Play Instant Apps in Custom Tabs via the enableInstantApps
boolean configuration property. For example, the following configuration activates the Instant Apps feature on Custom Tabs:
>customTabsOption: CustomTabsOption( enableInstantApps: true )
This package also lets you send some header values with the HTTP request, as shown below:
customTabsOption: CustomTabsOption( // --- Other options --- // ... headers: { 'content-type': 'text/plain' } )
Note that these header values need to be CORS safelisted request headers  —  otherwise, you need to configure Google Digital Asset Links with your domain name to send arbitrary request headers.
The flutter_custom_>tabs package supports Android, iOS, and web platforms. On Android, it uses Chrome or other fallback browsers via the underlying CustomTabsLauncher platform-specific Kotlin library.
On iOS, it uses the system-included SFSafariViewController class directly via Flutter method channel APIs for opening a customized Safari instance. On the web platform, it opens a new browser tab via the url_launcher_web package.
The web version of flutter_custom_tabs is not customizable, since we can’t change the browser UI with client-side JavaScript. But, we can customize the Safari instance on iOS in a somewhat similar fashion to Custom Tabs. Look at the following configuration object:
>customTabsOption: CustomTabsOption( // --- Other options --- // ... safariVCOption: SafariViewControllerOption( preferredBarTintColor: Colors.blue, preferredControlTintColor: Colors.white, barCollapsingEnabled: true, entersReaderIfAvailable: true, dismissButtonStyle: SafariViewControllerDismissButtonStyle.close, ), )
The above configuration sets the following Safari UI customizations:
preferredBarTintColor
propertypreferredControlTintColor
propertybarCollapsingEnabled
propertyentersReaderIfAvailable
propertydismissButtonStyle
propertyCustom Tabs is not the only way to render web content on Flutter applications — we have Flutter packages to open the default browser and implement in-app webviews, too. Let’s discuss the pros and cons of launching the default browser and using webview by comparing each option with the Custom Tabs solution.
Point of comparison | Custom Tabs | Default browser | Webview |
---|---|---|---|
Initialization | Offers the fastest initialization time with native Android and Chrome Custom Tabs performance optimizations | Webpage initialization is slow because the default web browser doesn’t use specific initialization performance optimizations as Custom Tabs | Webpage initialization can be boosted with various performance tweaks, but webview components don’t typically support full web APIs and browser features as web browsers do |
Customizability | Able to customize the UI (Toolbar and menu) according to the Flutter application theme | Not able to customize the UI, since the URL launching process is controlled by the Android system  —  not explicitly by the developer | Highly customizable if the webview resides in an activity created by the developer |
UX/Navigating between browser and app | Inbuilt, user-friendly navigation support between the application and Custom Tab | The default browser may appear as a completely different app compared to the Flutter app user experience, and the user typically needs to find ways to close the browser app to return to your app again | Developers can offer the same user experience as the other native parts of the app with various UI and internal webview features because they can customize the webview as they need |
Cookie and permissions sharing | Shares the cookie jar and permission model, so users can see the exact same website state on both Chrome and Custom Tabs instances | Use the same cookie jar and permission model because URLs are launched in the same default browser in the system | Neither the cookie jar nor the permission model is shared among browsers and webview components. The user may have to sign in twice into webpages from the browser and webview, unlike with Custom Tabs |
Availability | Available for Flutter developers via flutter_custom_tabs and flutter_web_browser plugins | Available for Flutter developers via the url_launcher plugin | Available for Flutter developers via the url_launcher plugin (very limited customization) and webview_flutter plugin (flexible customization) |
Let’s summarize the above comparison table according to your application development needs.
If you only need to display a webpage, consider using Custom Tabs rather than launching the default browser for a better user experience. If you need to present your own web content with native-web, communication-bridge-like advanced customizations, the webview-oriented approach gives you complete flexibility and support.
Think twice before using the default browser option  — it does change the application context, affecting the user experience.
Development best practices always help us to build high-quality software systems that users appreciate and may recommend to other users. As you have already experienced, implementing Custom Tabs is very easy with the flutter_custom_tabs package. Even though the implementation is not complex, we need to think about best practices for achieving better application quality.
Consider the following best practices while you are working with Custom Tabs in Flutter.
CustomTabsOption
object in many places by following the DRY programming principle  —  implement a shared function for launching all Custom Tabs, as we created the _launchURL
function beforesafariVCOption
configuration propertyTake a look at the following complete configuration for the final bullet point:
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()); } }
You can download a complete sample Flutter Custom Tabs project source code from my GitHub repository.
In this tutorial, we’ve discussed almost all the features that the flutter_custom_tabs provides. This package provides a simple function to integrate Custom Tabs in Flutter applications. Even though it offers a minimal Dart function for working with Custom Tabs, it lets you make almost all customizations you need using Chrome Custom tabs in Flutter.
However, flutter_custom_tabs has a few issues that need to be fixed. For example, some configuration options like enableDefaultShare
don’t work as expected. Also, there is no option for adding additional action/menu items to a particular Custom Tab, even though the native Android API supports custom action/menu items in Custom Tabs. I also noticed that the Custom Tab closing animation didn’t work properly. Due to these issues, Flutter developers started supporting some alternative plugins, like flutter_web_browser.
But, overall, the flutter_custom_tabs package is still the best Flutter Custom Tabs plugin out there, and it offers a flexible and fully-featured interface for developers. It even provides you Custom Tabs-like implementations for both Flutter web and iOS. This package has a high popularity score on the Dart packages registry and has a well-structured codebase — hence, we can use flutter_custom_tabs
in production apps without any issue.
Package maintainers will add the missing features and fix existing issues soon to support the entire Flutter developer community.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
One Reply to "Using custom Chrome tabs in Flutter with the <code>flutter_custom_tabs</code> plugin"
Hi, I’m new to flutter.
She developed a pwa in react.
I am looking for alternatives to be able to create the app for the apple store, and customtabs, it seems interesting to me.
Tried using webview, but it gives me a lot of trouble using social login.
With custom tabs, is it possible to start the custom tab, when the first widget loads?
That is, the custom tabs opens without pressing the button.
Thanks.
Norbert.