PageSourceSearch

https://inappwebview.dev/assets/js/fbf4b7cc.78955d2c.js

js inappwebview.dev collected 2026-10-03 21:35:27 UTC 17,845 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkinappweview_website=self.webpackChunkinappweview_website||[]).push([["7897"],{3735:function(e,n,r){r.r(n),r.d(n,{metadata:()=>t,contentTitle:()=>l,default:()=>h,assets:()=>c,toc:()=>p,frontMatter:()=>d});var t=JSON.parse('{"id":"in-app-browsers/chrome-safari-browser","title":"ChromeSafariBrowser","description":"The ChromeSafariBrowser class represents Chrome Custom Tabs on Android and","source":"@site/versioned_docs/version-5.x.x/in-app-browsers/chrome-safari-browser.mdx","sourceDirName":"in-app-browsers","slug":"/in-app-browsers/chrome-safari-browser","permalink":"/docs/5.x.x/in-app-browsers/chrome-safari-browser","draft":false,"unlisted":false,"editUrl":"https://github.com/pichillilorenzo/inappwebview.dev/tree/main/versioned_docs/version-5.x.x/in-app-browsers/chrome-safari-browser.mdx","tags":[],"version":"5.x.x","sidebarPosition":2,"frontMatter":{"sidebar_position":2,"date":"2021-03-08T10:48:00.000Z"},"sidebar":"tutorialSidebar","previous":{"title":"InAppBrowser","permalink":"/docs/5.x.x/in-app-browsers/in-app-browser"},"next":{"title":"In-App Localhost Server","permalink":"/docs/5.x.x/in-app-localhost-server"}}'),a=r("5893"),i=r("65"),o=r("7902"),s=r("5525");let d={sidebar_position:2,date:new Date("2021-03-08T10:48:00.000Z")},l="ChromeSafariBrowser",c={},p=[{value:"Basic Usage",id:"basic-usage",level:2},{value:"Android TWA",id:"android-twa",level:2},{value:"Remove the URL bar",id:"remove-the-url-bar",level:3},{value:"TWA Example",id:"twa-example",level:3}];function u(e){let n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",header:"header",img:"img",li:"li",p:"p",pre:"pre",ul:"ul",...(0,i.a)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"chromesafaribrowser",children:"ChromeSafariBrowser"})}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"ChromeSafariBrowser"})," class represents ",(0,a.jsx)(n.a,{href:"https://developer.chrome.com/docs/android/custom-tabs/overview/",children:"Chrome Custom Tabs"})," on Android and\n",(0,a.jsx)(n.a,{href:"https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller",children:"SFSafariViewController"})," on iOS."]}),"\n",(0,a.jsx)(n.h2,{id:"basic-usage",children:"Basic Usage"}),"\n",(0,a.jsx)(n.admonition,{title:"Note for Android",type:"caution",children:(0,a.jsxs)(n.p,{children:["If you want to use the ",(0,a.jsx)(n.code,{children:"ChromeSafariBrowser"})," class on Android 11+, then you need to specify your app querying for ",(0,a.jsx)(n.code,{children:"android.support.customtabs.action.CustomTabsService"}),"\nin your ",(0,a.jsx)(n.code,{children:"AndroidManifest.xml"})," (you can read more about it ",(0,a.jsx)(n.a,{href:"https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above",children:"here"}),")."]})}),"\n",(0,a.jsxs)(n.p,{children:["To be able to listen to the ",(0,a.jsx)(n.code,{children:"ChromeSafariBrowser"})," events, you need to create a class that extends the ",(0,a.jsx)(n.code,{children:"ChromeSafariBrowser"})," class in order to override the method callbacks."]}),"\n",(0,a.jsx)(n.p,{children:"Example:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-dart",children:"import 'dart:io';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\n\nclass MyChromeSafariBrowser extends ChromeSafariBrowser {\n  @override\n  void onOpened() {\n    print(\"ChromeSafari browser opened\");\n  }\n\n  @override\n  void onCompletedInitialLoad() {\n    print(\"ChromeSafari browser initial load completed\");\n  }\n\n  @override\n  void onClosed() {\n    print(\"ChromeSafari browser closed\");\n  }\n}\n\nFuture main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  if (Platform.isAndroid) {\n    await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);\n  }\n\n  runApp(MaterialApp(home: MyApp()));\n}\n\nclass MyApp extends StatefulWidget {\n  final ChromeSafariBrowser browser = new MyChromeSafariBrowser();\n\n  @override\n  _MyAppState createState() => new _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  @override\n  void initState() {\n    widget.browser.addMenuItem(new ChromeSafariBrowserMenuItem(\n        id: 1,\n        label: 'Custom item menu 1',\n        action: (url, title) {\n          print('Custom item menu 1 clicked!');\n        }));\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text('ChromeSafariBrowser Example'),\n      ),\n      body: Center(\n        child: ElevatedButton(\n            onPressed: () async {\n              await widget.browser.open(\n                  url: Uri.parse(\"https://flutter.dev/\"),\n                  options: ChromeSafariBrowserClassOptions(\n                      android: AndroidChromeCustomTabsOptions(\n                          shareState: CustomTabsShareState.SHARE_STATE_OFF),\n                      ios: IOSSafariOptions(barCollapsingEnabled: true)));\n            },\n            child: Text(\"Open Chrome Safari Browser\")),\n      ),\n    );\n  }\n}\n"})}),"\n",(0,a.jsx)(n.p,{children:"This is the result:"}),"\n",(0,a.jsxs)(o.Z,{children:[(0,a.jsx)(s.Z,{value:"android",label:"Android",default:!0,children:(0,a.jsx)(n.p,{children:(0,a.jsx)(n.img,{alt:"ChromeSafariBrowser android basic usage",src:r(3800).Z+"",title:"ChromeSafariBrowser android basic usage.",width:"240",height:"427"})})}),(0,a.jsx)(s.Z,{value:"ios",label:"iOS",children:(0,a.jsx)(n.p,{children:(0,a.jsx)(n.img,{alt:"ChromeSafariBrowser iOS basic usage",src:r(4486).Z+"",title:"ChromeSafariBrowser iOS basic usage.",width:"240",height:"519"})})})]}),"\n",(0,a.jsx)(n.h2,{id:"android-twa",children:"Android TWA"}),"\n",(0,a.jsxs)(n.p,{children:["To create an App for Android that supports TWA (Trusted Web Activity), you can use the ",(0,a.jsx)(n.code,{children:"ChromeSafariBrowser"})," class with the Android-specific option ",(0,a.jsx)(n.code,{children:"isTrustedWebActivity: true"}),"."]}),"\n",(0,a.jsx)(n.h3,{id:"remove-the-url-bar",children:"Remove the URL bar"}),"\n",(0,a.jsx)(n.p,{children:"Trusted Web Activities require an association between the Android application and the website to be established to remove the URL bar."}),"\n",(0,a.jsxs)(n.p,{children:["This association is created via ",(0,a.jsx)(n.a,{href:"https://developers.google.com/digital-asset-links/v1/getting-started",children:"Digital Asset Links"})," and the association must be established in both ways, linking ",(0,a.jsx)(n.a,{href:"https://developers.google.com/digital-asset-links/v1/create-statement",children:"from the app to the website"})," and ",(0,a.jsx)(n.a,{href:"https://developer.android.com/training/app-links/verify-site-associations#web-assoc",children:"from the website to the app"}),"."]}),"\n",(0,a.jsx)(n.p,{children:"It is possible to setup the app to website validation and setup Chrome to skip the website to app validation, for debugging purposes."}),"\n",(0,a.jsxs)(n.p,{children:["Check the official ",(0,a.jsx)(n.a,{href:"https://developer.chrome.com/docs/android/trusted-web-activity/integration-guide/#remove-url-bar",children:"Android Integration Guide"})," for more details."]}),"\n",(0,a.jsx)(n.h3,{id:"twa-example",children:"TWA Example"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-dart",children:'import \'dart:io\';\n\nimport \'package:flutter/material.dart\';\nimport \'package:flutter_inappwebview/flutter_inappwebview.dart\';\n\nclass AndroidTWABrowser extends ChromeSafariBrowser {\n  @override\n  void onOpened() {\n    print("Android TWA browser opened");\n  }\n\n  @override\n  void onCompletedInitialLoad() {\n    print("Android TWA browser initial load completed");\n  }\n\n  @override\n  void onClosed() {\n    print("Android TWA browser closed");\n  }\n}\n\nFuture main() async {\n  WidgetsFlutterBinding.ensureInitialized();
1\n\n  if (Platform.isAndroid) {\n    await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);\n  }\n\n  runApp(MaterialApp(home: MyApp()));\n}\n\nclass MyApp extends StatefulWidget {\n  final ChromeSafariBrowser browser = new AndroidTWABrowser();\n\n  @override\n  _MyAppState createState() => new _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text(\'Android TWA Example\'),\n      ),\n      body: Center(\n        child: ElevatedButton(\n            onPressed: () async {\n              await widget.browser.open(\n                  url: Uri.parse("https://flutter.dev/"),\n                  options: ChromeSafariBrowserClassOptions(\n                    android: AndroidChromeCustomTabsOptions(\n                        isTrustedWebActivity: true),\n                  ));\n            },\n            child: Text("Open Android TWA Browser")),\n      ),\n    );\n  }\n}\n'})}),"\n",(0,a.jsxs)(n.p,{children:["Create the Android string resource file ",(0,a.jsx)(n.code,{children:"android/app/src/main/res/values/strings.xml"})," and add the Digital AssetLinks statement below for the ",(0,a.jsx)(n.a,{href:"https://flutter.dev/",children:"https://flutter.dev/"})," website:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-xml",children:'<resources>\n    <string name="app_name">Flutter Trusted Web Activity</string>\n    <string name="asset_statements">\n        [{\n            \\"relation\\": [\\"delegate_permission/common.handle_all_urls\\"],\n            \\"target\\": {\n                \\"namespace\\": \\"web\\",\n                \\"site\\": \\"https://flutter.dev\\"}\n        }]\n    </string>\n</resources>\n'})}),"\n",(0,a.jsxs)(n.p,{children:["In the Android App Manifest file ",(0,a.jsx)(n.code,{children:"android/app/src/main/AndroidManifest.xml"}),", link to the statement by adding a new ",(0,a.jsx)(n.code,{children:"meta-data"})," tag as a child of the ",(0,a.jsx)(n.code,{children:"application"})," tag:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-xml",children:'<application\n    android:label="flutter_inappwebview_example"\n    android:icon="@mipmap/ic_launcher">\n    \x3c!-- ... --\x3e\n    <meta-data\n        android:name="asset_statements"\n        android:resource="@string/asset_statements" />\n    \x3c!-- ... --\x3e\n    <activity>\n        \x3c!-- ... --\x3e\n    </activity>\n</application>\n'})}),"\n",(0,a.jsx)(n.p,{children:"To enable debug mode:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["Open Chrome on the development device, navigate to ",(0,a.jsx)(n.code,{children:"chrome://flags"}),", search for an item called ",(0,a.jsx)(n.em,{children:"Enable command line on non-rooted devices"})," and change it to ",(0,a.jsx)(n.code,{children:"ENABLED"})," and then restart the browser."]}),"\n",(0,a.jsxs)(n.li,{children:["Next, on the Terminal application of your operating system, use the ",(0,a.jsx)(n.a,{href:"https://developer.android.com/studio/command-line/adb",children:"Android Debug Bridge"})," (installed with Android Studio), and run the following command:"]}),"\n"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-shell",children:'adb shell "echo \'_ --disable-digital-asset-link-verification-for-url=\\"https://flutter.dev\\"\' > /data/local/tmp/chrome-command-line"\n'})}),"\n",(0,a.jsx)(n.p,{children:"Close Chrome and re-launch your application from Android Studio. The application should now be shown in full-screen."}),"\n",(0,a.jsxs)(n.p,{children:["Note: It may needed to force close Chrome so it restarts with the correct command line. Go to ",(0,a.jsx)(n.em,{children:"Android Settings > Apps & notifications > Chrome"}),", and click on ",(0,a.jsx)(n.em,{children:"Force stop"}),"."]}),"\n",(0,a.jsx)(n.p,{children:"This is the result:"}),"\n",(0,a.jsx)(o.Z,{children:(0,a.jsx)(s.Z,{value:"android",label:"Android",default:!0,children:(0,a.jsx)(n.p,{children:(0,a.jsx)(n.img,{alt:"Android TWA example",src:r(6805).Z+"",title:"Android TWA example.",width:"240",height:"480"})})})})]})}function h(e={}){let{wrapper:n}={...(0,i.a)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(u,{...e})}):u(e)}},3800:function(e,n,r){r.d(n,{Z:function(){return t}});let t=r.p+"assets/images/android-chrome-safari-browser-basic-usage-04e5ee63d77d1388b8f81af631b51c61.gif"},6805:function(e,n,r){r.d(n,{Z:function(){return t}});let t=r.p+"assets/images/android-twa-a2610b491621f739c780decbf2ab2186.gif"}
1,4486:function(e,n,r){r.d(n,{Z:function(){return t}});let t=r.p+"assets/images/ios-chrome-safari-browser-basic-usage-64e0fb38076de0ac5bd65a2df1a2185d.gif"},5525:function(e,n,r){r.d(n,{Z:()=>o});var t=r("5893");r("7294");var a=r("4904");let i="tabItem_Ymn6";function o(e){let{children:n,hidden:r,className:o}=e;return(0,t.jsx)("div",{role:"tabpanel",className:(0,a.Z)(i,o),hidden:r,children:n})}},7902:function(e,n,r){r.d(n,{Z:()=>g});var t=r("5893"),a=r("7294"),i=r("4904"),o=r("9599"),s=r("6550"),d=r("2000"),l=r("4520"),c=r("8341"),p=r("6009");function u(e){return a.Children.toArray(e).filter(e=>"\n"!==e).map(e=>{if(!e||a.isValidElement(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)})?.filter(Boolean)??[]}function h(e){let{value:n,tabValues:r}=e;return r.some(e=>e.value===n)}var m=r("7227");let f="tabList__CuJ",b="tabItem_LNqP";function x(e){let{className:n,block:r,selectedValue:a,selectValue:s,tabValues:d}=e,l=[],{blockElementScrollPositionUntilNextRender:c}=(0,o.o5)(),p=e=>{let n=e.currentTarget,r=d[l.indexOf(n)].value;r!==a&&(c(n),s(r))},u=e=>{let n=null;switch(e.key){case"Enter":p(e);break;case"ArrowRight":{let r=l.indexOf(e.currentTarget)+1;n=l[r]??l[0];break}case"ArrowLeft":{let r=l.indexOf(e.currentTarget)-1;n=l[r]??l[l.length-1]}}n?.focus()};return(0,t.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.Z)("tabs",{"tabs--block":r},n),children:d.map(e=>{let{value:n,label:r,attributes:o}=e;return(0,t.jsx)("li",{role:"tab",tabIndex:a===n?0:-1,"aria-selected":a===n,ref:e=>l.push(e),onKeyDown:u,onClick:p,...o,className:(0,i.Z)("tabs__item",b,o?.className,{"tabs__item--active":a===n}),children:r??n},n)})})}function v(e){let{lazy:n,children:r,selectedValue:o}=e,s=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){let e=s.find(e=>e.props.value===o);return e?(0,a.cloneElement)(e,{className:(0,i.Z)("margin-top--md",e.props.className)}):null}return(0,t.jsx)("div",{className:"margin-top--md",children:s.map((e,n)=>(0,a.cloneElement)(e,{key:n,hidden:e.props.value!==o}))})}function w(e){let n=function(e){let{defaultValue:n,queryString:r=!1,groupId:t}=e,i=function(e){let{values:n,children:r}=e;return(0,a.useMemo)(()=>{let e=n??u(r).map(e=>{let{props:{value:n,label:r,attributes:t,default:a}}=e;return{value:n,label:r,attributes:t,default:a}});return!function(e){let n=(0,c.lx)(e,(e,n)=>e.value===n.value);if(n.length>0)throw Error(`Docusaurus error: Duplicate values "${n.map(e=>e.value).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,r])}(e),[o,m]=(0,a.useState)(()=>(function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(n){if(!h({value:n,tabValues:r}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}let t=r.find(e=>e.default)??r[0];if(!t)throw Error("Unexpected error: 0 tabValues");return t.value})({defaultValue:n,tabValues:i})),[f,b]=function(e){let{queryString:n=!1,groupId:r}=e,t=(0,s.k6)(),i=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r}),o=(0,l._X)(i);return[o,(0,a.useCallback)(e=>{if(!i)return;let n=new URLSearchParams(t.location.search);n.set(i,e),t.replace({...t.location,search:n.toString()})},[i,t])]}({queryString:r,groupId:t}),[x,v]=function(e){var n;let{groupId:r}=e;let t=(n=r)?`docusaurus.tab.${n}`:null,[i,o]=(0,p.Nk)(t);return[i,(0,a.useCallback)(e=>{if(!!t)o.set(e)},[t,o])]}({groupId:t}),w=(()=>{let e=f??x;return h({value:e,tabValues:i})?e:null})();return(0,d.Z)(()=>{w&&m(w)},[w]),{selectedValue:o,selectValue:(0,a.useCallback)(e=>{if(!h({value:e,tabValues:i}))throw Error(`Can't select invalid tab value=${e}`);m(e),b(e),v(e)},[b,v,i]),tabValues:i}}(e);return(0,t.jsxs)("div",{className:(0,i.Z)("tabs-container",f),children:[(0,t.jsx)(x,{...n,...e}),(0,t.jsx)(v,{...n,...e})]})}function g(e){let n=(0,m.Z)();return(0,t.jsx)(w,{...e,children:u(e.children)},String(n))}},65:function(e,n,r){r.d(n,{Z:function(){return s},a:function(){return o}});var t=r(7294);let a={},i=t.createContext(a);function o(e){let n=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.