[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: ['https://www.paypal.me/LorenzoPichilli'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/BUG_REPORT.md",
    "content": "---\nname: Bug report\nabout: Something is crashing or not working as intended\nlabels: bug\n---\n\n<!--\n    ❗️❗️❗️ IMPORTANT ❗️❗️❗️\n\n    DON'T DELETE THIS TEMPLATE! USE IT TO WRITE YOUR ISSUE.\n\n    If you delete this and you don't give me enough info,\n    how would you expect me to solve your issue? Thanks.\n\n    Also, before posting a new issue, make sure to check the following points!\n    You may already find an answer to your problem!\n\n    ❗️❗️❗️ IMPORTANT ❗️❗️❗️\n-->\n\n## Environment\n\n| Technology           | Version       |\n| -------------------- | ------------- |\n| Flutter version      |               |\n| App version          |               |\n| Android version      |               |\n| iOS version          |               |\n| Xcode version        |               |\n\nDevice information: <!-- Manufacturer and model -->\n\n## Description\n\n**Expected behavior:** \n\n**Current behavior:** \n\n## Steps to reproduce\n\n<!-- Optionally provide the least amount of code that shows this behaviour. -->\n\n1. This\n2. Than that\n3. Then\n\n## Images <!-- if available, else delete -->  \n\n## Stacktrace/Logcat <!-- if available, else delete -->  \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/FEATURE_REQUEST.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n## Environment\n\n**Flutter version:** \n**App version:**  <!-- Add branch if necessary -->  \n**Android version:**  <!-- If customize ROM, write which -->  \n**iOS version:**\n**Xcode version:**  \n**Device information:**  <!-- Manufacturer and model -->  \n\n## Description\n\n**What you'd like to happen:** \n\n**Alternatives you've considered:** <!-- if available, else delete -->  \n\n**Images:** <!-- if available, else delete -->  \n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: \"Build & Release\"\n\non:\n  push:\n    tags:\n      - \"v*\"\n\njobs:\n  build-mac-ios-android:\n    runs-on: macos-latest\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: master\n\n      - name: Download Android keystore\n        id: android_keystore\n        uses: timheuer/base64-to-file@v1.2\n        with:\n          fileName: keystore.jks\n          encodedString: ${{ secrets.KEYSTORE_BASE64 }}\n      - name: Create key.properties\n        run: |\n          echo \"storeFile=${{ steps.android_keystore.outputs.filePath }}\" > android/key.properties\n          echo \"storePassword=${{ secrets.KEYSTORE_PASSWORD }}\" >> android/key.properties\n          echo \"keyPassword=${{ secrets.KEY_PASSWORD }}\" >> android/key.properties\n          echo \"keyAlias=${{ secrets.KEY_ALIAS }}\" >> android/key.properties\n\n      - uses: actions/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version: \"17\"\n          cache: 'gradle'\n\n      - name: Flutter action\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: '3.24.x'\n          cache: true\n\n      - name: Restore packages\n        run: |\n          flutter pub get\n\n      - name: Install appdmg\n        run: npm install -g appdmg\n\n      - name: Install flutter_distributor\n        run: dart pub global activate flutter_distributor\n\n      - name: Build APK\n        run: |\n          flutter build apk --release --split-per-abi\n\n      - name: Upload APK to Artifacts\n        uses: actions/upload-artifact@v3\n        with:\n          name: android\n          path: |\n            build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk\n            build/app/outputs/flutter-apk/app-arm64-v8a-release.apk\n            build/app/outputs/flutter-apk/app-x86_64-release.apk\n\n      - name: Build IPA\n        run: |\n          flutter build ios --release --no-codesign\n\n      - name: Create IPA\n        run: |\n          mkdir build/ios/iphoneos/Payload\n          cp -R build/ios/iphoneos/Runner.app build/ios/iphoneos/Payload/Runner.app\n          cd build/ios/iphoneos/\n          zip -q -r ios_no_sign.ipa Payload\n          cd ../../..\n\n      - name: Upload IPA to Artifacts\n        uses: actions/upload-artifact@v3\n        with:\n          name: ios\n          path: |\n            build/ios/iphoneos/ios_no_sign.ipa\n\n      - name: Build MacOS\n        run: |\n          flutter_distributor package --platform macos --targets dmg,zip --skip-clean\n\n      - name: Upload MacOS to Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: mac\n          path: |\n            dist/*/*.dmg\n            dist/*/*.zip\n\n      - name: Extract version from pubspec.yaml\n        id: yq\n        run: |\n          yq -r '.version' 'pubspec.yaml'\n\n      - name: Upload Release\n        uses: softprops/action-gh-release@v2\n        with:\n          name: \"${{ steps.yq.outputs.result }}\"\n          token: ${{ secrets.TOKEN }}\n          files: |\n            build/app/outputs/flutter-apk/app-x86_64-release.apk\n            build/app/outputs/flutter-apk/app-arm64-v8a-release.apk\n            build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk\n            build/ios/iphoneos/ios_no_sign.ipa\n            dist/*/*.dmg\n            dist/*/*.zip\n\n      - run: echo \"🍏 This job's status is ${{ job.status }}.\"\n\n  build-windows:\n    runs-on: windows-latest\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: master\n\n      - name: Install yq command line utility\n        run: choco install yq\n\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: \"3.24.x\"\n          cache: true\n      - name: Restore Packages\n        run: |\n          flutter pub get\n\n      - name: Install flutter_distributor\n        run: dart pub global activate flutter_distributor\n\n      - name: Build Windows\n        run: |\n          flutter_distributor package --platform windows --targets msix,zip --skip-clean\n\n      - name: Upload Windows APP to Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: windows\n          path: |\n            dist/*/*.msix\n            dist/*/*.zip\n\n      - name: Extract version from pubspec.yaml\n        id: yq\n        run: |\n          yq -r '.version' 'pubspec.yaml'\n\n      - name: Upload Release\n        uses: softprops/action-gh-release@v2\n        with:\n          name: \"${{ steps.yq.outputs.result }}\"\n          token: ${{ secrets.TOKEN }}\n          files: |\n            dist/*/*.msix\n            dist/*/*.zip\n\n      - run: echo \"🍏 Windows job's status is ${{ job.status }}.\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.build/\n.buildlog/\n.history\n.svn/\n.swiftpm/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n.fvm/\n\n# Web related\nlib/generated_plugin_registrant.dart\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\n# Exceptions to above rules.\n!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages\n"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"2663184aa79047d0a33a14a3b607954f8fdd8730\"\n  channel: \"stable\"\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: macos\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: windows\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   Copyright 2020 Lorenzo Pichilli\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Flutter Browser App\n\n![flutter-browser-article-logo](https://user-images.githubusercontent.com/5956938/86740154-b7a48180-c036-11ea-85c1-cbd662f65f84.png)\n\nA Full-Featured Mobile and Desktop Browser App (such as the Google Chrome mobile browser) created using Flutter and the features offered by the [flutter_inappwebview](https://github.com/pichillilorenzo/flutter_inappwebview) plugin.\n\nIt is available on the **Google Play Store** at [https://play.google.com/store/apps/details?id=com.pichillilorenzo.flutter_browser](https://play.google.com/store/apps/details?id=com.pichillilorenzo.flutter_browser)\n\nFor Desktop builds, check the [Releases](https://github.com/pichillilorenzo/flutter_browser_app/releases) page.\n\n## Introduction\n**Old article**: [Creating a Full-Featured Browser using WebViews in Flutter](https://medium.com/flutter-community/creating-a-full-featured-browser-using-webviews-in-flutter-9c8f2923c574?source=friends_link&sk=55fc8267f351082aa9e73ced546f6bcb).\n\n**OLD**: Check out also the article that introduces the [flutter_inappwebview](https://github.com/pichillilorenzo/flutter_inappwebview) plugin here: [InAppWebView: The Real Power of WebViews in Flutter](https://medium.com/flutter-community/inappwebview-the-real-power-of-webviews-in-flutter-c6d52374209d?source=friends_link&sk=cb74487219bcd85e610a670ee0b447d0).\n\n## Features\n- **Multi-Window Support on Desktop**;\n- **WebView Tab**, with custom on long-press link/image preview, and how to move from one tab to another without losing the WebView state;\n- **Browser App Bar** with the current URL and all popup menu actions such as opening a new tab, a new incognito tab, saving the current URL to the favorite list, saving a page to offline usage, viewing the SSL Certificate used by the website, enable Desktop Mode, etc. (features similar to the Google Chrome App);\n- **Developer console**, where you can execute JavaScript code, see some network info, manage the browser storage such as cookies, window.localStorage, etc;\n- **Settings page**, where you can update the browser general settings and enable/disable all the features offered by the flutter_inappwebview for each WebView Tab, such as enabling/disabling JavaScript, caching, scrollbars, setting custom user-agent, etc., and all the Android and iOS-specific features;\n- **Save** and **restore** the current Browser state.\n\n### Final Result\n**Old video**: [Flutter Browser App Final Result](https://drive.google.com/file/d/1wE2yUGwjNBiUy72GOjPIYyDXYQn3ewYn/view?usp=sharing).\n\nIf you found this useful and you like the [flutter_inappwebview](https://github.com/pichillilorenzo/flutter_inappwebview) plugin and this App project, give a star to these projects, thanks!"
  },
  {
    "path": "analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n**/*.keystore\n**/*.jks\n"
  },
  {
    "path": "android/app/build.gradle",
    "content": "plugins {\n    id \"com.android.application\"\n    id \"kotlin-android\"\n    id \"dev.flutter.flutter-gradle-plugin\"\n}\n\ndef localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\ndef keystoreProperties = new Properties()\ndef keystorePropertiesFile = rootProject.file('key.properties')\nif (keystorePropertiesFile.exists()) {\n    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))\n}\n\nandroid {\n    compileSdk 34\n    ndkVersion flutter.ndkVersion\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n\n    defaultConfig {\n        applicationId \"com.pichillilorenzo.flutter_browser\"\n        minSdkVersion flutter.minSdkVersion\n        targetSdkVersion 34\n        versionCode flutterVersionCode.toInteger()\n        versionName flutterVersionName\n        multiDexEnabled true\n    }\n\n    signingConfigs {\n        release {\n            keyAlias keystoreProperties['keyAlias']\n            keyPassword keystoreProperties['keyPassword']\n            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null\n            storePassword keystoreProperties['storePassword']\n        }\n    }\n\n    buildTypes {\n        release {\n            signingConfig signingConfigs.release\n            ndk {\n                debugSymbolLevel 'FULL'\n            }\n        }\n    }\n    namespace 'com.pichillilorenzo.flutter_browser'\n    lint {\n        disable 'InvalidPackage'\n    }\n}\n\nflutter {\n    source '../..'\n}\n\ndependencies {\n    implementation 'com.google.android.material:material:1.12.0'\n\n    def multidex_version = \"2.0.1\"\n    implementation \"androidx.multidex:multidex:$multidex_version\"\n}\n"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.VIDEO_CAPTURE\" />\n    <uses-permission android:name=\"android.permission.AUDIO_CAPTURE\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.VIDEO_CAPTURE\" />\n    <uses-permission android:name=\"android.permission.AUDIO_CAPTURE\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n    <application\n        android:label=\"Flutter Browser\"\n        android:name=\"${applicationName}\"\n        android:usesCleartextTraffic=\"true\"\n        android:icon=\"@mipmap/launcher_icon\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n\n            <!-- For these schemes were not particular MIME type has been\n                 supplied, we are a good candidate. -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:scheme=\"about\" />\n                <data android:scheme=\"javascript\" />\n            </intent-filter>\n            <!--  For these schemes where any of these particular MIME types\n                  have been supplied, we are a good candidate. -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:scheme=\"inline\" />\n                <data android:mimeType=\"text/html\"/>\n                <data android:mimeType=\"text/plain\"/>\n                <data android:mimeType=\"application/xhtml+xml\"/>\n                <data android:mimeType=\"application/vnd.wap.xhtml+xml\"/>\n            </intent-filter>\n            <!-- We are also the main entry point of the browser. -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.WEB_SEARCH\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n\n        <provider\n            android:name=\"com.pichillilorenzo.flutter_inappwebview_android.InAppWebViewFileProvider\"\n            android:authorities=\"${applicationId}.flutter_inappwebview_android.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/provider_paths\" />\n        </provider>\n\n        <provider\n            android:name=\"vn.hunghd.flutterdownloader.DownloadedFileProvider\"\n            android:authorities=\"${applicationId}.flutter_downloader.provider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/provider_paths\"/>\n        </provider>\n    </application>\n</manifest>"
  },
  {
    "path": "android/app/src/main/kotlin/com/pichillilorenzo/flutter_browser/MainActivity.kt",
    "content": "package com.pichillilorenzo.flutter_browser\n\nimport android.app.SearchManager\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Bundle\nimport android.provider.MediaStore\nimport android.speech.RecognizerResultsIntent\nimport io.flutter.embedding.android.FlutterActivity\nimport io.flutter.embedding.engine.FlutterEngine\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugins.GeneratedPluginRegistrant\n\n\nclass MainActivity: FlutterActivity() {\n\n    private var url: String? = null;\n    //private var headers: Map<String, String>? = null;\n    private val CHANNEL = \"com.pichillilorenzo.flutter_browser.intent_data\"\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        // Log.d(\"intent URI\", intent.toUri(0));\n\n        var url: String? = null\n        //var headers: Map<String, String>? = null\n        val action = intent.action\n        if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS == action) {\n            return\n        }\n        if (Intent.ACTION_VIEW == action) {\n            val data: Uri? = intent.data\n            if (data != null) url = data.toString()\n        } else if (Intent.ACTION_SEARCH == action || MediaStore.INTENT_ACTION_MEDIA_SEARCH == action\n                || Intent.ACTION_WEB_SEARCH == action) {\n            url = intent.getStringExtra(SearchManager.QUERY)\n        }\n//        if (url != null && url.startsWith(\"http\")) {\n//            val pairs = intent\n//                    .getBundleExtra(Browser.EXTRA_HEADERS)\n//            if (pairs != null && !pairs.isEmpty) {\n//                val iter: Iterator<String> = pairs.keySet().iterator()\n//                headers = HashMap()\n//                while (iter.hasNext()) {\n//                    val key = iter.next()\n//                    headers.put(key, pairs.getString(key)!!)\n//                }\n//            }\n//        }\n\n        this.url = url\n        //this.headers = headers\n    }\n\n    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {\n        GeneratedPluginRegistrant.registerWith(flutterEngine)\n        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)\n                .setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->\n                    val methodName = call.method\n                    if (methodName == \"getIntentData\") {\n//                        val data = ArrayList<Any?>();\n//                        data.add(url)\n//                        data.add(headers)\n                        result.success(url)\n                        this.url = null;\n                        //this.headers = null;\n                    }\n                }\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/scrollbar_vertical_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <gradient\n        android:angle=\"0\"\n        android:endColor=\"#005A87\"\n        android:startColor=\"#007AB8\" />\n\n    <corners android:radius=\"6dp\" />\n\n</shape>"
  },
  {
    "path": "android/app/src/main/res/drawable/scrollbar_vertical_track.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <gradient\n        android:angle=\"0\"\n        android:endColor=\"#9BA3C5\"\n        android:startColor=\"#8388A4\" />\n\n    <corners android:radius=\"6dp\" />\n\n</shape>"
  },
  {
    "path": "android/app/src/main/res/drawable-v21/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"?android:colorBackground\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"Theme.MaterialComponents.Light.NoActionBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"Theme.MaterialComponents.DayNight.NoActionBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.VIDEO_CAPTURE\" />\n    <uses-permission android:name=\"android.permission.AUDIO_CAPTURE\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n</manifest>\n"
  },
  {
    "path": "android/build.gradle",
    "content": "allprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntasks.register(\"clean\", Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.5-all.zip\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.enableR8=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nandroid.bundle.enableUncompressedNativeLibs=false"
  },
  {
    "path": "android/settings.gradle",
    "content": "pluginManagement {\n    def flutterSdkPath = {\n        def properties = new Properties()\n        file(\"local.properties\").withInputStream { properties.load(it) }\n        def flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n        assert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\n        return flutterSdkPath\n    }()\n\n    includeBuild(\"$flutterSdkPath/packages/flutter_tools/gradle\")\n\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\nplugins {\n    id \"dev.flutter.flutter-plugin-loader\" version \"1.0.0\"\n    id \"com.android.application\" version '7.4.2' apply false\n    id \"org.jetbrains.kotlin.android\" version \"1.7.10\" apply false\n}\n\ninclude \":app\""
  },
  {
    "path": "ios/.gitignore",
    "content": "**/dgph\n*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/ephemeral/\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>$(DEVELOPMENT_LANGUAGE)</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>12.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Flutter/Debug.xcconfig",
    "content": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Flutter/Release.xcconfig",
    "content": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\nplatform :ios, '12.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "ios/Runner/AppDelegate.swift",
    "content": "import UIKit\nimport Flutter\nimport flutter_downloader\n\n@UIApplicationMain\n\n@objc class AppDelegate: FlutterAppDelegate {\n\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n\nprivate func registerPlugins(registry: FlutterPluginRegistry) {\n    if (!registry.hasPlugin(\"FlutterDownloaderPlugin\")) {\n       FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: \"FlutterDownloaderPlugin\")!)\n    }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "content": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images."
  },
  {
    "path": "ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>FlutterBrowser</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSAppTransportSecurity</key>\n\t<dict>\n\t\t<key>NSAllowsArbitraryLoads</key>\n\t\t<true/>\n\t\t<key>NSAllowsArbitraryLoadsInWebContent</key>\n\t\t<true/>\n\t\t<key>NSAllowsLocalNetworking</key>\n\t\t<true/>\n\t</dict>\n\t<key>NSBonjourServices</key>\n\t<array>\n\t\t<string>_dartobservatory._tcp</string>\n\t</array>\n\t<key>NSCameraUsageDescription</key>\n\t<string>$(PRODUCT_NAME) camera use</string>\n\t<key>NSMicrophoneUsageDescription</key>\n\t<string>$(PRODUCT_NAME) microphone use</string>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>fetch</string>\n\t\t<string>remote-notification</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t420340CC0D9D0D1B41D8C7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65580FF38E583F75628F4B2B /* Pods_Runner.framework */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t3D42713954B5CD677C63D861 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t58603F292996258C3A0A1BB4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t65580FF38E583F75628F4B2B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t9BF35EFC29634C4D7E14C52D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t420340CC0D9D0D1B41D8C7D1 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t04C19C2BB5F0C41EB406429B /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t65580FF38E583F75628F4B2B /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\tB49E7B33350EC7421B2098A9 /* Pods */,\n\t\t\t\t04C19C2BB5F0C41EB406429B /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t97C146F11CF9000F007C117D /* Supporting Files */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F11CF9000F007C117D /* Supporting Files */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = \"Supporting Files\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB49E7B33350EC7421B2098A9 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t58603F292996258C3A0A1BB4 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t3D42713954B5CD677C63D861 /* Pods-Runner.release.xcconfig */,\n\t\t\t\t9BF35EFC29634C4D7E14C52D /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t13F2619FF9F3F4C52B0076CA /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t\t37FF70F36850709A3BFADFDB /* [CP] Embed Pods Frameworks */,\n\t\t\t\tADCA64C961EA4FED14CC71A2 /* [CP] Copy Pods Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t13F2619FF9F3F4C52B0076CA /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t37FF70F36850709A3BFADFDB /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\",\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n\t\tADCA64C961EA4FED14CC71A2 /* [CP] Copy Pods Resources */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Copy Pods Resources\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = PFP8UV45Y6;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.pichillilorenzo.flutter-browser-app2\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = \"\";\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = PFP8UV45Y6;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.pichillilorenzo.flutter-browser-app2\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = PFP8UV45Y6;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.pichillilorenzo.flutter-browser-app2\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "lib/animated_flutter_browser_logo.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AnimatedFlutterBrowserLogo extends StatefulWidget {\n  final Duration animationDuration;\n  final double size;\n\n  const AnimatedFlutterBrowserLogo({\n    super.key,\n    this.animationDuration = const Duration(milliseconds: 1000),\n    this.size = 100.0,\n  });\n\n  @override\n  State<StatefulWidget> createState() => _AnimatedFlutterBrowserLogoState();\n}\n\nclass _AnimatedFlutterBrowserLogoState extends State<AnimatedFlutterBrowserLogo>\n    with TickerProviderStateMixin {\n  late AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n\n    _controller =\n        AnimationController(duration: widget.animationDuration, vsync: this);\n    _controller.repeat(reverse: true);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ScaleTransition(\n      scale: Tween(begin: 0.75, end: 2.0).animate(\n          CurvedAnimation(parent: _controller, curve: Curves.elasticOut)),\n      child: SizedBox(\n        height: widget.size,\n        width: widget.size,\n        child: const CircleAvatar(\n            backgroundImage: AssetImage(\"assets/icon/icon.png\")),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/browser_app_bar.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/app_bar/desktop_app_bar.dart';\nimport 'package:flutter_browser/app_bar/find_on_page_app_bar.dart';\nimport 'package:flutter_browser/app_bar/webview_tab_app_bar.dart';\nimport 'package:flutter_browser/util.dart';\n\nclass BrowserAppBar extends StatefulWidget implements PreferredSizeWidget {\n  BrowserAppBar({super.key})\n      : preferredSize =\n            Size.fromHeight(Util.isMobile() ? kToolbarHeight : 90.0);\n\n  @override\n  State<BrowserAppBar> createState() => _BrowserAppBarState();\n\n  @override\n  final Size preferredSize;\n}\n\nclass _BrowserAppBarState extends State<BrowserAppBar> {\n  bool _isFindingOnPage = false;\n\n  @override\n  Widget build(BuildContext context) {\n    final List<Widget> children = [];\n\n    if (Util.isDesktop()) {\n      children.add(const DesktopAppBar());\n    }\n\n    children.add(_isFindingOnPage\n        ? FindOnPageAppBar(\n            hideFindOnPage: () {\n              setState(() {\n                _isFindingOnPage = false;\n              });\n            },\n          )\n        : WebViewTabAppBar(\n            showFindOnPage: () {\n              setState(() {\n                _isFindingOnPage = true;\n              });\n            },\n          ));\n\n    return Column(\n      children: children,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/certificates_info_popup.dart",
    "content": "import 'dart:developer';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:crypto/crypto.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_downloader/flutter_downloader.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:intl/intl.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:provider/provider.dart';\nimport 'package:collection/collection.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass CertificateInfoPopup extends StatefulWidget {\n  const CertificateInfoPopup({super.key});\n\n  @override\n  State<CertificateInfoPopup> createState() => _CertificateInfoPopupState();\n}\n\nclass _CertificateInfoPopupState extends State<CertificateInfoPopup> {\n  final List<X509Certificate> _otherCertificates = [];\n  X509Certificate? _topMainCertificate;\n  X509Certificate? _selectedCertificate;\n\n  @override\n  Widget build(BuildContext context) {\n    return _build();\n  }\n\n  Widget _build() {\n    if (_topMainCertificate == null) {\n      var webViewModel = Provider.of<WebViewModel>(context, listen: true);\n\n      return FutureBuilder(\n        future: webViewModel.webViewController?.getCertificate(),\n        builder: (context, snapshot) {\n          if (!snapshot.hasData ||\n              snapshot.connectionState != ConnectionState.done) {\n            return Container();\n          }\n          SslCertificate sslCertificate = snapshot.data as SslCertificate;\n          _topMainCertificate = sslCertificate.x509Certificate;\n          _selectedCertificate = _topMainCertificate!;\n\n          return FutureBuilder(\n            future: _getOtherCertificatesFromTopMain(\n                _otherCertificates, _topMainCertificate!),\n            builder: (context, snapshot) {\n              if (snapshot.connectionState == ConnectionState.done) {\n                return _buildCertificatesInfoAlertDialog();\n              }\n              return Center(\n                child: Container(\n                  decoration: const BoxDecoration(\n                      color: Colors.white,\n                      borderRadius: BorderRadius.all(Radius.circular(2.5))),\n                  padding: const EdgeInsets.all(25.0),\n                  width: 100.0,\n                  height: 100.0,\n                  child: const CircularProgressIndicator(\n                    strokeWidth: 4.0,\n                  ),\n                ),\n              );\n            },\n          );\n        },\n      );\n    } else {\n      return _buildCertificatesInfoAlertDialog();\n    }\n  }\n\n  Future<void> _getOtherCertificatesFromTopMain(\n      List<X509Certificate> otherCertificates,\n      X509Certificate x509certificate) async {\n    var authorityInfoAccess = x509certificate.authorityInfoAccess;\n    if (authorityInfoAccess != null && authorityInfoAccess.infoAccess != null) {\n      for (var i = 0; i < authorityInfoAccess.infoAccess!.length; i++) {\n        try {\n          var caIssuerUrl = authorityInfoAccess\n              .infoAccess![i].location; // [OID.caIssuers.toValue()];\n          HttpClientRequest request =\n              await HttpClient().getUrl(Uri.parse(caIssuerUrl));\n          HttpClientResponse response = await request.close();\n          var certData = Uint8List.fromList(await response.first);\n          var cert = X509Certificate.fromData(data: certData);\n          otherCertificates.add(cert);\n          await _getOtherCertificatesFromTopMain(otherCertificates, cert);\n        } catch (e) {\n          log(e.toString());\n        }\n      }\n    }\n\n    var cRLDistributionPoints = x509certificate.cRLDistributionPoints;\n    if (cRLDistributionPoints != null && cRLDistributionPoints.crls != null) {\n      for (var i = 0; i < cRLDistributionPoints.crls!.length; i++) {\n        var crlUrl = cRLDistributionPoints.crls![i];\n        try {\n          HttpClientRequest request =\n              await HttpClient().getUrl(Uri.parse(crlUrl));\n          HttpClientResponse response = await request.close();\n          var certData = Uint8List.fromList(await response.first);\n          var cert = X509Certificate.fromData(data: certData);\n          otherCertificates.add(cert);\n          await _getOtherCertificatesFromTopMain(otherCertificates, cert);\n        } catch (e) {\n          log(e.toString());\n        }\n      }\n    }\n  }\n\n  AlertDialog _buildCertificatesInfoAlertDialog() {\n    var webViewModel = Provider.of<WebViewModel>(context, listen: true);\n    var url = webViewModel.url;\n\n    return AlertDialog(\n      content: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: <Widget>[\n          Row(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              Container(\n                decoration: const BoxDecoration(\n                    color: Colors.green,\n                    borderRadius: BorderRadius.all(Radius.circular(5.0))),\n                padding: const EdgeInsets.all(5.0),\n                child: const Icon(\n                  Icons.lock,\n                  color: Colors.white,\n                  size: 20.0,\n                ),\n              ),\n              Expanded(\n                child: Container(\n                  padding: const EdgeInsets.symmetric(horizontal: 10.0),\n                  child: Column(\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: <Widget>[\n                      Text(\n                        url?.host ?? \"\",\n                        style: const TextStyle(\n                            fontSize: 16.0, fontWeight: FontWeight.bold),\n                      ),\n                      const SizedBox(\n                        height: 15.0,\n                      ),\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: <Widget>[\n                          Expanded(\n                            child: Text(\n                              \"Flutter Browser has verified that ${_topMainCertificate?.issuer(dn: ASN1DistinguishedNames.COMMON_NAME)} has emitted the web site certificate.\",\n                              softWrap: true,\n                              style: const TextStyle(fontSize: 12.0),\n                            ),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(\n                        height: 15.0,\n                      ),\n                      RichText(\n                        text: TextSpan(\n                            text: \"Certificate info\",\n                            style: const TextStyle(\n                                color: Colors.blue, fontSize: 12.0),\n                            recognizer: TapGestureRecognizer()\n                              ..onTap = () {\n                                showDialog(\n                                  context: context,\n                                  builder: (context) {\n                                    List<X509Certificate> certificates = [\n                                      _topMainCertificate!\n                                    ];\n                                    certificates.addAll(_otherCertificates);\n\n                                    return AlertDialog(\n                                      content: Container(\n                                          constraints: BoxConstraints(\n                                              maxWidth: MediaQuery.of(context)\n                                                      .size\n                                                      .width /\n                                                  2.5),\n                                          child: StatefulBuilder(\n                                            builder: (context, setState) {\n                                              List<\n                                                      DropdownMenuItem<\n                                                          X509Certificate>>\n                                                  dropdownMenuItems = [];\n                                              for (var certificate\n                                                  in certificates) {\n                                                var name = _findCommonName(\n                                                        x509certificate:\n                                                            certificate,\n                                                        isSubject: true) ??\n                                                    _findOrganizationName(\n                                                        x509certificate:\n                                                            certificate,\n                                                        isSubject: true) ??\n                                                    \"\";\n                                                if (name != \"\") {\n                                                  dropdownMenuItems.add(\n                                                      DropdownMenuItem<\n                                                          X509Certificate>(\n                                                    value: certificate,\n                                                    child: Text(name),\n                                                  ));\n                                                }\n                                              }\n\n                                              return Column(\n                                                crossAxisAlignment:\n                                                    CrossAxisAlignment.start,\n                                                mainAxisSize: MainAxisSize.min,\n                                                children: <Widget>[\n                                                  const Text(\n                                                    \"Certificate Viewer\",\n                                                    style: TextStyle(\n                                                        fontSize: 24.0,\n                                                        color: Colors.black,\n                                                        fontWeight:\n                                                            FontWeight.bold),\n                                                  ),\n                                                  const SizedBox(\n                                                    height: 15.0,\n                                                  ),\n                                                  DropdownButton<\n                                                      X509Certificate>(\n                                                    isExpanded: true,\n                                                    onChanged: (value) {\n                                                      setState(() {\n                                                        _selectedCertificate =\n                                                            value;\n                                                      });\n                                                    },\n                                                    value: _selectedCertificate,\n                                                    items: dropdownMenuItems,\n                                                  ),\n                                                  const SizedBox(\n                                                    height: 15.0,\n                                                  ),\n                                                  Flexible(\n                                                    child:\n                                                        SingleChildScrollView(\n                                                      child: _buildCertificateInfo(\n                                                          _selectedCertificate!),\n                                                    ),\n                                                  ),\n                                                ],\n                                              );\n                                            },\n                                          )),\n                                    );\n                                  },\n                                );\n                              }),\n                      )\n                    ],\n                  ),\n                ),\n              ),\n            ],\n          )\n        ],\n      ),\n    );\n  }\n\n  Widget _buildCertificateInfo(X509Certificate x509certificate) {\n    var issuedToSection = _buildIssuedToSection(x509certificate);\n    var issuedBySection = _buildIssuedBySection(x509certificate);\n    var validityPeriodSection = _buildValidityPeriodSection(x509certificate);\n    var publicKeySection = _buildPublicKeySection(x509certificate);\n    var fingerprintSection = _buildFingerprintSection(x509certificate);\n    var extensionSection = _buildExtensionSection(x509certificate);\n\n    var children = <Widget>[];\n    children.addAll(issuedToSection);\n    children.addAll(issuedBySection);\n    children.addAll(validityPeriodSection);\n    children.addAll(publicKeySection);\n    children.addAll(fingerprintSection);\n    children.addAll(extensionSection);\n\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: children,\n    );\n  }\n\n  String? _findCountryName(\n      {required X509Certificate x509certificate, required bool isSubject}) {\n    try {\n      return (isSubject\n              ? x509certificate.subject(dn: ASN1DistinguishedNames.COUNTRY_NAME)\n              : x509certificate.issuer(\n                  dn: ASN1DistinguishedNames.COUNTRY_NAME)) ??\n          x509certificate.block1\n              ?.findOid(oid: OID.countryName)\n              ?.parent\n              ?.sub\n              ?.last\n              .value;\n    } catch (e) {\n      log(e.toString());\n    }\n    return null;\n  }\n\n  String? _findStateOrProvinceName(\n      {required X509Certificate x509certificate, required bool isSubject}) {\n    try {\n      return (isSubject\n              ? x509certificate.subject(\n                  dn: ASN1DistinguishedNames.STATE_OR_PROVINCE_NAME)\n              : x509certificate.issuer(\n                  dn: ASN1DistinguishedNames.STATE_OR_PROVINCE_NAME)) ??\n          x509certificate.block1\n              ?.findOid(oid: OID.stateOrProvinceName)\n              ?.parent\n              ?.sub\n              ?.last\n              .value;\n    } catch (e) {\n      log(e.toString());\n    }\n    return null;\n  }\n\n  String? _findCommonName(\n      {required X509Certificate x509certificate, required bool isSubject}) {\n    try {\n      return (isSubject\n              ? x509certificate.subject(dn: ASN1DistinguishedNames.COMMON_NAME)\n              : x509certificate.issuer(\n                  dn: ASN1DistinguishedNames.COMMON_NAME)) ??\n          x509certificate.block1\n              ?.findOid(oid: OID.commonName)\n              ?.parent\n              ?.sub\n              ?.last\n              .value;\n    } catch (e) {\n      log(e.toString());\n    }\n    return null;\n  }\n\n  String? _findOrganizationName(\n      {required X509Certificate x509certificate, required bool isSubject}) {\n    try {\n      return (isSubject\n              ? x509certificate.subject(\n                  dn: ASN1DistinguishedNames.ORGANIZATION_NAME)\n              : x509certificate.issuer(\n                  dn: ASN1DistinguishedNames.ORGANIZATION_NAME)) ??\n          x509certificate.block1\n              ?.findOid(oid: OID.organizationName)\n              ?.parent\n              ?.sub\n              ?.last\n              .value;\n    } catch (e) {\n      log(e.toString());\n    }\n    return null;\n  }\n\n  String? _findOrganizationUnitName(\n      {required X509Certificate x509certificate, required bool isSubject}) {\n    try {\n      return (isSubject\n              ? x509certificate.subject(\n                  dn: ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME)\n              : x509certificate.issuer(\n                  dn: ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME)) ??\n          x509certificate.block1\n              ?.findOid(oid: OID.organizationalUnitName)\n              ?.parent\n              ?.sub\n              ?.last\n              .value;\n    } catch (e) {\n      log(e.toString());\n    }\n    return null;\n  }\n\n  List<Widget> _buildIssuedToSection(X509Certificate x509certificate) {\n    var subjectCountryName =\n        _findCountryName(x509certificate: x509certificate, isSubject: true) ??\n            \"<Not Part Of Certificate>\";\n    var subjectStateOrProvinceName = _findStateOrProvinceName(\n            x509certificate: x509certificate, isSubject: true) ??\n        \"<Not Part Of Certificate>\";\n    var subjectCN =\n        _findCommonName(x509certificate: x509certificate, isSubject: true) ??\n            \"<Not Part Of Certificate>\";\n    var subjectO = _findOrganizationName(\n            x509certificate: x509certificate, isSubject: true) ??\n        \"<Not Part Of Certificate>\";\n    var subjectU = _findOrganizationUnitName(\n            x509certificate: x509certificate, isSubject: true) ??\n        \"<Not Part Of Certificate>\";\n\n    return <Widget>[\n      const Text(\n        \"ISSUED TO\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Common Name (CN)\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        subjectCN,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Organization (O)\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        subjectO,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Organizational Unit (U)\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        subjectU,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Country\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        subjectCountryName,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"State/Province\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        subjectStateOrProvinceName,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n    ];\n  }\n\n  List<Widget> _buildIssuedBySection(X509Certificate x509certificate) {\n    var issuerCountryName =\n        _findCountryName(x509certificate: x509certificate, isSubject: false) ??\n            \"<Not Part Of Certificate>\";\n    var issuerStateOrProvinceName = _findStateOrProvinceName(\n            x509certificate: x509certificate, isSubject: false) ??\n        \"<Not Part Of Certificate>\";\n    var issuerCN =\n        _findCommonName(x509certificate: x509certificate, isSubject: false) ??\n            \"<Not Part Of Certificate>\";\n    var issuerO = _findOrganizationName(\n            x509certificate: x509certificate, isSubject: false) ??\n        \"<Not Part Of Certificate>\";\n    var issuerU = _findOrganizationUnitName(\n            x509certificate: x509certificate, isSubject: false) ??\n        \"<Not Part Of Certificate>\";\n    var serialNumber = x509certificate.serialNumber\n            ?.map((byte) {\n              var hexValue = byte.toRadixString(16);\n              if (byte == 0 || hexValue.length == 1) {\n                hexValue = \"0$hexValue\";\n              }\n              return hexValue.toUpperCase();\n            })\n            .toList()\n            .join(\":\") ??\n        \"<Not Part Of Certificate>\";\n    var version =\n        x509certificate.version?.toString() ?? \"<Not Part Of Certificate>\";\n    var sigAlgName = x509certificate.sigAlgName ?? \"<Not Part Of Certificate>\";\n\n    return <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      const Text(\n        \"ISSUED BY\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Common Name (CN)\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        issuerCN,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Organization (O)\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        issuerO,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Organizational Unit (U)\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        issuerU,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Country\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        issuerCountryName,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"State/Province\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        issuerStateOrProvinceName,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Serial Number\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        serialNumber,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Version\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        version,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Signature Algorithm\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        sigAlgName,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n    ];\n  }\n\n  List<Widget> _buildValidityPeriodSection(X509Certificate x509certificate) {\n    var issuedOnDate = x509certificate.notBefore != null\n        ? DateFormat(\"dd MMM yyyy HH:mm:ss\").format(x509certificate.notBefore!)\n        : \"<Not Part Of Certificate>\";\n    var expiresOnDate = x509certificate.notAfter != null\n        ? DateFormat(\"dd MMM yyyy HH:mm:ss\").format(x509certificate.notAfter!)\n        : \"<Not Part Of Certificate>\";\n\n    return <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      const Text(\n        \"VALIDITY PERIOD\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Issued on date\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        issuedOnDate,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Expires on date\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        expiresOnDate,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n    ];\n  }\n\n  List<Widget> _buildPublicKeySection(X509Certificate x509certificate) {\n    var publicKey = x509certificate.publicKey;\n    var publicKeyAlg = \"<Not Part Of Certificate>\";\n    var publicKeyAlgParams = \"<Not Part Of Certificate>\";\n    if (publicKey != null) {\n      if (publicKey.algOid != null) {\n        publicKeyAlg =\n            \"${OID.fromValue(publicKey.algOid)!.name()} ( ${publicKey.algOid} )\";\n      }\n      if (publicKey.algParams != null) {\n        publicKeyAlgParams =\n            \"${OID.fromValue(publicKey.algParams)!.name()} ( ${publicKey.algParams} )\";\n      }\n    }\n\n    return <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      const Text(\n        \"PUBLIC KEY\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Algorithm\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        publicKeyAlg,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Parameters\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      Text(\n        publicKeyAlgParams,\n        style: const TextStyle(fontSize: 14.0),\n      ),\n    ];\n  }\n\n  List<Widget> _buildFingerprintSection(X509Certificate x509certificate) {\n    return <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      const Text(\n        \"FINGERPRINT\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Fingerprint SHA-256\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      FutureBuilder(\n        future: x509certificate.encoded != null\n            ? sha256.bind(Stream.value(x509certificate.encoded!)).first\n            : null,\n        builder: (context, snapshot) {\n          if (!snapshot.hasData ||\n              snapshot.connectionState != ConnectionState.done) {\n            return const Text(\"\");\n          }\n\n          Digest digest = snapshot.data as Digest;\n          return Text(\n            digest.bytes\n                .map((byte) {\n                  var hexValue = byte.toRadixString(16);\n                  if (byte == 0 || hexValue.length == 1) {\n                    hexValue = \"0$hexValue\";\n                  }\n                  return hexValue.toUpperCase();\n                })\n                .toList()\n                .join(\" \"),\n            style: const TextStyle(fontSize: 14.0),\n          );\n        },\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      const Text(\n        \"Fingerprint SHA-1\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      FutureBuilder(\n        future: sha1.bind(Stream.value(x509certificate.encoded!)).first,\n        builder: (context, snapshot) {\n          if (!snapshot.hasData ||\n              snapshot.connectionState != ConnectionState.done) {\n            return const Text(\"\");\n          }\n\n          Digest digest = snapshot.data as Digest;\n          return Text(\n            digest.bytes\n                .map((byte) {\n                  var hexValue = byte.toRadixString(16);\n                  if (byte == 0 || hexValue.length == 1) {\n                    hexValue = \"0$hexValue\";\n                  }\n                  return hexValue.toUpperCase();\n                })\n                .toList()\n                .join(\" \"),\n            style: const TextStyle(fontSize: 14.0),\n          );\n        },\n      ),\n    ];\n  }\n\n  List<Widget> _buildExtensionSection(X509Certificate x509certificate) {\n    var extensionSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      const Text(\n        \"EXTENSIONS\",\n        style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n\n    extensionSection.addAll(_buildKeyUsageSection(x509certificate));\n    extensionSection.addAll(_buildBasicConstraints(x509certificate));\n    extensionSection.addAll(_buildExtendedKeyUsage(x509certificate));\n    extensionSection.addAll(_buildSubjectKeyIdentifier(x509certificate));\n    extensionSection.addAll(_buildAuthorityKeyIdentifier(x509certificate));\n    extensionSection.addAll(_buildCertificatePolicies(x509certificate));\n    extensionSection.addAll(_buildCRLDistributionPoints(x509certificate));\n    extensionSection.addAll(_buildAuthorityInfoAccess(x509certificate));\n    extensionSection.addAll(_buildSubjectAlternativeNames(x509certificate));\n\n    return extensionSection;\n  }\n\n  List<Widget> _buildKeyUsageSection(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var keyUsage = x509certificate.keyUsage;\n\n    var keyUsageSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Key Usage ( ${OID.keyUsage.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n\n    var keyUsageIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull((oid) => oid == OID.keyUsage.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n\n    if (keyUsage.isNotEmpty) {\n      for (var i = 0; i < keyUsage.length; i++) {\n        if (keyUsage[i]) {\n          keyUsageSection.addAll(<Widget>[\n            const SizedBox(\n              height: 5.0,\n            ),\n            RichText(\n              text: TextSpan(children: [\n                const TextSpan(\n                    text: \"Critical \",\n                    style: TextStyle(\n                        fontSize: 12.0,\n                        fontWeight: FontWeight.bold,\n                        color: Colors.black)),\n                TextSpan(\n                    text: keyUsageIsCritical,\n                    style: const TextStyle(fontSize: 12.0, color: Colors.black))\n              ]),\n            ),\n            RichText(\n              text: TextSpan(children: [\n                const TextSpan(\n                    text: \"Usage \",\n                    style: TextStyle(\n                        fontSize: 12.0,\n                        fontWeight: FontWeight.bold,\n                        color: Colors.black)),\n                TextSpan(\n                    text: KeyUsage.fromIndex(i)!.name(),\n                    style: const TextStyle(fontSize: 12.0, color: Colors.black))\n              ]),\n            ),\n          ]);\n        }\n      }\n    } else {\n      keyUsageSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return keyUsageSection;\n  }\n\n  List<Widget> _buildBasicConstraints(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var basicConstraints = x509certificate.basicConstraints;\n\n    var basicConstraintsSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Basic Constraints ( ${OID.basicConstraints.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n    var basicConstraintsIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull(\n                    (oid) => oid == OID.basicConstraints.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n    if (basicConstraints != null && basicConstraints.pathLenConstraint == -1) {\n      basicConstraintsSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Critical \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: basicConstraintsIsCritical,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n        RichText(\n          text: const TextSpan(children: [\n            TextSpan(\n                text: \"Certificate Authority \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: \"NO\",\n                style: TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n      ]);\n    } else {\n      basicConstraintsSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Critical \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: basicConstraintsIsCritical,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n        RichText(\n          text: const TextSpan(children: [\n            TextSpan(\n                text: \"Certificate Authority \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: \"YES\",\n                style: TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Path Length Constraints \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: basicConstraints.toString(),\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n      ]);\n    }\n\n    return basicConstraintsSection;\n  }\n\n  List<Widget> _buildExtendedKeyUsage(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var extendedKeyUsage = x509certificate.extendedKeyUsage;\n\n    var extendedKeyUsageSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Extended Key Usage ( ${OID.extKeyUsage.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n    var extendedKeyUsageIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull((oid) => oid == OID.extKeyUsage.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n    if (extendedKeyUsage.isNotEmpty) {\n      for (var i = 0; i < extendedKeyUsage.length; i++) {\n        OID oid = OID.fromValue(extendedKeyUsage[i])!;\n\n        extendedKeyUsageSection.addAll(<Widget>[\n          const SizedBox(\n            height: 5.0,\n          ),\n          RichText(\n            text: TextSpan(children: [\n              const TextSpan(\n                  text: \"Critical \",\n                  style: TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                  text: extendedKeyUsageIsCritical,\n                  style: const TextStyle(fontSize: 12.0, color: Colors.black))\n            ]),\n          ),\n          RichText(\n            text: TextSpan(children: [\n              TextSpan(\n                  text: \"Purpose #${i + 1} \",\n                  style: const TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                  text: \"${oid.name()} ( ${oid.toValue()} )\",\n                  style: const TextStyle(fontSize: 12.0, color: Colors.black))\n            ]),\n          ),\n        ]);\n      }\n    } else {\n      extendedKeyUsageSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return extendedKeyUsageSection;\n  }\n\n  List<Widget> _buildSubjectKeyIdentifier(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var subjectKeyIdentifier = x509certificate.subjectKeyIdentifier;\n\n    var subjectKeyIdentifierSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Subject Key Identifier ( ${OID.subjectKeyIdentifier.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n    var subjectKeyIdentifierIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull(\n                    (oid) => oid == OID.subjectKeyIdentifier.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n    if (subjectKeyIdentifier?.value != null &&\n        subjectKeyIdentifier!.value!.isNotEmpty) {\n      var subjectKeyIdentifierToHexValue = subjectKeyIdentifier.value!\n          .map((byte) {\n            var hexValue = byte.toRadixString(16);\n            if (byte == 0 || hexValue.length == 1) {\n              hexValue = \"0$hexValue\";\n            }\n            return hexValue.toUpperCase();\n          })\n          .toList()\n          .join(\" \");\n\n      subjectKeyIdentifierSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Critical \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: subjectKeyIdentifierIsCritical,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Key ID \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: subjectKeyIdentifierToHexValue,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        )\n      ]);\n    } else {\n      subjectKeyIdentifierSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return subjectKeyIdentifierSection;\n  }\n\n  List<Widget> _buildAuthorityKeyIdentifier(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var authorityKeyIdentifier = x509certificate.authorityKeyIdentifier;\n\n    var authorityKeyIdentifierSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Authority Key Identifier ( ${OID.authorityKeyIdentifier.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n    var authorityKeyIdentifierIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull(\n                    (oid) => oid == OID.authorityKeyIdentifier.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n    if (authorityKeyIdentifier?.keyIdentifier != null &&\n        authorityKeyIdentifier!.keyIdentifier!.isNotEmpty) {\n      var authorityKeyIdentifierToHexValue =\n          authorityKeyIdentifier.keyIdentifier!\n              .map((byte) {\n                var hexValue = byte.toRadixString(16);\n                if (byte == 0 || hexValue.length == 1) {\n                  hexValue = \"0$hexValue\";\n                }\n                return hexValue.toUpperCase();\n              })\n              .toList()\n              .join(\" \");\n\n      authorityKeyIdentifierSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Critical \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: authorityKeyIdentifierIsCritical,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Key ID \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: authorityKeyIdentifierToHexValue,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        )\n      ]);\n    } else {\n      authorityKeyIdentifierSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return authorityKeyIdentifierSection;\n  }\n\n  List<Widget> _buildCertificatePolicies(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var certificatePolicies = x509certificate.certificatePolicies;\n\n    var certificatePoliciesIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull((oid) => oid == OID.extKeyUsage.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n\n    var certificatePoliciesSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Certificate Policies ( ${OID.certificatePolicies.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      RichText(\n        text: TextSpan(children: [\n          const TextSpan(\n              text: \"Critical \",\n              style: TextStyle(\n                  fontSize: 12.0,\n                  fontWeight: FontWeight.bold,\n                  color: Colors.black)),\n          TextSpan(\n              text: certificatePoliciesIsCritical,\n              style: const TextStyle(fontSize: 12.0, color: Colors.black))\n        ]),\n      ),\n    ];\n\n    if (certificatePolicies?.policies != null &&\n        certificatePolicies!.policies!.isNotEmpty) {\n      for (var i = 0; i < certificatePolicies.policies!.length; i++) {\n        OID? oid = OID.fromValue(certificatePolicies.policies![i].oid);\n\n        certificatePoliciesSection.addAll(<Widget>[\n          RichText(\n            text: TextSpan(children: [\n              TextSpan(\n                  text: \"ID policy num. ${i + 1} \",\n                  style: const TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                  text: (oid != null)\n                      ? \"${oid.name()} ( ${oid.toValue()} )\"\n                      : \"( ${certificatePolicies.policies![i].oid} )\",\n                  style: const TextStyle(fontSize: 12.0, color: Colors.black))\n            ]),\n          ),\n        ]);\n      }\n    } else {\n      certificatePoliciesSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return certificatePoliciesSection;\n  }\n\n  List<Widget> _buildCRLDistributionPoints(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var cRLDistributionPoints = x509certificate.cRLDistributionPoints;\n\n    var cRLDistributionPointsIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull(\n                    (oid) => oid == OID.cRLDistributionPoints.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n\n    var cRLDistributionPointsSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"CRL Distribution Points ( ${OID.cRLDistributionPoints.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      RichText(\n        text: TextSpan(children: [\n          const TextSpan(\n              text: \"Critical \",\n              style: TextStyle(\n                  fontSize: 12.0,\n                  fontWeight: FontWeight.bold,\n                  color: Colors.black)),\n          TextSpan(\n              text: cRLDistributionPointsIsCritical,\n              style: const TextStyle(fontSize: 12.0, color: Colors.black))\n        ]),\n      ),\n    ];\n\n    if (cRLDistributionPoints?.crls != null &&\n        cRLDistributionPoints!.crls!.isNotEmpty) {\n      for (var i = 0; i < cRLDistributionPoints.crls!.length; i++) {\n        cRLDistributionPointsSection.addAll(<Widget>[\n          RichText(\n            text: TextSpan(children: [\n              const TextSpan(\n                  text: \"URI \",\n                  style: TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                  text: cRLDistributionPoints.crls![i],\n                  style: const TextStyle(fontSize: 12.0, color: Colors.blue),\n                  recognizer: TapGestureRecognizer()\n                    ..onTap = () async {\n                      final crlUrl = cRLDistributionPoints.crls![i];\n                      try {\n                        Directory? directory =\n                            await getExternalStorageDirectory();\n                        await FlutterDownloader.enqueue(\n                          url: crlUrl,\n                          savedDir: directory!.path,\n                          showNotification: true,\n                          // show download progress in status bar (for Android)\n                          openFileFromNotification:\n                              true, // click on notification to open downloaded file (for Android)\n                        );\n                      } catch (e) {\n                        final crlUri = Uri.tryParse(crlUrl);\n                        if (crlUri != null && await canLaunchUrl(crlUri)) {\n                          launchUrl(crlUri);\n                        }\n                      }\n                    })\n            ]),\n          ),\n        ]);\n      }\n    } else {\n      cRLDistributionPointsSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return cRLDistributionPointsSection;\n  }\n\n  List<Widget> _buildAuthorityInfoAccess(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var authorityInfoAccess = x509certificate.authorityInfoAccess;\n\n    var authorityInfoAccessIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull(\n                    (oid) => oid == OID.authorityInfoAccess.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n\n    var authorityInfoAccessSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Authority Info Access ( ${OID.authorityInfoAccess.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n      const SizedBox(\n        height: 5.0,\n      ),\n      RichText(\n        text: TextSpan(children: [\n          const TextSpan(\n              text: \"Critical \",\n              style: TextStyle(\n                  fontSize: 12.0,\n                  fontWeight: FontWeight.bold,\n                  color: Colors.black)),\n          TextSpan(\n              text: authorityInfoAccessIsCritical,\n              style: const TextStyle(fontSize: 12.0, color: Colors.black))\n        ]),\n      ),\n    ];\n\n    if (authorityInfoAccess?.infoAccess != null &&\n        authorityInfoAccess!.infoAccess!.isNotEmpty) {\n      for (var i = 0; i < authorityInfoAccess.infoAccess!.length; i++) {\n        var infoAccess = authorityInfoAccess.infoAccess![i];\n        var value = infoAccess.location;\n        var oid = OID.fromValue(infoAccess.method);\n\n        authorityInfoAccessSection.addAll(<Widget>[\n          RichText(\n            text: TextSpan(children: [\n              TextSpan(\n                  text: \"Method #${i + 1} \",\n                  style: const TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                text: oid != null\n                    ? \"${oid.name()} ( ${oid.toValue()} )\"\n                    : infoAccess.method,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black),\n              )\n            ]),\n          ),\n          RichText(\n            text: TextSpan(children: [\n              const TextSpan(\n                  text: \"URI \",\n                  style: TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                  text: value,\n                  style: const TextStyle(fontSize: 12.0, color: Colors.blue),\n                  recognizer: TapGestureRecognizer()\n                    ..onTap = () async {\n                      Directory? directory =\n                          await getExternalStorageDirectory();\n                      await FlutterDownloader.enqueue(\n                        url: value,\n                        savedDir: directory!.path,\n                        showNotification: true,\n                        // show download progress in status bar (for Android)\n                        openFileFromNotification:\n                            true, // click on notification to open downloaded file (for Android)\n                      );\n                    })\n            ]),\n          ),\n        ]);\n      }\n    } else {\n      authorityInfoAccessSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return authorityInfoAccessSection;\n  }\n\n  List<Widget> _buildSubjectAlternativeNames(X509Certificate x509certificate) {\n    var criticalExtensionOIDs = x509certificate.criticalExtensionOIDs;\n    var subjectAlternativeNames = x509certificate.subjectAlternativeNames;\n\n    var subjectAlternativeNamesSection = <Widget>[\n      const SizedBox(\n        height: 15.0,\n      ),\n      Text(\n        \"Subject Alternative Names ( ${OID.subjectAltName.toValue()} )\",\n        style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),\n      ),\n    ];\n    var subjectAlternativeNamesIsCritical = criticalExtensionOIDs\n                .map((e) => e)\n                .firstWhereOrNull(\n                    (oid) => oid == OID.subjectAltName.toValue()) !=\n            null\n        ? \"YES\"\n        : \"NO\";\n    if (subjectAlternativeNames.isNotEmpty) {\n      subjectAlternativeNamesSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        RichText(\n          text: TextSpan(children: [\n            const TextSpan(\n                text: \"Critical \",\n                style: TextStyle(\n                    fontSize: 12.0,\n                    fontWeight: FontWeight.bold,\n                    color: Colors.black)),\n            TextSpan(\n                text: subjectAlternativeNamesIsCritical,\n                style: const TextStyle(fontSize: 12.0, color: Colors.black))\n          ]),\n        ),\n      ]);\n      for (var subjectAlternativeName in subjectAlternativeNames) {\n        subjectAlternativeNamesSection.addAll(<Widget>[\n          const SizedBox(\n            height: 5.0,\n          ),\n          RichText(\n            text: TextSpan(children: [\n              const TextSpan(\n                  text: \"DNS Name \",\n                  style: TextStyle(\n                      fontSize: 12.0,\n                      fontWeight: FontWeight.bold,\n                      color: Colors.black)),\n              TextSpan(\n                  text: subjectAlternativeName,\n                  style: const TextStyle(fontSize: 12.0, color: Colors.black))\n            ]),\n          ),\n        ]);\n      }\n    } else {\n      subjectAlternativeNamesSection.addAll(<Widget>[\n        const SizedBox(\n          height: 5.0,\n        ),\n        const Text(\n          \"<Not Part Of Certificate>\",\n          style: TextStyle(fontSize: 14.0),\n        ),\n      ]);\n    }\n\n    return subjectAlternativeNamesSection;\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/custom_app_bar_wrapper.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/app_bar/desktop_app_bar.dart';\nimport 'package:flutter_browser/util.dart';\n\nclass CustomAppBarWrapper extends StatefulWidget implements PreferredSizeWidget {\n  final AppBar appBar;\n\n  CustomAppBarWrapper({super.key, required this.appBar})\n      : preferredSize = Util.isMobile()\n            ? _PreferredAppBarSize(\n                kToolbarHeight, appBar.bottom?.preferredSize.height)\n            : Size.fromHeight(_PreferredAppBarSize(\n                        kToolbarHeight, appBar.bottom?.preferredSize.height)\n                    .height +\n                40);\n\n  @override\n  State<CustomAppBarWrapper> createState() => _CustomAppBarWrapperState();\n\n  @override\n  final Size preferredSize;\n}\n\nclass _CustomAppBarWrapperState extends State<CustomAppBarWrapper> {\n  @override\n  Widget build(BuildContext context) {\n    final List<Widget> children = [];\n\n    if (Util.isDesktop()) {\n      children.add(const DesktopAppBar(showTabs: false,));\n    } else {\n      return widget.appBar;\n    }\n\n    children.add(Flexible(child: widget.appBar));\n\n    return Column(\n      children: children,\n    );\n  }\n}\n\nclass _PreferredAppBarSize extends Size {\n  _PreferredAppBarSize(this.toolbarHeight, this.bottomHeight)\n      : super.fromHeight(\n            (toolbarHeight ?? kToolbarHeight) + (bottomHeight ?? 0));\n\n  final double? toolbarHeight;\n  final double? bottomHeight;\n}\n"
  },
  {
    "path": "lib/app_bar/desktop_app_bar.dart",
    "content": "import 'package:collection/collection.dart';\nimport 'package:context_menus/context_menus.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\nimport 'package:window_manager_plus/window_manager_plus.dart';\n\nimport '../custom_image.dart';\nimport '../models/browser_model.dart';\nimport '../models/webview_model.dart';\nimport '../models/window_model.dart';\nimport '../util.dart';\nimport '../webview_tab.dart';\n\nclass DesktopAppBar extends StatefulWidget {\n  final bool showTabs;\n\n  const DesktopAppBar({super.key, this.showTabs = true});\n\n  @override\n  State<DesktopAppBar> createState() => _DesktopAppBarState();\n}\n\nclass _DesktopAppBarState extends State<DesktopAppBar> {\n  @override\n  Widget build(BuildContext context) {\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n\n    final tabSelectors = !widget.showTabs\n        ? <Widget>[]\n        : windowModel.webViewTabs.map((webViewTab) {\n            final index = windowModel.webViewTabs.indexOf(webViewTab);\n            final currentIndex = windowModel.getCurrentTabIndex();\n\n            return Flexible(\n                flex: 1,\n                fit: FlexFit.loose,\n                child: IntrinsicHeight(\n                  child: Row(\n                    children: [\n                      Expanded(\n                          child: WebViewTabSelector(\n                              tab: webViewTab, index: index)),\n                      SizedBox(\n                        height: 15,\n                        child: VerticalDivider(\n                            thickness: 1,\n                            width: 1,\n                            color: index == currentIndex - 1 ||\n                                    index == currentIndex\n                                ? Colors.transparent\n                                : Colors.black45),\n                      )\n                    ],\n                  ),\n                ));\n          }).toList();\n\n    final windowActions = [];\n    if (!Util.isWindows()) {\n      windowActions.addAll([\n        const SizedBox(\n          width: 8,\n        ),\n        IconButton(\n            onPressed: () {\n              WindowManagerPlus.current.close();\n            },\n            constraints: const BoxConstraints(\n              maxWidth: 13,\n              minWidth: 13,\n              maxHeight: 13,\n              minHeight: 13,\n            ),\n            padding: EdgeInsets.zero,\n            style: ButtonStyle(\n                tapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                backgroundColor: const WidgetStatePropertyAll(Colors.red),\n                iconColor: WidgetStateProperty.resolveWith(\n                  (states) => states.contains(WidgetState.hovered)\n                      ? Colors.black45\n                      : Colors.red,\n                )),\n            color: Colors.red,\n            icon: const Icon(\n              Icons.close,\n              size: 10,\n            )),\n        const SizedBox(\n          width: 8,\n        ),\n        IconButton(\n            onPressed: () async {\n              if (!(await  WindowManagerPlus.current.isFullScreen())) {\n                 WindowManagerPlus.current.minimize();\n              }\n            },\n            constraints: const BoxConstraints(\n              maxWidth: 13,\n              minWidth: 13,\n              maxHeight: 13,\n              minHeight: 13,\n            ),\n            padding: EdgeInsets.zero,\n            style: ButtonStyle(\n                tapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                backgroundColor: const WidgetStatePropertyAll(Colors.amber),\n                iconColor: WidgetStateProperty.resolveWith(\n                  (states) => states.contains(WidgetState.hovered)\n                      ? Colors.black45\n                      : Colors.amber,\n                )),\n            color: Colors.amber,\n            icon: const Icon(\n              Icons.remove,\n              size: 10,\n            )),\n        const SizedBox(\n          width: 8,\n        ),\n        IconButton(\n            onPressed: () async {\n               WindowManagerPlus.current\n                  .setFullScreen(!(await  WindowManagerPlus.current.isFullScreen()));\n            },\n            constraints: const BoxConstraints(\n              maxWidth: 13,\n              minWidth: 13,\n              maxHeight: 13,\n              minHeight: 13,\n            ),\n            padding: EdgeInsets.zero,\n            style: ButtonStyle(\n                tapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                backgroundColor: const WidgetStatePropertyAll(Colors.green),\n                iconColor: WidgetStateProperty.resolveWith(\n                  (states) => states.contains(WidgetState.hovered)\n                      ? Colors.black45\n                      : Colors.green,\n                )),\n            color: Colors.green,\n            icon: const Icon(\n              Icons.open_in_full,\n              size: 10,\n            )),\n        const SizedBox(\n          width: 8,\n        ),\n      ]);\n    }\n\n    final children = [\n      Container(\n        constraints:\n            BoxConstraints(maxWidth: MediaQuery.of(context).size.width - 100),\n        child: IntrinsicWidth(\n          child: Row(\n            children: [\n              ...windowActions,\n              Flexible(\n                  child: Container(\n                padding: const EdgeInsets.only(top: 4.0),\n                child: Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: tabSelectors.isNotEmpty\n                      ? tabSelectors\n                      : [\n                          const SizedBox(\n                            height: 30,\n                          )\n                        ],\n                ),\n              )),\n              const SizedBox(\n                width: 5,\n              ),\n              !widget.showTabs\n                  ? null\n                  : IconButton(\n                      onPressed: () {\n                        _addNewTab();\n                      },\n                      constraints: const BoxConstraints(\n                        maxWidth: 25,\n                        minWidth: 25,\n                        maxHeight: 25,\n                        minHeight: 25,\n                      ),\n                      padding: EdgeInsets.zero,\n                      icon: const Icon(\n                        Icons.add,\n                        size: 15,\n                        color: Colors.white,\n                      )),\n            ].whereNotNull().toList().cast<Widget>(),\n          ),\n        ),\n      ),\n      Flexible(\n          child: MouseRegion(\n              hitTestBehavior: HitTestBehavior.opaque,\n              onEnter: (details) {\n                if (!Util.isWindows()) {\n                   WindowManagerPlus.current.setMovable(true);\n                }\n                setState(() {});\n              },\n              onExit: (details) {\n                if (!Util.isWindows()) {\n                   WindowManagerPlus.current.setMovable(false);\n                }\n              },\n              child: GestureDetector(\n                behavior: HitTestBehavior.opaque,\n                onDoubleTap: () async {\n                  await  WindowManagerPlus.current.maximize();\n                },\n                child: !widget.showTabs\n                    ? const SizedBox(\n                        height: 30,\n                        width: double.infinity,\n                      )\n                    : ContextMenuRegion(\n                        behavior: const [ContextMenuShowBehavior.secondaryTap],\n                        contextMenu: GenericContextMenu(\n                          buttonConfigs: [\n                            ContextMenuButtonConfig(\n                              \"New Tab\",\n                              onPressed: () {\n                                _addNewTab();\n                              },\n                            ),\n                            ContextMenuButtonConfig(\n                              \"Close All\",\n                              onPressed: () {\n                                windowModel.closeAllTabs();\n                              },\n                            ),\n                          ],\n                        ),\n                        child: const SizedBox(\n                          height: 30,\n                          width: double.infinity,\n                        )),\n              ))),\n      !widget.showTabs\n          ? null\n          : OpenTabsViewer(\n              webViewTabs: windowModel.webViewTabs,\n            ),\n    ].whereNotNull().toList();\n\n    return Container(\n      color: Theme.of(context).colorScheme.primary,\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        mainAxisSize: MainAxisSize.max,\n        children: children,\n      ),\n    );\n  }\n\n  void _addNewTab() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final settings = browserModel.getSettings();\n    windowModel.addTab(WebViewTab(\n      key: GlobalKey(),\n      webViewModel: WebViewModel(url: WebUri(settings.searchEngine.url)),\n    ));\n  }\n}\n\nclass WebViewTabSelector extends StatefulWidget {\n  final WebViewTab tab;\n  final int index;\n\n  const WebViewTabSelector({super.key, required this.tab, required this.index});\n\n  @override\n  State<WebViewTabSelector> createState() => _WebViewTabSelectorState();\n}\n\nclass _WebViewTabSelectorState extends State<WebViewTabSelector> {\n  bool isHover = false;\n\n  @override\n  void dispose() {\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    final isCurrentTab = windowModel.getCurrentTabIndex() == widget.index;\n\n    final tab = widget.tab;\n    final url = tab.webViewModel.url;\n    var tabName = tab.webViewModel.title ?? url?.toString() ?? '';\n    if (tabName.isEmpty) {\n      tabName = 'New Tab';\n    }\n    final tooltipText =\n        '$tabName\\n${(url?.host ?? '').isEmpty ? url?.toString() : url?.host}'\n            .trim();\n    final faviconUrl = tab.webViewModel.favicon != null\n        ? tab.webViewModel.favicon!.url\n        : (url != null && [\"http\", \"https\"].contains(url.scheme)\n            ? Uri.parse(\"${url.origin}/favicon.ico\")\n            : null);\n\n    return MouseRegion(\n      onEnter: (event) {\n        setState(() {\n          isHover = true;\n        });\n      },\n      onExit: (event) {\n        setState(() {\n          isHover = false;\n        });\n      },\n      child: GestureDetector(\n        behavior: HitTestBehavior.opaque,\n        onTap: () {\n          windowModel.showTab(widget.index);\n        },\n        child: ContextMenuRegion(\n            contextMenu: GenericContextMenu(\n              buttonConfigs: [\n                ContextMenuButtonConfig(\n                  \"Reload\",\n                  onPressed: () {\n                    tab.webViewModel.webViewController?.reload();\n                  },\n                ),\n                ContextMenuButtonConfig(\n                  \"Duplicate\",\n                  onPressed: () {\n                    if (tab.webViewModel.url != null) {\n                      windowModel.addTab(WebViewTab(\n                        key: GlobalKey(),\n                        webViewModel: WebViewModel(url: tab.webViewModel.url),\n                      ));\n                    }\n                  },\n                ),\n                ContextMenuButtonConfig(\n                  \"Close\",\n                  onPressed: () {\n                    windowModel.closeTab(widget.index);\n                  },\n                ),\n              ],\n            ),\n            child: Container(\n              height: 30,\n              width: double.infinity,\n              constraints: const BoxConstraints(maxWidth: 250),\n              padding: const EdgeInsets.only(right: 5.0),\n              decoration: !isCurrentTab\n                  ? null\n                  : BoxDecoration(\n                      color: Theme.of(context).colorScheme.primaryContainer,\n                      borderRadius:\n                          const BorderRadius.vertical(top: Radius.circular(5))),\n              child: Tooltip(\n                decoration: const BoxDecoration(\n                    color: Colors.black,\n                    borderRadius: BorderRadius.all(Radius.circular(5))),\n                richMessage: WidgetSpan(\n                    alignment: PlaceholderAlignment.baseline,\n                    baseline: TextBaseline.alphabetic,\n                    child: Container(\n                      constraints: const BoxConstraints(maxWidth: 400),\n                      child: Text(\n                        tooltipText,\n                        overflow: TextOverflow.ellipsis,\n                        maxLines: 3,\n                        style: const TextStyle(color: Colors.white),\n                      ),\n                    )),\n                waitDuration: const Duration(milliseconds: 500),\n                child: Row(\n                  mainAxisSize: MainAxisSize.max,\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: [\n                    Flexible(\n                        child: Row(\n                      children: [\n                        Container(\n                          padding: const EdgeInsets.all(8),\n                          child: CustomImage(\n                              url: faviconUrl, maxWidth: 20.0, height: 20.0),\n                        ),\n                        Flexible(\n                            child: Text(tabName,\n                                overflow: TextOverflow.ellipsis,\n                                maxLines: 1,\n                                softWrap: false,\n                                style: TextStyle(\n                                    fontSize: 12,\n                                    color:\n                                        !isCurrentTab ? Colors.white : null))),\n                      ],\n                    )),\n                    IconButton(\n                        onPressed: () {\n                          windowModel.closeTab(widget.index);\n                        },\n                        constraints: const BoxConstraints(\n                          maxWidth: 20,\n                          minWidth: 20,\n                          maxHeight: 20,\n                          minHeight: 20,\n                        ),\n                        padding: EdgeInsets.zero,\n                        icon: Icon(\n                          Icons.cancel,\n                          color: !isCurrentTab ? Colors.white : null,\n                          size: 15,\n                        )),\n                  ],\n                ),\n              ),\n            )),\n      ),\n    );\n  }\n}\n\nclass OpenTabsViewer extends StatefulWidget {\n  final List<WebViewTab> webViewTabs;\n\n  const OpenTabsViewer({super.key, required this.webViewTabs});\n\n  @override\n  State<OpenTabsViewer> createState() => _OpenTabsViewerState();\n}\n\nclass _OpenTabsViewerState extends State<OpenTabsViewer> {\n  final TextEditingController _controller = TextEditingController();\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n        padding: const EdgeInsets.all(4.0),\n        child: MenuAnchor(\n          builder: (context, controller, child) {\n            return IconButton(\n                onPressed: () {\n                  if (controller.isOpen) {\n                    controller.close();\n                  } else {\n                    controller.open();\n                  }\n                },\n                constraints: const BoxConstraints(\n                  maxWidth: 25,\n                  minWidth: 25,\n                  maxHeight: 25,\n                  minHeight: 25,\n                ),\n                padding: EdgeInsets.zero,\n                icon: const Icon(\n                  Icons.keyboard_arrow_down,\n                  size: 15,\n                  color: Colors.white,\n                ));\n          },\n          menuChildren: [\n            ConstrainedBox(\n              constraints: const BoxConstraints(\n                minWidth: 200,\n              ),\n              child: TextFormField(\n                controller: _controller,\n                maxLines: 1,\n                style: Theme.of(context).textTheme.labelLarge,\n                decoration: const InputDecoration(\n                  prefixIcon: Icon(Icons.search),\n                  hintText: 'Search open tabs',\n                  contentPadding: EdgeInsets.only(top: 15),\n                  isDense: true,\n                ),\n                onChanged: (value) {\n                  setState(() {});\n                },\n              ),\n            ),\n            MenuItemButton(\n              onPressed: null,\n              child: Text(\n                widget.webViewTabs.isEmpty ? 'No tabs open' : 'Tabs open',\n                style: Theme.of(context).textTheme.labelLarge,\n              ),\n            ),\n            ...(widget.webViewTabs.where(\n              (element) {\n                final search = _controller.text.toLowerCase().trim();\n                final containsInTitle = element.webViewModel.title\n                        ?.toLowerCase()\n                        .contains(search) ??\n                    false;\n                final containsInUrl = element.webViewModel.url\n                        ?.toString()\n                        .toLowerCase()\n                        .contains(search) ??\n                    false;\n                return search.isEmpty || containsInTitle || containsInUrl;\n              },\n            ).map((w) {\n              final url = w.webViewModel.url;\n              final title = (w.webViewModel.title ?? '').isNotEmpty\n                  ? w.webViewModel.title!\n                  : 'New Tab';\n              var subtitle =\n                  (url?.host ?? '').isEmpty ? url?.toString() : url?.host;\n              final diffTime =\n                  DateTime.now().difference(w.webViewModel.lastOpenedTime);\n              var diffTimeSubtitle = 'now';\n              if (diffTime.inDays > 0) {\n                diffTimeSubtitle =\n                    '${diffTime.inDays} ${diffTime.inDays == 1 ? 'day' : 'days'} ago';\n              } else if (diffTime.inMinutes > 0) {\n                diffTimeSubtitle = '${diffTime.inMinutes} min ago';\n              } else if (diffTime.inSeconds > 0) {\n                diffTimeSubtitle = '${diffTime.inSeconds} sec ago';\n              }\n\n              final faviconUrl = w.webViewModel.favicon != null\n                  ? w.webViewModel.favicon!.url\n                  : (url != null && [\"http\", \"https\"].contains(url.scheme)\n                      ? Uri.parse(\"${url.origin}/favicon.ico\")\n                      : null);\n\n              return MenuItemButton(\n                onPressed: () {\n                  final windowModel =\n                      Provider.of<WindowModel>(context, listen: false);\n                  windowModel.showTab(windowModel.webViewTabs.indexOf(w));\n                },\n                leadingIcon: Container(\n                  padding: const EdgeInsets.all(8),\n                  child: CustomImage(url: faviconUrl, maxWidth: 15, height: 15),\n                ),\n                trailingIcon: IconButton(\n                    onPressed: () {\n                      final windowModel =\n                          Provider.of<WindowModel>(context, listen: false);\n                      windowModel.closeTab(widget.webViewTabs.indexOf(w));\n                    },\n                    constraints: const BoxConstraints(\n                      maxWidth: 25,\n                      minWidth: 25,\n                      maxHeight: 25,\n                      minHeight: 25,\n                    ),\n                    padding: EdgeInsets.zero,\n                    icon: const Icon(\n                      Icons.cancel,\n                      size: 15,\n                    )),\n                child: ConstrainedBox(\n                    constraints: const BoxConstraints(\n                      maxWidth: 250,\n                    ),\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          title,\n                          overflow: TextOverflow.ellipsis,\n                          style: Theme.of(context).textTheme.labelMedium,\n                        ),\n                        Row(\n                          children: [\n                            Flexible(\n                                child: Text(\n                              subtitle ?? '',\n                              overflow: TextOverflow.ellipsis,\n                              style: Theme.of(context).textTheme.labelSmall,\n                            )),\n                            Text(\n                              \" - $diffTimeSubtitle\",\n                              style: Theme.of(context).textTheme.labelSmall,\n                            )\n                          ],\n                        )\n                      ].whereNotNull().toList(),\n                    )),\n              );\n            }).toList())\n          ].whereNotNull().toList(),\n        ));\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/find_on_page_app_bar.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:provider/provider.dart';\n\nimport '../models/window_model.dart';\n\nclass FindOnPageAppBar extends StatefulWidget {\n  final void Function()? hideFindOnPage;\n\n  const FindOnPageAppBar({super.key, this.hideFindOnPage});\n\n  @override\n  State<FindOnPageAppBar> createState() => _FindOnPageAppBarState();\n}\n\nclass _FindOnPageAppBarState extends State<FindOnPageAppBar> {\n  final TextEditingController _finOnPageController = TextEditingController();\n\n  OutlineInputBorder outlineBorder = const OutlineInputBorder(\n    borderSide: BorderSide(color: Colors.transparent, width: 0.0),\n    borderRadius: BorderRadius.all(\n      Radius.circular(50.0),\n    ),\n  );\n\n  @override\n  void dispose() {\n    _finOnPageController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final webViewModel = windowModel.getCurrentTab()?.webViewModel;\n    final findInteractionController = webViewModel?.findInteractionController;\n\n    return AppBar(\n      titleSpacing: 10.0,\n      title: SizedBox(\n          height: 40.0,\n          child: TextField(\n            onSubmitted: (value) {\n              findInteractionController?.findAll(find: value);\n            },\n            controller: _finOnPageController,\n            textInputAction: TextInputAction.go,\n            decoration: InputDecoration(\n              contentPadding: const EdgeInsets.all(10.0),\n              filled: true,\n              fillColor: Colors.white,\n              border: outlineBorder,\n              focusedBorder: outlineBorder,\n              enabledBorder: outlineBorder,\n              hintText: \"Find on page ...\",\n              hintStyle: const TextStyle(color: Colors.black54, fontSize: 16.0),\n            ),\n            style: const TextStyle(color: Colors.black, fontSize: 16.0),\n          )),\n      actions: <Widget>[\n        IconButton(\n          icon: const Icon(Icons.keyboard_arrow_up),\n          onPressed: () {\n            findInteractionController?.findNext(forward: false);\n          },\n        ),\n        IconButton(\n          icon: const Icon(Icons.keyboard_arrow_down),\n          onPressed: () {\n            findInteractionController?.findNext(forward: true);\n          },\n        ),\n        IconButton(\n          icon: const Icon(Icons.close),\n          onPressed: () {\n            findInteractionController?.clearMatches();\n            _finOnPageController.text = \"\";\n\n            if (widget.hideFindOnPage != null) {\n              widget.hideFindOnPage!();\n            }\n          },\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/tab_viewer_app_bar.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/pages/settings/main.dart';\nimport 'package:flutter_browser/webview_tab.dart';\nimport 'package:flutter_font_icons/flutter_font_icons.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport '../custom_popup_menu_item.dart';\nimport '../models/window_model.dart';\nimport '../tab_viewer_popup_menu_actions.dart';\n\nclass TabViewerAppBar extends StatefulWidget implements PreferredSizeWidget {\n  const TabViewerAppBar({super.key})\n      : preferredSize = const Size.fromHeight(kToolbarHeight);\n\n  @override\n  State<TabViewerAppBar> createState() => _TabViewerAppBarState();\n\n  @override\n  final Size preferredSize;\n}\n\nclass _TabViewerAppBarState extends State<TabViewerAppBar> {\n  GlobalKey tabInkWellKey = GlobalKey();\n\n  @override\n  Widget build(BuildContext context) {\n    return AppBar(\n      titleSpacing: 10.0,\n      leading: _buildAddTabButton(),\n      actions: _buildActionsMenu(),\n    );\n  }\n\n  Widget _buildAddTabButton() {\n    return IconButton(\n      icon: const Icon(Icons.add),\n      onPressed: () {\n        addNewTab();\n      },\n    );\n  }\n\n  List<Widget> _buildActionsMenu() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: true);\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    final settings = browserModel.getSettings();\n\n    return <Widget>[\n      InkWell(\n        key: tabInkWellKey,\n        onTap: () {\n          if (windowModel.webViewTabs.isNotEmpty) {\n            browserModel.showTabScroller = !browserModel.showTabScroller;\n          } else {\n            browserModel.showTabScroller = false;\n          }\n        },\n        child: Padding(\n          padding: settings.homePageEnabled\n              ? const EdgeInsets.only(\n                  left: 20.0, top: 15.0, right: 10.0, bottom: 15.0)\n              : const EdgeInsets.only(\n                  left: 10.0, top: 15.0, right: 10.0, bottom: 15.0),\n          child: Container(\n            decoration: BoxDecoration(\n                border: Border.all(width: 2.0),\n                shape: BoxShape.rectangle,\n                borderRadius: BorderRadius.circular(5.0)),\n            constraints: const BoxConstraints(minWidth: 25.0),\n            child: Center(\n                child: Text(\n                  windowModel.webViewTabs.length.toString(),\n              style: const TextStyle(\n                  fontWeight: FontWeight.bold,\n                  fontSize: 14.0),\n            )),\n          ),\n        ),\n      ),\n      PopupMenuButton<String>(\n        onSelected: _popupMenuChoiceAction,\n        itemBuilder: (popupMenuContext) {\n          var items = <PopupMenuEntry<String>>[];\n\n          items.addAll(TabViewerPopupMenuActions.choices.map((choice) {\n            switch (choice) {\n              case TabViewerPopupMenuActions.NEW_TAB:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.add,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case TabViewerPopupMenuActions.NEW_INCOGNITO_TAB:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          MaterialCommunityIcons.incognito,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case TabViewerPopupMenuActions.CLOSE_ALL_TABS:\n                return CustomPopupMenuItem<String>(\n                  enabled: windowModel.webViewTabs.isNotEmpty,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.close,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case TabViewerPopupMenuActions.SETTINGS:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.settings,\n                          color: Colors.grey,\n                        )\n                      ]),\n                );\n              default:\n                return CustomPopupMenuItem<String>(\n                  value: choice,\n                  child: Text(choice),\n                );\n            }\n          }).toList());\n\n          return items;\n        },\n      )\n    ];\n  }\n\n  void _popupMenuChoiceAction(String choice) async {\n    switch (choice) {\n      case TabViewerPopupMenuActions.NEW_TAB:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          addNewTab();\n        });\n        break;\n      case TabViewerPopupMenuActions.NEW_INCOGNITO_TAB:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          addNewIncognitoTab();\n        });\n        break;\n      case TabViewerPopupMenuActions.CLOSE_ALL_TABS:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          closeAllTabs();\n        });\n        break;\n      case TabViewerPopupMenuActions.SETTINGS:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          goToSettingsPage();\n        });\n        break;\n    }\n  }\n\n  void addNewTab({WebUri? url}) {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final settings = browserModel.getSettings();\n\n    url ??= settings.homePageEnabled && settings.customUrlHomePage.isNotEmpty\n        ? WebUri(settings.customUrlHomePage)\n        : WebUri(settings.searchEngine.url);\n\n    browserModel.showTabScroller = false;\n\n    windowModel.addTab(WebViewTab(\n      key: GlobalKey(),\n      webViewModel: WebViewModel(url: url),\n    ));\n  }\n\n  void addNewIncognitoTab({WebUri? url}) {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final settings = browserModel.getSettings();\n\n    url ??= settings.homePageEnabled && settings.customUrlHomePage.isNotEmpty\n        ? WebUri(settings.customUrlHomePage)\n        : WebUri(settings.searchEngine.url);\n\n    browserModel.showTabScroller = false;\n\n    windowModel.addTab(WebViewTab(\n      key: GlobalKey(),\n      webViewModel: WebViewModel(url: url, isIncognitoMode: true),\n    ));\n  }\n\n  void closeAllTabs() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n\n    browserModel.showTabScroller = false;\n\n    windowModel.closeAllTabs();\n  }\n\n  void goToSettingsPage() {\n    Navigator.push(\n        context, MaterialPageRoute(builder: (context) => const SettingsPage()));\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/url_info_popup.dart",
    "content": "import 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_browser/app_bar/certificates_info_popup.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:provider/provider.dart';\n\nimport '../custom_popup_dialog.dart';\n\nclass UrlInfoPopup extends StatefulWidget {\n  final CustomPopupDialogPageRoute route;\n  final Duration transitionDuration;\n  final Function()? onWebViewTabSettingsClicked;\n\n  const UrlInfoPopup(\n      {super.key,\n      required this.route,\n      required this.transitionDuration,\n      this.onWebViewTabSettingsClicked});\n\n  @override\n  State<UrlInfoPopup> createState() => _UrlInfoPopupState();\n}\n\nclass _UrlInfoPopupState extends State<UrlInfoPopup> {\n  var text1 = \"Your connection to this website is not protected\";\n  var text2 =\n      \"You should not enter sensitive data on this site (e.g. passwords or credit cards) because they could be intercepted by malicious users.\";\n\n  var showFullInfoUrl = false;\n  var defaultTextSpanStyle = const TextStyle(\n    color: Colors.black54,\n    fontSize: 12.5,\n  );\n\n  @override\n  Widget build(BuildContext context) {\n    var webViewModel = Provider.of<WebViewModel>(context, listen: true);\n    if (webViewModel.isSecure) {\n      text1 = \"Your connection is protected\";\n      text2 =\n          \"Your sensitive data (e.g. passwords or credit card numbers) remains private when it is sent to this site.\";\n    }\n    var url = webViewModel.url;\n\n    return SafeArea(\n        child: Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        StatefulBuilder(\n          builder: (context, setState) {\n            return GestureDetector(\n              onTap: () {\n                setState(() {\n                  showFullInfoUrl = !showFullInfoUrl;\n                });\n              },\n              child: Container(\n                  padding: const EdgeInsets.only(bottom: 15.0),\n                  constraints: const BoxConstraints(maxHeight: 100.0),\n                  child: RichText(\n                    maxLines: showFullInfoUrl ? null : 2,\n                    overflow: showFullInfoUrl\n                        ? TextOverflow.clip\n                        : TextOverflow.ellipsis,\n                    text: TextSpan(\n                      children: <TextSpan>[\n                        TextSpan(\n                            text: url?.scheme,\n                            style: defaultTextSpanStyle.copyWith(\n                                color: webViewModel.isSecure\n                                    ? Colors.green\n                                    : Colors.black54,\n                                fontWeight: FontWeight.bold)),\n                        TextSpan(\n                            text: webViewModel.url?.toString() == \"about:blank\"\n                                ? ':'\n                                : '://',\n                            style: defaultTextSpanStyle),\n                        TextSpan(\n                            text: url?.host,\n                            style: defaultTextSpanStyle.copyWith(\n                                color: Colors.black)),\n                        TextSpan(text: url?.path, style: defaultTextSpanStyle),\n                        TextSpan(text: url?.query, style: defaultTextSpanStyle),\n                      ],\n                    ),\n                  )),\n            );\n          },\n        ),\n        Container(\n          padding: const EdgeInsets.only(bottom: 10.0),\n          child: Text(text1,\n              style: const TextStyle(\n                fontSize: 16.0,\n              )),\n        ),\n        RichText(\n            text: TextSpan(\n                style: const TextStyle(fontSize: 12.0, color: Colors.black87),\n                children: [\n              TextSpan(\n                text: \"$text2 \",\n              ),\n              TextSpan(\n                text: \"Details\",\n                style: const TextStyle(color: Colors.blue),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () async {\n                    Navigator.maybePop(context);\n\n                    await widget.route.popped;\n\n                    await Future.delayed(Duration(\n                        milliseconds:\n                            widget.transitionDuration.inMilliseconds - 200));\n\n                    showDialog(\n                      context: context,\n                      builder: (context) {\n                        return const CertificateInfoPopup();\n                      },\n                    );\n                  },\n              ),\n            ])),\n        const SizedBox(\n          height: 30.0,\n        ),\n        Align(\n          alignment: Alignment.centerRight,\n          child: ElevatedButton(\n            child: const Text(\n              \"WebView Tab Settings\",\n            ),\n            onPressed: () async {\n              Navigator.maybePop(context);\n\n              await widget.route.popped;\n\n              Future.delayed(widget.transitionDuration, () {\n                if (widget.onWebViewTabSettingsClicked != null) {\n                  widget.onWebViewTabSettingsClicked!();\n                }\n              });\n            },\n          ),\n        ),\n      ],\n    ));\n  }\n}\n"
  },
  {
    "path": "lib/app_bar/webview_tab_app_bar.dart",
    "content": "// import 'package:cached_network_image/cached_network_image.dart';\nimport 'dart:io';\n\nimport 'package:collection/collection.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/app_bar/url_info_popup.dart';\nimport 'package:flutter_browser/custom_image.dart';\nimport 'package:flutter_browser/main.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/favorite_model.dart';\nimport 'package:flutter_browser/models/web_archive_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/pages/developers/main.dart';\nimport 'package:flutter_browser/pages/settings/main.dart';\nimport 'package:flutter_browser/tab_popup_menu_actions.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_font_icons/flutter_font_icons.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:provider/provider.dart';\nimport 'package:share_plus/share_plus.dart';\n\nimport '../animated_flutter_browser_logo.dart';\nimport '../custom_popup_dialog.dart';\nimport '../custom_popup_menu_item.dart';\nimport '../models/window_model.dart';\nimport '../popup_menu_actions.dart';\nimport '../project_info_popup.dart';\nimport '../webview_tab.dart';\n\nclass WebViewTabAppBar extends StatefulWidget {\n  final void Function()? showFindOnPage;\n\n  const WebViewTabAppBar({super.key, this.showFindOnPage});\n\n  @override\n  State<WebViewTabAppBar> createState() => _WebViewTabAppBarState();\n}\n\nclass _WebViewTabAppBarState extends State<WebViewTabAppBar>\n    with SingleTickerProviderStateMixin {\n  TextEditingController? _searchController = TextEditingController();\n  FocusNode? _focusNode;\n\n  GlobalKey tabInkWellKey = GlobalKey();\n\n  Duration customPopupDialogTransitionDuration =\n      const Duration(milliseconds: 300);\n  CustomPopupDialogPageRoute? route;\n\n  OutlineInputBorder outlineBorder = const OutlineInputBorder(\n    borderSide: BorderSide(color: Colors.transparent, width: 0.0),\n    borderRadius: BorderRadius.all(\n      Radius.circular(50.0),\n    ),\n  );\n\n  bool shouldSelectText = true;\n\n  @override\n  void initState() {\n    super.initState();\n    _focusNode = FocusNode();\n    _focusNode?.addListener(() async {\n      if (_focusNode != null &&\n          !_focusNode!.hasFocus &&\n          _searchController != null &&\n          _searchController!.text.isEmpty) {\n        final windowModel = Provider.of<WindowModel>(context, listen: false);\n        final webViewModel = windowModel.getCurrentTab()?.webViewModel;\n        var webViewController = webViewModel?.webViewController;\n        _searchController!.text =\n            (await webViewController?.getUrl())?.toString() ?? \"\";\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    _focusNode?.dispose();\n    _focusNode = null;\n    _searchController?.dispose();\n    _searchController = null;\n    super.dispose();\n  }\n\n  int _prevTabIndex = -1;\n\n  @override\n  Widget build(BuildContext context) {\n    return Selector<WebViewModel, ({WebUri? item1, int? item2})>(\n        selector: (context, webViewModel) => (item1: webViewModel.url, item2: webViewModel.tabIndex),\n        builder: (context, record, child) {\n          if (_prevTabIndex != record.item2) {\n            _searchController?.text = record.item1?.toString() ?? '';\n            _prevTabIndex = record.item2 ?? _prevTabIndex;\n            _focusNode?.unfocus();\n          } else {\n            if (record.item1 == null) {\n              _searchController?.text = \"\";\n            }\n            if (record.item1 != null && _focusNode != null &&\n                !_focusNode!.hasFocus) {\n              _searchController?.text = record.item1.toString();\n            }\n          }\n\n          Widget? leading = _buildAppBarHomePageWidget();\n\n          return Selector<WebViewModel, bool>(\n              selector: (context, webViewModel) => webViewModel.isIncognitoMode,\n              builder: (context, isIncognitoMode, child) {\n                return leading != null\n                    ? AppBar(\n                  backgroundColor: isIncognitoMode\n                      ? Colors.black38\n                      : Theme.of(context).colorScheme.primaryContainer,\n                  leading: leading,\n                  leadingWidth: 130,\n                  titleSpacing: 0.0,\n                  title: _buildSearchTextField(),\n                  actions: _buildActionsMenu(),\n                )\n                    : AppBar(\n                  backgroundColor: isIncognitoMode\n                      ? Colors.black38\n                      : Theme.of(context).colorScheme.primaryContainer,\n                  titleSpacing: 10.0,\n                  title: _buildSearchTextField(),\n                  actions: _buildActionsMenu(),\n                );\n              });\n        });\n  }\n\n  Widget? _buildAppBarHomePageWidget() {\n    var browserModel = Provider.of<BrowserModel>(context, listen: true);\n    var settings = browserModel.getSettings();\n\n    var webViewModel = Provider.of<WebViewModel>(context, listen: true);\n\n    if (Util.isMobile() && !settings.homePageEnabled) {\n      return null;\n    }\n\n    final children = <Widget>[];\n    if (Util.isDesktop()) {\n      children.addAll([\n        IconButton(\n          icon: const Icon(\n            Icons.arrow_back,\n            size: 20,\n          ),\n          constraints: const BoxConstraints(\n            maxWidth: 30,\n            minWidth: 30,\n            maxHeight: 30,\n            minHeight: 30,\n          ),\n          padding: EdgeInsets.zero,\n          onPressed: () async {\n            webViewModel.webViewController?.goBack();\n          },\n        ),\n        IconButton(\n          icon: const Icon(\n            Icons.arrow_forward,\n            size: 20,\n          ),\n          constraints: const BoxConstraints(\n            maxWidth: 30,\n            minWidth: 30,\n            maxHeight: 30,\n            minHeight: 30,\n          ),\n          padding: EdgeInsets.zero,\n          onPressed: () async {\n            webViewModel.webViewController?.goForward();\n          },\n        ),\n        IconButton(\n          icon: const Icon(\n            Icons.refresh,\n            size: 20,\n          ),\n          constraints: const BoxConstraints(\n            maxWidth: 30,\n            minWidth: 30,\n            maxHeight: 30,\n            minHeight: 30,\n          ),\n          padding: EdgeInsets.zero,\n          onPressed: () async {\n            webViewModel.webViewController?.reload();\n          },\n        )\n      ]);\n    }\n    if (settings.homePageEnabled || Util.isDesktop()) {\n      children.add(IconButton(\n        icon: const Icon(\n          Icons.home,\n          size: 20,\n        ),\n        constraints: const BoxConstraints(\n          maxWidth: 30,\n          minWidth: 30,\n          maxHeight: 30,\n          minHeight: 30,\n        ),\n        padding: EdgeInsets.zero,\n        onPressed: () {\n          if (webViewModel.webViewController != null) {\n            var url = settings.homePageEnabled &&\n                    settings.customUrlHomePage.isNotEmpty\n                ? WebUri(settings.customUrlHomePage)\n                : WebUri(settings.searchEngine.url);\n            webViewModel.webViewController\n                ?.loadUrl(urlRequest: URLRequest(url: url));\n          } else {\n            addNewTab();\n          }\n        },\n      ));\n    }\n\n    return Container(\n      margin: const EdgeInsets.symmetric(horizontal: 5),\n      child: Row(\n        children: children,\n      ),\n    );\n  }\n\n  Widget _buildSearchTextField() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: true);\n    final settings = browserModel.getSettings();\n\n    final webViewModel = Provider.of<WebViewModel>(context, listen: true);\n\n    return SizedBox(\n      height: 40.0,\n      child: Stack(\n        children: <Widget>[\n          TextField(\n            onSubmitted: (value) {\n              var url = WebUri(value.trim());\n              if (Util.isLocalizedContent(url) ||\n                  (url.isValidUri && url.toString().split(\".\").length > 1)) {\n                url = url.scheme.isEmpty ? WebUri(\"https://$url\") : url;\n              } else {\n                url = WebUri(settings.searchEngine.searchUrl + value);\n              }\n\n              if (webViewModel.webViewController != null) {\n                webViewModel.webViewController\n                    ?.loadUrl(urlRequest: URLRequest(url: url));\n              } else {\n                addNewTab(url: url);\n                webViewModel.url = url;\n              }\n            },\n            onTap: () {\n              if (!shouldSelectText ||\n                  _searchController == null ||\n                  _searchController!.text.isEmpty) return;\n              shouldSelectText = false;\n              _searchController!.selection = TextSelection(\n                  baseOffset: 0, extentOffset: _searchController!.text.length);\n            },\n            onTapOutside: (event) {\n              shouldSelectText = true;\n            },\n            keyboardType: TextInputType.url,\n            focusNode: _focusNode,\n            autofocus: false,\n            controller: _searchController,\n            textInputAction: TextInputAction.go,\n            decoration: InputDecoration(\n              contentPadding: const EdgeInsets.only(\n                  left: 45.0, top: 10.0, right: 10.0, bottom: 10.0),\n              filled: true,\n              fillColor: Colors.white,\n              border: outlineBorder,\n              focusedBorder: outlineBorder,\n              enabledBorder: outlineBorder,\n              hintText: \"Search for or type a web address\",\n              hintStyle: const TextStyle(color: Colors.black54, fontSize: 16.0),\n            ),\n            style: const TextStyle(color: Colors.black, fontSize: 16.0),\n          ),\n          IconButton(\n            icon: Selector<WebViewModel, bool>(\n              selector: (context, webViewModel) => webViewModel.isSecure,\n              builder: (context, isSecure, child) {\n                var icon = Icons.info_outline;\n                if (webViewModel.isIncognitoMode) {\n                  icon = MaterialCommunityIcons.incognito;\n                } else if (isSecure) {\n                  if (webViewModel.url != null &&\n                      webViewModel.url!.scheme == \"file\") {\n                    icon = Icons.offline_pin;\n                  } else {\n                    icon = Icons.lock;\n                  }\n                }\n\n                return Icon(\n                  icon,\n                  color: isSecure ? Colors.green : Colors.grey,\n                );\n              },\n            ),\n            onPressed: () {\n              showUrlInfo();\n            },\n          ),\n        ],\n      ),\n    );\n  }\n\n  List<Widget> _buildActionsMenu() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: true);\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    final settings = browserModel.getSettings();\n\n    return [\n      settings.homePageEnabled\n          ? const SizedBox(\n              width: 10.0,\n            )\n          : Container(),\n      Util.isDesktop()\n          ? null\n          : InkWell(\n              key: tabInkWellKey,\n              onLongPress: () {\n                final RenderBox? box = tabInkWellKey.currentContext!\n                    .findRenderObject() as RenderBox?;\n                if (box == null) {\n                  return;\n                }\n\n                Offset position = box.localToGlobal(Offset.zero);\n\n                showMenu(\n                        context: context,\n                        position: RelativeRect.fromLTRB(position.dx,\n                            position.dy + box.size.height, box.size.width, 0),\n                        items: TabPopupMenuActions.choices\n                            .map((tabPopupMenuAction) {\n                          IconData? iconData;\n                          switch (tabPopupMenuAction) {\n                            case TabPopupMenuActions.CLOSE_TABS:\n                              iconData = Icons.cancel;\n                              break;\n                            case TabPopupMenuActions.NEW_TAB:\n                              iconData = Icons.add;\n                              break;\n                            case TabPopupMenuActions.NEW_INCOGNITO_TAB:\n                              iconData = MaterialCommunityIcons.incognito;\n                              break;\n                          }\n\n                          return PopupMenuItem<String>(\n                            value: tabPopupMenuAction,\n                            child: Row(children: [\n                              Icon(\n                                iconData,\n                                color: Colors.black,\n                              ),\n                              Container(\n                                padding: const EdgeInsets.only(left: 10.0),\n                                child: Text(tabPopupMenuAction),\n                              )\n                            ]),\n                          );\n                        }).toList())\n                    .then((value) {\n                  switch (value) {\n                    case TabPopupMenuActions.CLOSE_TABS:\n                      windowModel.closeAllTabs();\n                      break;\n                    case TabPopupMenuActions.NEW_TAB:\n                      addNewTab();\n                      break;\n                    case TabPopupMenuActions.NEW_INCOGNITO_TAB:\n                      addNewIncognitoTab();\n                      break;\n                  }\n                });\n              },\n              onTap: () async {\n                if (windowModel.webViewTabs.isNotEmpty) {\n                  var webViewModel = windowModel.getCurrentTab()?.webViewModel;\n                  var webViewController = webViewModel?.webViewController;\n\n                  if (View.of(context).viewInsets.bottom > 0.0) {\n                    SystemChannels.textInput.invokeMethod('TextInput.hide');\n                    if (FocusManager.instance.primaryFocus != null) {\n                      FocusManager.instance.primaryFocus!.unfocus();\n                    }\n                    if (webViewController != null) {\n                      await webViewController.evaluateJavascript(\n                          source: \"document.activeElement.blur();\");\n                    }\n                    await Future.delayed(const Duration(milliseconds: 300));\n                  }\n\n                  if (webViewModel != null && webViewController != null) {\n                    webViewModel.screenshot = await webViewController\n                        .takeScreenshot(\n                            screenshotConfiguration: ScreenshotConfiguration(\n                                compressFormat: CompressFormat.JPEG,\n                                quality: 20))\n                        .timeout(\n                          const Duration(milliseconds: 1500),\n                          onTimeout: () => null,\n                        );\n                  }\n\n                  browserModel.showTabScroller = true;\n                }\n              },\n              child: Container(\n                margin: const EdgeInsets.only(\n                    left: 10.0, top: 15.0, right: 10.0, bottom: 15.0),\n                decoration: BoxDecoration(\n                    border: Border.all(width: 2.0),\n                    shape: BoxShape.rectangle,\n                    borderRadius: BorderRadius.circular(5.0)),\n                constraints: const BoxConstraints(minWidth: 25.0),\n                child: Center(\n                    child: Text(\n                  windowModel.webViewTabs.length.toString(),\n                  style: const TextStyle(\n                      fontWeight: FontWeight.bold, fontSize: 14.0),\n                )),\n              ),\n            ),\n      const SizedBox.square(\n        dimension: 5,\n      ),\n      PopupMenuButton<String>(\n        icon: const Icon(\n          Icons.more_vert,\n        ),\n        position: PopupMenuPosition.under,\n        onSelected: _popupMenuChoiceAction,\n        itemBuilder: (popupMenuContext) {\n          var items = [\n            CustomPopupMenuItem<String>(\n              enabled: true,\n              isIconButtonRow: true,\n              child: StatefulBuilder(\n                builder: (statefulContext, setState) {\n                  var browserModel =\n                      Provider.of<BrowserModel>(statefulContext, listen: true);\n                  var webViewModel =\n                      Provider.of<WebViewModel>(statefulContext, listen: true);\n\n                  var isFavorite = false;\n                  FavoriteModel? favorite;\n\n                  if (webViewModel.url != null &&\n                      webViewModel.url!.toString().isNotEmpty) {\n                    favorite = FavoriteModel(\n                        url: webViewModel.url,\n                        title: webViewModel.title ?? \"\",\n                        favicon: webViewModel.favicon);\n                    isFavorite = browserModel.containsFavorite(favorite);\n                  }\n\n                  var children = <Widget>[];\n\n                  if (Util.isIOS() || Util.isMacOS() || Util.isWindows()) {\n                    children.add(\n                      SizedBox(\n                          width: 35.0,\n                          child: IconButton(\n                              padding: const EdgeInsets.all(0.0),\n                              icon: const Icon(\n                                Icons.arrow_back,\n                                color: Colors.black,\n                              ),\n                              onPressed: () {\n                                webViewModel.webViewController?.goBack();\n                                Navigator.pop(popupMenuContext);\n                              })),\n                    );\n                  }\n\n                  children.addAll([\n                    SizedBox(\n                        width: 35.0,\n                        child: IconButton(\n                            padding: const EdgeInsets.all(0.0),\n                            icon: const Icon(\n                              Icons.arrow_forward,\n                              color: Colors.black,\n                            ),\n                            onPressed: () {\n                              webViewModel.webViewController?.goForward();\n                              Navigator.pop(popupMenuContext);\n                            })),\n                    SizedBox(\n                        width: 35.0,\n                        child: IconButton(\n                            padding: const EdgeInsets.all(0.0),\n                            icon: Icon(\n                              isFavorite ? Icons.star : Icons.star_border,\n                              color: Colors.black,\n                            ),\n                            onPressed: () {\n                              setState(() {\n                                if (favorite != null) {\n                                  if (!browserModel\n                                      .containsFavorite(favorite)) {\n                                    browserModel.addFavorite(favorite);\n                                  } else if (browserModel\n                                      .containsFavorite(favorite)) {\n                                    browserModel.removeFavorite(favorite);\n                                  }\n                                }\n                              });\n                            })),\n                    SizedBox(\n                        width: 35.0,\n                        child: IconButton(\n                            padding: const EdgeInsets.all(0.0),\n                            icon: const Icon(\n                              Icons.file_download,\n                              color: Colors.black,\n                            ),\n                            onPressed: () async {\n                              Navigator.pop(popupMenuContext);\n                              if (webViewModel.url != null &&\n                                  webViewModel.url!.scheme.startsWith(\"http\")) {\n                                var url = webViewModel.url;\n                                if (url == null) {\n                                  return;\n                                }\n\n                                String webArchivePath =\n                                    \"$WEB_ARCHIVE_DIR${Platform.pathSeparator}${url.scheme}-${url.host}${url.path.replaceAll(\"/\", \"-\")}${DateTime.now().microsecondsSinceEpoch}.${Util.isAndroid() ? WebArchiveFormat.MHT.toValue() : WebArchiveFormat.WEBARCHIVE.toValue()}\";\n\n                                String? savedPath = (await webViewModel\n                                    .webViewController\n                                    ?.saveWebArchive(\n                                        filePath: webArchivePath,\n                                        autoname: false));\n\n                                var webArchiveModel = WebArchiveModel(\n                                    url: url,\n                                    path: savedPath,\n                                    title: webViewModel.title,\n                                    favicon: webViewModel.favicon,\n                                    timestamp: DateTime.now());\n\n                                if (savedPath != null) {\n                                  browserModel.addWebArchive(\n                                      url.toString(), webArchiveModel);\n                                  if (mounted) {\n                                    ScaffoldMessenger.of(context)\n                                        .showSnackBar(SnackBar(\n                                      content: Text(\n                                          \"${webViewModel.url} saved offline!\"),\n                                    ));\n                                  }\n                                  browserModel.save();\n                                } else {\n                                  if (mounted) {\n                                    ScaffoldMessenger.of(context)\n                                        .showSnackBar(const SnackBar(\n                                      content: Text(\"Unable to save!\"),\n                                    ));\n                                  }\n                                }\n                              }\n                            })),\n                    SizedBox(\n                        width: 35.0,\n                        child: IconButton(\n                            padding: const EdgeInsets.all(0.0),\n                            icon: const Icon(\n                              Icons.info_outline,\n                              color: Colors.black,\n                            ),\n                            onPressed: () async {\n                              Navigator.pop(popupMenuContext);\n\n                              await route?.completed;\n                              showUrlInfo();\n                            })),\n                    SizedBox(\n                        width: 35.0,\n                        child: IconButton(\n                            padding: const EdgeInsets.all(0.0),\n                            icon: const Icon(\n                              MaterialCommunityIcons.cellphone_screenshot,\n                              color: Colors.black,\n                            ),\n                            onPressed: () async {\n                              Navigator.pop(popupMenuContext);\n\n                              await route?.completed;\n\n                              takeScreenshotAndShow();\n                            })),\n                    SizedBox(\n                        width: 35.0,\n                        child: IconButton(\n                            padding: const EdgeInsets.all(0.0),\n                            icon: const Icon(\n                              Icons.refresh,\n                              color: Colors.black,\n                            ),\n                            onPressed: () {\n                              webViewModel.webViewController?.reload();\n                              Navigator.pop(popupMenuContext);\n                            })),\n                  ]);\n\n                  return Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    mainAxisSize: MainAxisSize.max,\n                    children: children,\n                  );\n                },\n              ),\n            )\n          ];\n\n          items.addAll(PopupMenuActions.choices.map((choice) {\n            switch (choice) {\n              case PopupMenuActions.OPEN_NEW_WINDOW:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.open_in_new,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.SAVE_WINDOW:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        Selector<WindowModel, bool>(\n                          selector: (context, windowModel) =>\n                              windowModel.shouldSave,\n                          builder: (context, value, child) {\n                            return Icon(\n                              value\n                                  ? Icons.check_box\n                                  : Icons.check_box_outline_blank,\n                              color: Colors.black,\n                            );\n                          },\n                        )\n                      ]),\n                );\n              case PopupMenuActions.SAVED_WINDOWS:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.window,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.NEW_TAB:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.add,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.NEW_INCOGNITO_TAB:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          MaterialCommunityIcons.incognito,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.FAVORITES:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.star,\n                          color: Colors.yellow,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.WEB_ARCHIVES:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.offline_pin,\n                          color: Colors.blue,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.DESKTOP_MODE:\n                return CustomPopupMenuItem<String>(\n                  enabled: windowModel.getCurrentTab() != null,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        Selector<WebViewModel, bool>(\n                          selector: (context, webViewModel) =>\n                              webViewModel.isDesktopMode,\n                          builder: (context, value, child) {\n                            return Icon(\n                              value\n                                  ? Icons.check_box\n                                  : Icons.check_box_outline_blank,\n                              color: Colors.black,\n                            );\n                          },\n                        )\n                      ]),\n                );\n              case PopupMenuActions.HISTORY:\n                return CustomPopupMenuItem<String>(\n                  enabled: windowModel.getCurrentTab() != null,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.history,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.SHARE:\n                return CustomPopupMenuItem<String>(\n                  enabled: windowModel.getCurrentTab() != null,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Ionicons.logo_whatsapp,\n                          color: Colors.green,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.SETTINGS:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.settings,\n                          color: Colors.grey,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.DEVELOPERS:\n                return CustomPopupMenuItem<String>(\n                  enabled: windowModel.getCurrentTab() != null,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.developer_mode,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.FIND_ON_PAGE:\n                return CustomPopupMenuItem<String>(\n                  enabled: windowModel.getCurrentTab() != null,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        const Icon(\n                          Icons.search,\n                          color: Colors.black,\n                        )\n                      ]),\n                );\n              case PopupMenuActions.INAPPWEBVIEW_PROJECT:\n                return CustomPopupMenuItem<String>(\n                  enabled: true,\n                  value: choice,\n                  child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(choice),\n                        Container(\n                          padding: const EdgeInsets.only(right: 6),\n                          child: const AnimatedFlutterBrowserLogo(\n                            size: 12.5,\n                          ),\n                        )\n                      ]),\n                );\n              default:\n                return CustomPopupMenuItem<String>(\n                  value: choice,\n                  child: Text(choice),\n                );\n            }\n          }).toList());\n\n          return items;\n        },\n      )\n    ].whereNotNull().toList();\n  }\n\n  void _popupMenuChoiceAction(String choice) async {\n    var currentWebViewModel = Provider.of<WebViewModel>(context, listen: false);\n\n    switch (choice) {\n      case PopupMenuActions.OPEN_NEW_WINDOW:\n        openNewWindow();\n        break;\n      case PopupMenuActions.SAVE_WINDOW:\n        setShouldSave();\n        break;\n      case PopupMenuActions.SAVED_WINDOWS:\n        showSavedWindows();\n        break;\n      case PopupMenuActions.NEW_TAB:\n        addNewTab();\n        break;\n      case PopupMenuActions.NEW_INCOGNITO_TAB:\n        addNewIncognitoTab();\n        break;\n      case PopupMenuActions.FAVORITES:\n        showFavorites();\n        break;\n      case PopupMenuActions.HISTORY:\n        showHistory();\n        break;\n      case PopupMenuActions.WEB_ARCHIVES:\n        showWebArchives();\n        break;\n      case PopupMenuActions.FIND_ON_PAGE:\n        var isFindInteractionEnabled =\n            currentWebViewModel.settings?.isFindInteractionEnabled ?? false;\n        var findInteractionController =\n            currentWebViewModel.findInteractionController;\n        if ((Util.isIOS() || Util.isMacOS()) &&\n            isFindInteractionEnabled &&\n            findInteractionController != null) {\n          await findInteractionController.presentFindNavigator();\n        } else if (widget.showFindOnPage != null) {\n          widget.showFindOnPage!();\n        }\n        break;\n      case PopupMenuActions.SHARE:\n        share();\n        break;\n      case PopupMenuActions.DESKTOP_MODE:\n        toggleDesktopMode();\n        break;\n      case PopupMenuActions.DEVELOPERS:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          goToDevelopersPage();\n        });\n        break;\n      case PopupMenuActions.SETTINGS:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          goToSettingsPage();\n        });\n        break;\n      case PopupMenuActions.INAPPWEBVIEW_PROJECT:\n        Future.delayed(const Duration(milliseconds: 300), () {\n          openProjectPopup();\n        });\n        break;\n    }\n  }\n\n  void addNewTab({WebUri? url}) {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final settings = browserModel.getSettings();\n\n    url ??= settings.homePageEnabled && settings.customUrlHomePage.isNotEmpty\n        ? WebUri(settings.customUrlHomePage)\n        : WebUri(settings.searchEngine.url);\n\n    windowModel.addTab(WebViewTab(\n      key: GlobalKey(),\n      webViewModel: WebViewModel(url: url),\n    ));\n  }\n\n  void addNewIncognitoTab({WebUri? url}) {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final settings = browserModel.getSettings();\n\n    url ??= settings.homePageEnabled && settings.customUrlHomePage.isNotEmpty\n        ? WebUri(settings.customUrlHomePage)\n        : WebUri(settings.searchEngine.url);\n\n    windowModel.addTab(WebViewTab(\n      key: GlobalKey(),\n      webViewModel: WebViewModel(url: url, isIncognitoMode: true),\n    ));\n  }\n\n  void showSavedWindows() {\n    showDialog(\n        context: context,\n        builder: (context) {\n          final browserModel = Provider.of<BrowserModel>(context, listen: true);\n\n          return AlertDialog(\n              contentPadding: const EdgeInsets.all(0.0),\n              content: SizedBox(\n                  width: double.maxFinite,\n                  child: StatefulBuilder(\n                    builder: (context, setState) {\n                      return FutureBuilder(\n                        future: browserModel.getWindows(),\n                        builder: (context, snapshot) {\n                          final savedWindows = (snapshot.data ?? []);\n                          savedWindows.sortBy(\n                            (e) => e.updatedTime,\n                          );\n                          return ListView(\n                            children: savedWindows.map((window) {\n                              return ListTile(\n                                title: Text(\n                                    window.name.isNotEmpty\n                                        ? window.name\n                                        : window.id,\n                                    maxLines: 2,\n                                    overflow: TextOverflow.ellipsis),\n                                onTap: () async {\n                                  await browserModel.openWindow(window);\n                                  setState(() {\n                                    Navigator.pop(context);\n                                  });\n                                },\n                                trailing: Row(\n                                  mainAxisSize: MainAxisSize.min,\n                                  children: <Widget>[\n                                    IconButton(\n                                      icon: const Icon(Icons.close, size: 20.0),\n                                      onPressed: () async {\n                                        await browserModel.removeWindow(window);\n                                        setState(() {\n                                          if (savedWindows.isEmpty ||\n                                              savedWindows.length == 1) {\n                                            Navigator.pop(context);\n                                          }\n                                        });\n                                      },\n                                    )\n                                  ],\n                                ),\n                              );\n                            }).toList(),\n                          );\n                        },\n                      );\n                    },\n                  )));\n        });\n  }\n\n  void showFavorites() {\n    showDialog(\n        context: context,\n        builder: (context) {\n          var browserModel = Provider.of<BrowserModel>(context, listen: true);\n\n          return AlertDialog(\n              contentPadding: const EdgeInsets.all(0.0),\n              content: SizedBox(\n                  width: double.maxFinite,\n                  child: ListView(\n                    children: browserModel.favorites.map((favorite) {\n                      var url = favorite.url;\n                      var faviconUrl = favorite.favicon != null\n                          ? favorite.favicon!.url\n                          : WebUri(\"${url?.origin ?? \"\"}/favicon.ico\");\n\n                      return ListTile(\n                        leading: Column(\n                          mainAxisAlignment: MainAxisAlignment.center,\n                          children: <Widget>[\n                            // CachedNetworkImage(\n                            //   placeholder: (context, url) =>\n                            //       CircularProgressIndicator(),\n                            //   imageUrl: faviconUrl,\n                            //   height: 30,\n                            // )\n                            CustomImage(\n                              url: faviconUrl,\n                              maxWidth: 30.0,\n                              height: 30.0,\n                            )\n                          ],\n                        ),\n                        title: Text(\n                            favorite.title ?? favorite.url?.toString() ?? \"\",\n                            maxLines: 2,\n                            overflow: TextOverflow.ellipsis),\n                        subtitle: Text(favorite.url?.toString() ?? \"\",\n                            maxLines: 2, overflow: TextOverflow.ellipsis),\n                        isThreeLine: true,\n                        onTap: () {\n                          setState(() {\n                            addNewTab(url: favorite.url);\n                            Navigator.pop(context);\n                          });\n                        },\n                        trailing: Row(\n                          mainAxisSize: MainAxisSize.min,\n                          children: <Widget>[\n                            IconButton(\n                              icon: const Icon(Icons.close, size: 20.0),\n                              onPressed: () {\n                                setState(() {\n                                  browserModel.removeFavorite(favorite);\n                                  if (browserModel.favorites.isEmpty) {\n                                    Navigator.pop(context);\n                                  }\n                                });\n                              },\n                            )\n                          ],\n                        ),\n                      );\n                    }).toList(),\n                  )));\n        });\n  }\n\n  void showHistory() {\n    showDialog(\n        context: context,\n        builder: (context) {\n          var webViewModel = Provider.of<WebViewModel>(context, listen: true);\n\n          return AlertDialog(\n              contentPadding: const EdgeInsets.all(0.0),\n              content: FutureBuilder(\n                future:\n                    webViewModel.webViewController?.getCopyBackForwardList(),\n                builder: (context, snapshot) {\n                  if (!snapshot.hasData) {\n                    return Container();\n                  }\n\n                  WebHistory history = snapshot.data as WebHistory;\n                  return SizedBox(\n                      width: double.maxFinite,\n                      child: ListView(\n                        children: history.list?.reversed.map((historyItem) {\n                              var url = historyItem.url;\n\n                              return ListTile(\n                                leading: Column(\n                                  mainAxisAlignment: MainAxisAlignment.center,\n                                  children: <Widget>[\n                                    // CachedNetworkImage(\n                                    //   placeholder: (context, url) =>\n                                    //       CircularProgressIndicator(),\n                                    //   imageUrl: (url?.origin ?? \"\") + \"/favicon.ico\",\n                                    //   height: 30,\n                                    // )\n                                    CustomImage(\n                                        url: WebUri(\n                                            \"${url?.origin ?? \"\"}/favicon.ico\"),\n                                        maxWidth: 30.0,\n                                        height: 30.0)\n                                  ],\n                                ),\n                                title: Text(historyItem.title ?? url.toString(),\n                                    maxLines: 2,\n                                    overflow: TextOverflow.ellipsis),\n                                subtitle: Text(url?.toString() ?? \"\",\n                                    maxLines: 2,\n                                    overflow: TextOverflow.ellipsis),\n                                isThreeLine: true,\n                                onTap: () {\n                                  webViewModel.webViewController\n                                      ?.goTo(historyItem: historyItem);\n                                  Navigator.pop(context);\n                                },\n                              );\n                            }).toList() ??\n                            <Widget>[],\n                      ));\n                },\n              ));\n        });\n  }\n\n  void showWebArchives() async {\n    showDialog(\n        context: context,\n        builder: (context) {\n          var browserModel = Provider.of<BrowserModel>(context, listen: true);\n          var webArchives = browserModel.webArchives;\n\n          var listViewChildren = <Widget>[];\n          webArchives.forEach((key, webArchive) {\n            var path = webArchive.path;\n            // String fileName = path.substring(path.lastIndexOf('/') + 1);\n\n            var url = webArchive.url;\n\n            listViewChildren.add(ListTile(\n              leading: Column(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  // CachedNetworkImage(\n                  //   placeholder: (context, url) => CircularProgressIndicator(),\n                  //   imageUrl: (url?.origin ?? \"\") + \"/favicon.ico\",\n                  //   height: 30,\n                  // )\n                  CustomImage(\n                      url: WebUri(\"${url?.origin ?? \"\"}/favicon.ico\"),\n                      maxWidth: 30.0,\n                      height: 30.0)\n                ],\n              ),\n              title: Text(webArchive.title ?? url?.toString() ?? \"\",\n                  maxLines: 2, overflow: TextOverflow.ellipsis),\n              subtitle: Text(url?.toString() ?? \"\",\n                  maxLines: 2, overflow: TextOverflow.ellipsis),\n              trailing: IconButton(\n                icon: const Icon(Icons.delete),\n                onPressed: () async {\n                  setState(() {\n                    browserModel.removeWebArchive(webArchive);\n                    browserModel.save();\n                  });\n                },\n              ),\n              isThreeLine: true,\n              onTap: () {\n                if (path != null) {\n                  final windowModel =\n                      Provider.of<WindowModel>(context, listen: false);\n                  windowModel.addTab(WebViewTab(\n                    key: GlobalKey(),\n                    webViewModel: WebViewModel(url: WebUri(\"file://$path\")),\n                  ));\n                }\n                Navigator.pop(context);\n              },\n            ));\n          });\n\n          return AlertDialog(\n              contentPadding: const EdgeInsets.all(0.0),\n              content: Builder(\n                builder: (context) {\n                  return SizedBox(\n                      width: double.maxFinite,\n                      child: ListView(\n                        children: listViewChildren,\n                      ));\n                },\n              ));\n        });\n  }\n\n  void share() {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final webViewModel = windowModel.getCurrentTab()?.webViewModel;\n    final url = webViewModel?.url;\n    if (url != null) {\n      Share.share(url.toString(), subject: webViewModel?.title);\n    }\n  }\n\n  void openNewWindow() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    browserModel.openWindow(null);\n  }\n\n  void setShouldSave() {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    windowModel.shouldSave = !windowModel.shouldSave;\n  }\n\n  void toggleDesktopMode() async {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final webViewModel = windowModel.getCurrentTab()?.webViewModel;\n    final webViewController = webViewModel?.webViewController;\n\n    final currentWebViewModel =\n        Provider.of<WebViewModel>(context, listen: false);\n\n    if (webViewController != null) {\n      webViewModel?.isDesktopMode = !webViewModel.isDesktopMode;\n      currentWebViewModel.isDesktopMode = webViewModel?.isDesktopMode ?? false;\n\n      final currentSettings = await webViewController.getSettings();\n      if (currentSettings != null) {\n        currentSettings.preferredContentMode =\n            webViewModel?.isDesktopMode ?? false\n                ? UserPreferredContentMode.DESKTOP\n                : UserPreferredContentMode.RECOMMENDED;\n        await webViewController.setSettings(settings: currentSettings);\n      }\n      await webViewController.reload();\n    }\n  }\n\n  void showUrlInfo() {\n    var webViewModel = Provider.of<WebViewModel>(context, listen: false);\n    var url = webViewModel.url;\n    if (url == null || url.toString().isEmpty) {\n      return;\n    }\n\n    route = CustomPopupDialog.show(\n      context: context,\n      transitionDuration: customPopupDialogTransitionDuration,\n      builder: (context) {\n        return UrlInfoPopup(\n          route: route!,\n          transitionDuration: customPopupDialogTransitionDuration,\n          onWebViewTabSettingsClicked: () {\n            goToSettingsPage();\n          },\n        );\n      },\n    );\n  }\n\n  void goToDevelopersPage() {\n    Navigator.push(context,\n        MaterialPageRoute(builder: (context) => const DevelopersPage()));\n  }\n\n  void goToSettingsPage() {\n    Navigator.push(\n        context, MaterialPageRoute(builder: (context) => const SettingsPage()));\n  }\n\n  void openProjectPopup() {\n    showGeneralDialog(\n      context: context,\n      barrierDismissible: false,\n      pageBuilder: (context, animation, secondaryAnimation) {\n        return const ProjectInfoPopup();\n      },\n      transitionDuration: const Duration(milliseconds: 300),\n    );\n  }\n\n  void takeScreenshotAndShow() async {\n    var webViewModel = Provider.of<WebViewModel>(context, listen: false);\n    var screenshot = await webViewModel.webViewController?.takeScreenshot();\n\n    if (screenshot != null) {\n      var dir = await getApplicationDocumentsDirectory();\n      File file = File(\n          \"${dir.path}/screenshot_${DateTime.now().microsecondsSinceEpoch}.png\");\n      await file.writeAsBytes(screenshot);\n\n      await showDialog(\n        context: context,\n        builder: (context) {\n          return AlertDialog(\n            content: Image.memory(screenshot),\n            actions: <Widget>[\n              ElevatedButton(\n                child: const Text(\"Share\"),\n                onPressed: () async {\n                  await Share.shareXFiles([XFile(file.path)]);\n                },\n              )\n            ],\n          );\n        },\n      );\n\n      file.delete();\n    }\n  }\n}\n"
  },
  {
    "path": "lib/browser.dart",
    "content": "import 'dart:async';\n\n// import 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/custom_image.dart';\nimport 'package:flutter_browser/tab_viewer.dart';\nimport 'package:flutter_browser/app_bar/browser_app_bar.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_browser/webview_tab.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport 'app_bar/tab_viewer_app_bar.dart';\nimport 'empty_tab.dart';\nimport 'models/browser_model.dart';\nimport 'models/window_model.dart';\n\nclass Browser extends StatefulWidget {\n  const Browser({super.key});\n\n  @override\n  State<Browser> createState() => _BrowserState();\n}\n\nclass _BrowserState extends State<Browser> with SingleTickerProviderStateMixin {\n  static const platform =\n      MethodChannel('com.pichillilorenzo.flutter_browser.intent_data');\n\n  var _isRestored = false;\n\n  @override\n  void initState() {\n    super.initState();\n    getIntentData();\n  }\n\n  getIntentData() async {\n    if (Util.isAndroid()) {\n      String? url = await platform.invokeMethod(\"getIntentData\");\n      if (url != null) {\n        if (mounted) {\n          final windowModel = Provider.of<WindowModel>(context, listen: false);\n          windowModel.addTab(WebViewTab(\n            key: GlobalKey(),\n            webViewModel: WebViewModel(url: WebUri(url)),\n          ));\n        }\n      }\n    }\n  }\n\n  restore() async {\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    browserModel.restore();\n    windowModel.restoreInfo();\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    if (!_isRestored) {\n      _isRestored = true;\n      restore();\n    }\n    precacheImage(const AssetImage(\"assets/icon/icon.png\"), context);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return _buildBrowser();\n  }\n\n  Widget _buildBrowser() {\n    final currentWebViewModel = Provider.of<WebViewModel>(context, listen: true);\n    final browserModel = Provider.of<BrowserModel>(context, listen: true);\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n\n    browserModel.addListener(() {\n      browserModel.save();\n    });\n    windowModel.addListener(() {\n      windowModel.saveInfo();\n    });\n    currentWebViewModel.addListener(() {\n      windowModel.saveInfo();\n    });\n\n    var canShowTabScroller =\n        browserModel.showTabScroller && windowModel.webViewTabs.isNotEmpty;\n\n    return IndexedStack(\n      index: canShowTabScroller ? 1 : 0,\n      children: [\n        _buildWebViewTabs(),\n        canShowTabScroller ? _buildWebViewTabsViewer() : Container()\n      ],\n    );\n  }\n\n  Widget _buildWebViewTabs() {\n    return WillPopScope(\n        onWillPop: () async {\n          final windowModel = Provider.of<WindowModel>(context, listen: false);\n          final webViewModel = windowModel.getCurrentTab()?.webViewModel;\n          final webViewController = webViewModel?.webViewController;\n\n          if (webViewController != null) {\n            if (await webViewController.canGoBack()) {\n              webViewController.goBack();\n              return false;\n            }\n          }\n\n          if (webViewModel != null && webViewModel.tabIndex != null) {\n            setState(() {\n              windowModel.closeTab(webViewModel.tabIndex!);\n            });\n            if (mounted) {\n              FocusScope.of(context).unfocus();\n            }\n            return false;\n          }\n\n          return windowModel.webViewTabs.isEmpty;\n        },\n        child: Listener(\n          onPointerUp: (_) {\n            if (Util.isIOS() || Util.isAndroid()) {\n              FocusScopeNode currentFocus = FocusScope.of(context);\n              if (!currentFocus.hasPrimaryFocus &&\n                  currentFocus.focusedChild != null) {\n                currentFocus.focusedChild!.unfocus();\n              }\n            }\n          },\n          child: Scaffold(\n              appBar: BrowserAppBar(), body: _buildWebViewTabsContent()),\n        ));\n  }\n\n  Widget _buildWebViewTabsContent() {\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n\n    if (windowModel.webViewTabs.isEmpty) {\n      return const EmptyTab();\n    }\n\n    for (final webViewTab in windowModel.webViewTabs) {\n      var isCurrentTab =\n          webViewTab.webViewModel.tabIndex == windowModel.getCurrentTabIndex();\n\n      if (isCurrentTab) {\n        Future.delayed(const Duration(milliseconds: 100), () {\n          webViewTabStateKey.currentState?.onShowTab();\n        });\n      } else {\n        webViewTabStateKey.currentState?.onHideTab();\n      }\n    }\n\n    var stackChildren = <Widget>[\n      windowModel.getCurrentTab() ?? Container(),\n      _createProgressIndicator()\n    ];\n\n    return Column(\n      children: [\n        Expanded(\n            child: Stack(\n          children: stackChildren,\n        ))\n      ],\n    );\n  }\n\n  Widget _createProgressIndicator() {\n    return Selector<WebViewModel, double>(\n        selector: (context, webViewModel) => webViewModel.progress,\n        builder: (context, progress, child) {\n          if (progress >= 1.0) {\n            return Container();\n          }\n          return PreferredSize(\n              preferredSize: const Size(double.infinity, 4.0),\n              child: SizedBox(\n                  height: 4.0,\n                  child: LinearProgressIndicator(\n                    value: progress,\n                  )));\n        });\n  }\n\n  Widget _buildWebViewTabsViewer() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: true);\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n\n    return WillPopScope(\n        onWillPop: () async {\n          browserModel.showTabScroller = false;\n          return false;\n        },\n        child: Scaffold(\n            appBar: const TabViewerAppBar(),\n            body: TabViewer(\n              currentIndex: windowModel.getCurrentTabIndex(),\n              children: windowModel.webViewTabs.map((webViewTab) {\n                webViewTabStateKey.currentState?.pause();\n                var screenshotData = webViewTab.webViewModel.screenshot;\n                Widget screenshotImage = Container(\n                  decoration: const BoxDecoration(color: Colors.white),\n                  width: double.infinity,\n                  child: screenshotData != null\n                      ? Image.memory(screenshotData)\n                      : null,\n                );\n\n                var url = webViewTab.webViewModel.url;\n                var faviconUrl = webViewTab.webViewModel.favicon != null\n                    ? webViewTab.webViewModel.favicon!.url\n                    : (url != null && [\"http\", \"https\"].contains(url.scheme)\n                        ? Uri.parse(\"${url.origin}/favicon.ico\")\n                        : null);\n\n                var isCurrentTab = windowModel.getCurrentTabIndex() ==\n                    webViewTab.webViewModel.tabIndex;\n\n                return Column(\n                  mainAxisSize: MainAxisSize.min,\n                  children: <Widget>[\n                    Material(\n                      color: isCurrentTab\n                          ? Colors.blue\n                          : (webViewTab.webViewModel.isIncognitoMode\n                              ? Colors.black\n                              : Colors.white),\n                      child: ListTile(\n                        leading: Column(\n                          mainAxisAlignment: MainAxisAlignment.center,\n                          children: <Widget>[\n                            // CachedNetworkImage(\n                            //   placeholder: (context, url) =>\n                            //   url == \"about:blank\"\n                            //       ? Container()\n                            //       : CircularProgressIndicator(),\n                            //   imageUrl: faviconUrl,\n                            //   height: 30,\n                            // )\n                            CustomImage(\n                                url: faviconUrl, maxWidth: 30.0, height: 30.0)\n                          ],\n                        ),\n                        title: Text(\n                            webViewTab.webViewModel.title ??\n                                webViewTab.webViewModel.url?.toString() ??\n                                \"\",\n                            maxLines: 2,\n                            style: TextStyle(\n                              color: webViewTab.webViewModel.isIncognitoMode ||\n                                      isCurrentTab\n                                  ? Colors.white\n                                  : Colors.black,\n                            ),\n                            overflow: TextOverflow.ellipsis),\n                        subtitle:\n                            Text(webViewTab.webViewModel.url?.toString() ?? \"\",\n                                style: TextStyle(\n                                  color:\n                                      webViewTab.webViewModel.isIncognitoMode ||\n                                              isCurrentTab\n                                          ? Colors.white60\n                                          : Colors.black54,\n                                ),\n                                maxLines: 2,\n                                overflow: TextOverflow.ellipsis),\n                        isThreeLine: true,\n                        trailing: Row(\n                          mainAxisSize: MainAxisSize.min,\n                          children: <Widget>[\n                            IconButton(\n                              icon: Icon(\n                                Icons.close,\n                                size: 20.0,\n                                color:\n                                    webViewTab.webViewModel.isIncognitoMode ||\n                                            isCurrentTab\n                                        ? Colors.white60\n                                        : Colors.black54,\n                              ),\n                              onPressed: () {\n                                setState(() {\n                                  if (webViewTab.webViewModel.tabIndex !=\n                                      null) {\n                                    windowModel.closeTab(\n                                        webViewTab.webViewModel.tabIndex!);\n                                    if (windowModel.webViewTabs.isEmpty) {\n                                      browserModel.showTabScroller = false;\n                                    }\n                                  }\n                                });\n                              },\n                            )\n                          ],\n                        ),\n                      ),\n                    ),\n                    Expanded(\n                      child: screenshotImage,\n                    )\n                  ],\n                );\n              }).toList(),\n              onTap: (index) async {\n                browserModel.showTabScroller = false;\n                windowModel.showTab(index);\n              },\n            )));\n  }\n}\n"
  },
  {
    "path": "lib/custom_image.dart",
    "content": "import 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:flutter/material.dart';\n\nclass CustomImage extends StatelessWidget {\n  final double? width;\n  final double? height;\n  final double maxWidth;\n  final double maxHeight;\n  final double minWidth;\n  final double minHeight;\n  final Uri? url;\n\n  const CustomImage(\n      {super.key,\n      this.url,\n      this.width,\n      this.height,\n      this.maxWidth = double.infinity,\n      this.maxHeight = double.infinity,\n      this.minWidth = 0.0,\n      this.minHeight = 0.0});\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      constraints: BoxConstraints(\n          maxWidth: maxWidth,\n          maxHeight: maxHeight,\n          minHeight: minHeight,\n          minWidth: minWidth),\n      width: width,\n      height: height,\n      child: getImage(),\n    );\n  }\n\n  Widget? getImage() {\n    if (url != null) {\n      if (url!.scheme == \"data\") {\n        Uint8List bytes = const Base64Decoder()\n            .convert(url.toString().replaceFirst(\"data:image/png;base64,\", \"\"));\n        return Image.memory(\n          bytes,\n          fit: BoxFit.contain,\n          errorBuilder: (context, error, stackTrace) => getBrokenImageIcon(),\n        );\n      }\n      return Image.network(\n        url.toString(),\n        fit: BoxFit.contain,\n        errorBuilder: (context, error, stackTrace) => getBrokenImageIcon(),\n      );\n    }\n    return getBrokenImageIcon();\n  }\n\n  Widget getBrokenImageIcon() {\n    return Icon(\n      Icons.broken_image,\n      size: width ?? height ?? maxWidth,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/custom_popup_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/material_transparent_page_route.dart';\n\nclass CustomPopupDialogPageRoute<T> extends MaterialTransparentPageRoute<T> {\n  final Color overlayColor;\n  final Duration? customTransitionDuration;\n  bool isPopped = false;\n\n  CustomPopupDialogPageRoute({\n    required super.builder,\n    Duration? transitionDuration,\n    Color? overlayColor,\n    super.settings,\n  })  : overlayColor = overlayColor ?? Colors.black.withOpacity(0.5),\n        customTransitionDuration = transitionDuration;\n\n  @override\n  Duration get transitionDuration => customTransitionDuration != null\n      ? customTransitionDuration!\n      : const Duration(milliseconds: 300);\n\n  @override\n  bool didPop(T? result) {\n    isPopped = true;\n    return super.didPop(result);\n  }\n\n  @override\n  Widget buildTransitions(BuildContext context, Animation<double> animation,\n      Animation<double> secondaryAnimation, Widget child) {\n    return Scaffold(\n        backgroundColor: Colors.transparent,\n        body: Stack(\n          children: <Widget>[\n            Positioned.fill(\n              child: GestureDetector(\n                onTap: () async {\n                  if (!isPopped) {\n                    Navigator.maybePop(context);\n                  } else {\n                    isPopped = true;\n                  }\n                },\n                child: Opacity(\n                  opacity: animation.value,\n                  child: Container(color: overlayColor),\n                ),\n              ),\n            ),\n            child,\n          ],\n        ));\n    // return child;\n  }\n}\n\nclass CustomPopupDialog extends StatefulWidget {\n  final Widget child;\n  final Duration transitionDuration;\n\n  const CustomPopupDialog(\n      {super.key,\n      required this.child,\n      this.transitionDuration = const Duration(milliseconds: 300)});\n\n  @override\n  State<StatefulWidget> createState() => _CustomPopupDialogState();\n\n  static CustomPopupDialogPageRoute show(\n      {required BuildContext context,\n      Widget? child,\n      WidgetBuilder? builder,\n      Color? overlayColor,\n      required Duration transitionDuration}) {\n    var route = CustomPopupDialogPageRoute(\n      transitionDuration: transitionDuration,\n      overlayColor: overlayColor,\n      builder: (context) {\n        return CustomPopupDialog(\n          transitionDuration: transitionDuration,\n          child: builder != null ? builder(context) : child!,\n        );\n      },\n    );\n    Navigator.push(context, route);\n    return route;\n  }\n}\n\nclass _CustomPopupDialogState extends State<CustomPopupDialog>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _slideController;\n  late Animation<Offset> _offsetSlideAnimation;\n\n  @override\n  void initState() {\n    super.initState();\n    _slideController = AnimationController(\n      duration: widget.transitionDuration,\n      vsync: this,\n    );\n\n    var begin = const Offset(0.0, -1.0);\n    var end = Offset.zero;\n    var tween = Tween(begin: begin, end: end);\n\n    _offsetSlideAnimation = tween.animate(CurvedAnimation(\n      parent: _slideController,\n      curve: Curves.ease,\n    ));\n\n    _slideController.forward();\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    _slideController.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return WillPopScope(\n      onWillPop: () async {\n        await hideTransition();\n        return true;\n      },\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        children: <Widget>[\n          SlideTransition(\n            position: _offsetSlideAnimation,\n            child: Container(\n                padding: const EdgeInsets.all(15.0),\n                width: MediaQuery.of(context).size.width,\n                color: Colors.white,\n                child: widget.child),\n          ),\n        ],\n      ),\n    );\n  }\n\n  Future<void> hideTransition() async {\n    _slideController.reverse();\n    await Future.delayed(widget.transitionDuration);\n  }\n\n  Future<void> hide() async {\n    await hideTransition();\n    if (mounted) {\n      Navigator.pop(context);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/custom_popup_menu_item.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass CustomPopupMenuItem<T> extends PopupMenuEntry<T> {\n  const CustomPopupMenuItem({\n    super.key,\n    this.value,\n    this.enabled = true,\n    this.height = kMinInteractiveDimension,\n    this.textStyle,\n    this.isIconButtonRow = false,\n    required this.child,\n  });\n\n  final T? value;\n\n  final bool enabled;\n\n  @override\n  final double height;\n\n  final TextStyle? textStyle;\n\n  final Widget child;\n\n  final bool isIconButtonRow;\n\n  @override\n  bool represents(T? value) => value == this.value;\n\n  @override\n  CustomPopupMenuItemState<T, CustomPopupMenuItem<T>> createState() =>\n      CustomPopupMenuItemState<T, CustomPopupMenuItem<T>>();\n}\n\nclass CustomPopupMenuItemState<T, W extends CustomPopupMenuItem<T>>\n    extends State<W> {\n  @protected\n  Widget buildChild() => widget.child;\n\n  @protected\n  void handleTap() {\n    Navigator.pop<T>(context, widget.value);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final ThemeData theme = Theme.of(context);\n    final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);\n    TextStyle style = (widget.textStyle ??\n        popupMenuTheme.textStyle ??\n        theme.textTheme.titleMedium)!;\n\n    if (!widget.enabled) style = style.copyWith(color: theme.disabledColor);\n\n    Widget item = AnimatedDefaultTextStyle(\n      style: style,\n      duration: kThemeChangeDuration,\n      child: Container(\n        alignment: AlignmentDirectional.centerStart,\n        constraints: BoxConstraints(minHeight: widget.height),\n        padding: const EdgeInsets.symmetric(horizontal: 16.0),\n        child: buildChild(),\n      ),\n    );\n\n    if (!widget.enabled) {\n      final bool isDark = theme.brightness == Brightness.dark;\n      item = IconTheme.merge(\n        data: IconThemeData(opacity: isDark ? 0.5 : 0.38),\n        child: item,\n      );\n    }\n\n    if (widget.isIconButtonRow) {\n      return Material(\n        child: item,\n      );\n    }\n\n    return InkWell(\n      onTap: widget.enabled ? handleTap : null,\n      canRequestFocus: widget.enabled,\n      child: item,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/empty_tab.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_browser/webview_tab.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport 'models/browser_model.dart';\nimport 'models/webview_model.dart';\nimport 'models/window_model.dart';\n\nclass EmptyTab extends StatefulWidget {\n  const EmptyTab({super.key});\n\n  @override\n  State<EmptyTab> createState() => _EmptyTabState();\n}\n\nclass _EmptyTabState extends State<EmptyTab> {\n  final _controller = TextEditingController();\n\n  @override\n  Widget build(BuildContext context) {\n    var browserModel = Provider.of<BrowserModel>(context, listen: true);\n    var settings = browserModel.getSettings();\n\n    return Scaffold(\n      body: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 40.0),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.center,\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            Image(image: AssetImage(settings.searchEngine.assetIcon)),\n            const SizedBox(\n              height: 10,\n            ),\n            Row(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                Expanded(\n                    child: TextField(\n                  controller: _controller,\n                  onSubmitted: (value) {\n                    openNewTab(value);\n                  },\n                  textInputAction: TextInputAction.go,\n                  decoration: const InputDecoration(\n                    hintText: \"Search for or type a web address\",\n                    hintStyle: TextStyle(color: Colors.black54, fontSize: 25.0),\n                  ),\n                  style: const TextStyle(\n                    color: Colors.black,\n                    fontSize: 25.0,\n                  ),\n                )),\n                IconButton(\n                  icon: const Icon(Icons.search,\n                      color: Colors.black54, size: 25.0),\n                  onPressed: () {\n                    openNewTab(_controller.text);\n                    FocusScope.of(context).unfocus();\n                  },\n                )\n              ],\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  void openNewTab(value) {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final browserModel = Provider.of<BrowserModel>(context, listen: false);\n    final settings = browserModel.getSettings();\n\n    var url = WebUri(value.trim());\n    if (Util.isLocalizedContent(url) ||\n        (url.isValidUri && url.toString().split(\".\").length > 1)) {\n      url = url.scheme.isEmpty ? WebUri(\"https://$url\") : url;\n    } else {\n      url = WebUri(settings.searchEngine.searchUrl + value);\n    }\n\n    windowModel.addTab(WebViewTab(\n      key: GlobalKey(),\n      webViewModel: WebViewModel(url: url),\n    ));\n  }\n}\n"
  },
  {
    "path": "lib/javascript_console_result.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n\nclass JavaScriptConsoleResult extends StatefulWidget {\n  final String data;\n  final Color textColor;\n  final Color backgroundColor;\n  final IconData? iconData;\n  final Color? iconColor;\n\n  const JavaScriptConsoleResult(\n      {super.key,\n      this.data = \"\",\n      this.textColor = Colors.black,\n      this.backgroundColor = Colors.transparent,\n      this.iconData,\n      this.iconColor});\n\n  @override\n  State<JavaScriptConsoleResult> createState() =>\n      _JavaScriptConsoleResultState();\n}\n\nclass _JavaScriptConsoleResultState extends State<JavaScriptConsoleResult> {\n  @override\n  Widget build(BuildContext context) {\n    var textSpanChildrens = <InlineSpan>[];\n    if (widget.iconData != null) {\n      textSpanChildrens.add(WidgetSpan(\n        child: Container(\n          padding: const EdgeInsets.only(right: 5.0),\n          child: Icon(widget.iconData, color: widget.iconColor, size: 14),\n        ),\n        alignment: PlaceholderAlignment.middle,\n      ));\n    }\n    textSpanChildrens.add(TextSpan(\n      text: widget.data,\n      style: TextStyle(color: widget.textColor),\n    ));\n\n    return Material(\n      color: widget.backgroundColor,\n      child: InkWell(\n          onTap: () {\n            Clipboard.setData(ClipboardData(text: widget.data));\n          },\n          child: Container(\n            padding:\n                const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),\n            color: Colors.transparent,\n            child: RichText(\n              text: TextSpan(\n                children: textSpanChildrens,\n              ),\n            ),\n          )),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/long_press_alert_dialog.dart",
    "content": "// import 'package:cached_network_image/cached_network_image.dart';\nimport 'dart:io';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/custom_image.dart';\nimport 'package:flutter_browser/webview_tab.dart';\nimport 'package:flutter_downloader/flutter_downloader.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:provider/provider.dart';\nimport 'package:share_plus/share_plus.dart';\n\nimport 'main.dart';\nimport 'models/browser_model.dart';\nimport 'models/webview_model.dart';\nimport 'models/window_model.dart';\n\nclass LongPressAlertDialog extends StatefulWidget {\n  static const List<InAppWebViewHitTestResultType> hitTestResultSupported = [\n    InAppWebViewHitTestResultType.SRC_IMAGE_ANCHOR_TYPE,\n    InAppWebViewHitTestResultType.SRC_ANCHOR_TYPE,\n    InAppWebViewHitTestResultType.IMAGE_TYPE\n  ];\n\n  const LongPressAlertDialog(\n      {super.key,\n      required this.webViewModel,\n      required this.hitTestResult,\n      this.requestFocusNodeHrefResult});\n\n  final WebViewModel webViewModel;\n  final InAppWebViewHitTestResult hitTestResult;\n  final RequestFocusNodeHrefResult? requestFocusNodeHrefResult;\n\n  @override\n  State<LongPressAlertDialog> createState() => _LongPressAlertDialogState();\n}\n\nclass _LongPressAlertDialogState extends State<LongPressAlertDialog> {\n  var _isLinkPreviewReady = false;\n\n  @override\n  Widget build(BuildContext context) {\n    return AlertDialog(\n      contentPadding: const EdgeInsets.all(0.0),\n      content: SingleChildScrollView(\n        child: SizedBox(\n          width: double.maxFinite,\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            children: _buildDialogLongPressHitTestResult(),\n          ),\n        ),\n      ),\n    );\n  }\n\n  List<Widget> _buildDialogLongPressHitTestResult() {\n    if (widget.hitTestResult.type ==\n            InAppWebViewHitTestResultType.SRC_ANCHOR_TYPE ||\n        widget.hitTestResult.type ==\n            InAppWebViewHitTestResultType.SRC_IMAGE_ANCHOR_TYPE ||\n        (widget.hitTestResult.type ==\n                InAppWebViewHitTestResultType.IMAGE_TYPE &&\n            widget.requestFocusNodeHrefResult != null &&\n            widget.requestFocusNodeHrefResult!.url != null &&\n            widget.requestFocusNodeHrefResult!.url.toString().isNotEmpty)) {\n      return <Widget>[\n        _buildLinkTile(),\n        const Divider(),\n        _buildLinkPreview(),\n        const Divider(),\n        _buildOpenNewTab(),\n        _buildOpenNewIncognitoTab(),\n        _buildCopyAddressLink(),\n        _buildShareLink(),\n      ];\n    } else if (widget.hitTestResult.type ==\n        InAppWebViewHitTestResultType.IMAGE_TYPE) {\n      return <Widget>[\n        _buildImageTile(),\n        const Divider(),\n        _buildOpenImageNewTab(),\n        _buildDownloadImage(),\n        _buildSearchImageOnGoogle(),\n        _buildShareImage(),\n      ];\n    }\n\n    return [];\n  }\n\n  Widget _buildLinkTile() {\n    var url =\n        widget.requestFocusNodeHrefResult?.url ?? Uri.parse(\"about:blank\");\n    var faviconUrl = Uri.parse(\"${url.origin}/favicon.ico\");\n\n    var title = widget.requestFocusNodeHrefResult?.title ?? \"\";\n    if (title.isEmpty) {\n      title = \"Link\";\n    }\n\n    return ListTile(\n      leading: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: <Widget>[\n          // CachedNetworkImage(\n          //   placeholder: (context, url) => CircularProgressIndicator(),\n          //   imageUrl: widget.requestFocusNodeHrefResult?.src != null ? widget.requestFocusNodeHrefResult!.src : faviconUrl,\n          //   height: 30,\n          // )\n          CustomImage(\n            url: widget.requestFocusNodeHrefResult?.src != null\n                ? Uri.parse(widget.requestFocusNodeHrefResult!.src!)\n                : faviconUrl,\n            maxWidth: 30.0,\n            height: 30.0,\n          )\n        ],\n      ),\n      title: Text(\n        title,\n        maxLines: 2,\n        overflow: TextOverflow.ellipsis,\n      ),\n      subtitle: Text(\n        widget.requestFocusNodeHrefResult?.url?.toString() ?? \"\",\n        maxLines: 2,\n        overflow: TextOverflow.ellipsis,\n      ),\n      isThreeLine: true,\n    );\n  }\n\n  Widget _buildLinkPreview() {\n    var browserModel = Provider.of<BrowserModel>(context, listen: true);\n    browserModel.getSettings();\n\n    return ListTile(\n      title: const Center(child: Text(\"Link Preview\")),\n      subtitle: Container(\n        padding: const EdgeInsets.only(top: 15.0),\n        height: 250,\n        child: IndexedStack(\n          index: _isLinkPreviewReady ? 1 : 0,\n          children: <Widget>[\n            const Center(\n              child: CircularProgressIndicator(),\n            ),\n            InAppWebView(\n              gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{\n                Factory<OneSequenceGestureRecognizer>(\n                  () => EagerGestureRecognizer(),\n                ),\n              },\n              webViewEnvironment: webViewEnvironment,\n              initialUrlRequest:\n                  URLRequest(url: widget.requestFocusNodeHrefResult?.url),\n              initialSettings: InAppWebViewSettings(\n                  verticalScrollbarThumbColor:\n                      const Color.fromRGBO(0, 0, 0, 0.5),\n                  horizontalScrollbarThumbColor:\n                      const Color.fromRGBO(0, 0, 0, 0.5)),\n              onProgressChanged: (controller, progress) {\n                if (progress > 50) {\n                  setState(() {\n                    _isLinkPreviewReady = true;\n                  });\n                }\n              },\n            )\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget _buildOpenNewTab() {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n\n    return ListTile(\n      title: const Text(\"Open in a new tab\"),\n      onTap: () {\n        windowModel.addTab(WebViewTab(\n          key: GlobalKey(),\n          webViewModel:\n              WebViewModel(url: widget.requestFocusNodeHrefResult?.url),\n        ));\n        Navigator.pop(context);\n      },\n    );\n  }\n\n  Widget _buildOpenNewIncognitoTab() {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n\n    return ListTile(\n      title: const Text(\"Open in a new incognito tab\"),\n      onTap: () {\n        windowModel.addTab(WebViewTab(\n          key: GlobalKey(),\n          webViewModel: WebViewModel(\n              url: widget.requestFocusNodeHrefResult?.url,\n              isIncognitoMode: true),\n        ));\n        Navigator.pop(context);\n      },\n    );\n  }\n\n  Widget _buildCopyAddressLink() {\n    return ListTile(\n      title: const Text(\"Copy address link\"),\n      onTap: () {\n        Clipboard.setData(ClipboardData(\n            text: widget.requestFocusNodeHrefResult?.url.toString() ??\n                widget.hitTestResult.extra ??\n                ''));\n        Navigator.pop(context);\n      },\n    );\n  }\n\n  Widget _buildShareLink() {\n    return ListTile(\n      title: const Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: [\n            Text(\"Share link\"),\n            Padding(\n              padding: EdgeInsets.only(right: 12.5),\n              child: Icon(\n                Icons.share,\n                color: Colors.black54,\n                size: 20.0,\n              ),\n            )\n          ]),\n      onTap: () {\n        if (widget.hitTestResult.extra != null) {\n          Share.share(widget.requestFocusNodeHrefResult?.url.toString() ??\n              widget.hitTestResult.extra!);\n        }\n        Navigator.pop(context);\n      },\n    );\n  }\n\n  Widget _buildImageTile() {\n    return ListTile(\n      contentPadding: const EdgeInsets.only(\n          left: 15.0, top: 15.0, right: 15.0, bottom: 5.0),\n      leading: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: <Widget>[\n          // CachedNetworkImage(\n          //   placeholder: (context, url) => CircularProgressIndicator(),\n          //   imageUrl: widget.hitTestResult.extra,\n          //   height: 50,\n          // ),\n          CustomImage(\n              url: Uri.parse(widget.hitTestResult.extra!),\n              maxWidth: 50.0,\n              height: 50.0)\n        ],\n      ),\n      title: Text(widget.webViewModel.title ?? \"\"),\n    );\n  }\n\n  Widget _buildDownloadImage() {\n    return ListTile(\n      title: const Text(\"Download image\"),\n      onTap: () async {\n        String? url = widget.hitTestResult.extra;\n        if (url != null) {\n          var uri = Uri.parse(widget.hitTestResult.extra!);\n          String path = uri.path;\n          String fileName = path.substring(path.lastIndexOf('/') + 1);\n          Directory? directory = await getExternalStorageDirectory();\n          await FlutterDownloader.enqueue(\n            url: url,\n            fileName: fileName,\n            savedDir: directory!.path,\n            showNotification: true,\n            openFileFromNotification: true,\n          );\n        }\n        if (mounted) {\n          Navigator.pop(context);\n        }\n      },\n    );\n  }\n\n  Widget _buildShareImage() {\n    return ListTile(\n      title: const Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: [\n            Text(\"Share image\"),\n            Padding(\n              padding: EdgeInsets.only(right: 12.5),\n              child: Icon(\n                Icons.share,\n                color: Colors.black54,\n                size: 20.0,\n              ),\n            )\n          ]),\n      onTap: () {\n        if (widget.hitTestResult.extra != null) {\n          Share.share(widget.hitTestResult.extra!);\n        }\n        Navigator.pop(context);\n      },\n    );\n  }\n\n  Widget _buildOpenImageNewTab() {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n\n    return ListTile(\n      title: const Text(\"Image in a new tab\"),\n      onTap: () {\n        windowModel.addTab(WebViewTab(\n          key: GlobalKey(),\n          webViewModel: WebViewModel(\n              url: WebUri(widget.hitTestResult.extra ?? \"about:blank\")),\n        ));\n        Navigator.pop(context);\n      },\n    );\n  }\n\n  Widget _buildSearchImageOnGoogle() {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n\n    return ListTile(\n      title: const Text(\"Search this image on Google\"),\n      onTap: () {\n        if (widget.hitTestResult.extra != null) {\n          var url =\n              \"http://images.google.com/searchbyimage?image_url=${widget.hitTestResult.extra!}\";\n          windowModel.addTab(WebViewTab(\n            key: GlobalKey(),\n            webViewModel: WebViewModel(url: WebUri(url)),\n          ));\n        }\n        Navigator.pop(context);\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/main.dart",
    "content": "import 'dart:io';\n\nimport 'package:context_menus/context_menus.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/scheduler.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/models/window_model.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_downloader/flutter_downloader.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:permission_handler/permission_handler.dart';\nimport 'package:provider/provider.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:sqflite/sqflite.dart';\nimport 'package:sqflite_common_ffi/sqflite_ffi.dart';\nimport 'package:window_manager_plus/window_manager_plus.dart';\nimport 'package:path/path.dart' as p;\n\nimport 'browser.dart';\n\n// ignore: non_constant_identifier_names\nlate final String WEB_ARCHIVE_DIR;\n// ignore: non_constant_identifier_names\nlate final double TAB_VIEWER_BOTTOM_OFFSET_1;\n// ignore: non_constant_identifier_names\nlate final double TAB_VIEWER_BOTTOM_OFFSET_2;\n// ignore: non_constant_identifier_names\nlate final double TAB_VIEWER_BOTTOM_OFFSET_3;\n// ignore: constant_identifier_names\nconst double TAB_VIEWER_TOP_OFFSET_1 = 0.0;\n// ignore: constant_identifier_names\nconst double TAB_VIEWER_TOP_OFFSET_2 = 10.0;\n// ignore: constant_identifier_names\nconst double TAB_VIEWER_TOP_OFFSET_3 = 20.0;\n// ignore: constant_identifier_names\nconst double TAB_VIEWER_TOP_SCALE_TOP_OFFSET = 250.0;\n// ignore: constant_identifier_names\nconst double TAB_VIEWER_TOP_SCALE_BOTTOM_OFFSET = 230.0;\n\nWebViewEnvironment? webViewEnvironment;\nDatabase? db;\n\nint windowId = 0;\nString? windowModelId;\n\nvoid main(List<String> args) async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  if (Util.isDesktop()) {\n    windowId = args.isNotEmpty ? int.tryParse(args[0]) ?? 0 : 0;\n    windowModelId = args.length > 1 ? args[1] : null;\n    await WindowManagerPlus.ensureInitialized(windowId);\n  }\n\n  final appDocumentsDir = await getApplicationDocumentsDirectory();\n\n  if (Util.isDesktop()) {\n    sqfliteFfiInit();\n    databaseFactory = databaseFactoryFfi;\n  }\n  db = await databaseFactory.openDatabase(\n      p.join(appDocumentsDir.path, \"databases\", \"myDb.db\"),\n      options: OpenDatabaseOptions(\n          version: 1,\n          singleInstance: false,\n          onCreate: (Database db, int version) async {\n            await db.execute(\n                'CREATE TABLE browser (id INTEGER PRIMARY KEY, json TEXT)');\n            await db.execute(\n                'CREATE TABLE windows (id TEXT PRIMARY KEY, json TEXT)');\n          }));\n\n  if (Util.isDesktop()) {\n    WindowOptions windowOptions = WindowOptions(\n      center: true,\n      backgroundColor: Colors.transparent,\n      titleBarStyle:\n          Util.isWindows() ? TitleBarStyle.normal : TitleBarStyle.hidden,\n      minimumSize: const Size(1280, 720),\n      size: const Size(1280, 720),\n    );\n    WindowManagerPlus.current.waitUntilReadyToShow(windowOptions, () async {\n      if (!Util.isWindows()) {\n        await WindowManagerPlus.current.setAsFrameless();\n        await WindowManagerPlus.current.setHasShadow(true);\n      }\n      await WindowManagerPlus.current.show();\n      await WindowManagerPlus.current.focus();\n    });\n  }\n\n  WEB_ARCHIVE_DIR = (await getApplicationSupportDirectory()).path;\n\n  TAB_VIEWER_BOTTOM_OFFSET_1 = 150.0;\n  TAB_VIEWER_BOTTOM_OFFSET_2 = 160.0;\n  TAB_VIEWER_BOTTOM_OFFSET_3 = 170.0;\n\n  if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) {\n    final availableVersion = await WebViewEnvironment.getAvailableVersion();\n    assert(availableVersion != null,\n        'Failed to find an installed WebView2 Runtime or non-stable Microsoft Edge installation.');\n\n    webViewEnvironment = await WebViewEnvironment.create(\n        settings:\n            WebViewEnvironmentSettings(userDataFolder: 'flutter_browser_app'));\n  }\n\n  if (Util.isMobile()) {\n    await FlutterDownloader.initialize(debug: kDebugMode);\n  }\n\n  if (Util.isMobile()) {\n    await Permission.camera.request();\n    await Permission.microphone.request();\n    await Permission.storage.request();\n  }\n\n  runApp(\n    MultiProvider(\n      providers: [\n        ChangeNotifierProvider(\n          create: (context) => BrowserModel(),\n        ),\n        ChangeNotifierProvider(\n          create: (context) => WebViewModel(),\n        ),\n        ChangeNotifierProxyProvider<WebViewModel, WindowModel>(\n          update: (context, webViewModel, windowModel) {\n            windowModel!.setCurrentWebViewModel(webViewModel);\n            return windowModel;\n          },\n          create: (BuildContext context) =>\n              WindowModel(id: null),\n        ),\n      ],\n      child: const FlutterBrowserApp(),\n    ),\n  );\n}\n\nclass FlutterBrowserApp extends StatefulWidget {\n  const FlutterBrowserApp({super.key});\n\n  @override\n  State<FlutterBrowserApp> createState() => _FlutterBrowserAppState();\n}\n\nclass _FlutterBrowserAppState extends State<FlutterBrowserApp>\n    with WindowListener {\n\n  // https://github.com/pichillilorenzo/window_manager_plus/issues/5\n  late final AppLifecycleListener? _appLifecycleListener;\n\n  @override\n  void initState() {\n    super.initState();\n    WindowManagerPlus.current.addListener(this);\n\n    // https://github.com/pichillilorenzo/window_manager_plus/issues/5\n    if (WindowManagerPlus.current.id > 0 && Platform.isMacOS) {\n      _appLifecycleListener = AppLifecycleListener(\n        onStateChange: _handleStateChange,\n      );\n    }\n  }\n\n  void _handleStateChange(AppLifecycleState state) {\n    // https://github.com/pichillilorenzo/window_manager_plus/issues/5\n    if (WindowManagerPlus.current.id > 0 && Platform.isMacOS && state == AppLifecycleState.hidden) {\n      SchedulerBinding.instance.handleAppLifecycleStateChanged(\n          AppLifecycleState.inactive);\n    }\n  }\n\n  @override\n  void dispose() {\n    WindowManagerPlus.current.removeListener(this);\n    _appLifecycleListener?.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final materialApp = MaterialApp(\n      title: 'Flutter Browser',\n      debugShowCheckedModeBanner: false,\n      theme: ThemeData(\n        visualDensity: VisualDensity.adaptivePlatformDensity,\n      ),\n      initialRoute: '/',\n      routes: {\n        '/': (context) => const Browser(),\n      },\n    );\n\n    return Util.isMobile()\n        ? materialApp\n        : ContextMenuOverlay(\n            child: materialApp,\n          );\n  }\n\n  @override\n  void onWindowFocus([int? windowId]) {\n    setState(() {});\n    if (!Util.isWindows()) {\n      WindowManagerPlus.current.setMovable(false);\n    }\n  }\n\n  @override\n  void onWindowBlur([int? windowId]) {\n    if (!Util.isWindows()) {\n      WindowManagerPlus.current.setMovable(true);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/material_transparent_page_route.dart",
    "content": "import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\n\nclass MaterialTransparentPageRoute<T> extends PageRoute<T> {\n  MaterialTransparentPageRoute({\n    required this.builder,\n    super.fullscreenDialog,\n    super.settings,\n  });\n\n  final WidgetBuilder builder;\n\n  @override\n  bool get opaque => false;\n\n  @override\n  Color? get barrierColor => null;\n\n  @override\n  String? get barrierLabel => null;\n\n  @override\n  bool get maintainState => true;\n\n  @override\n  Duration get transitionDuration => const Duration(milliseconds: 300);\n\n  @override\n  bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {\n    // Don't perform outgoing animation if the next route is a fullscreen dialog.\n    return (nextRoute is MaterialPageRoute && !nextRoute.fullscreenDialog) ||\n        (nextRoute is CupertinoPageRoute && !nextRoute.fullscreenDialog);\n  }\n\n  @override\n  Widget buildPage(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n  ) {\n    final Widget result = builder(context);\n    return Semantics(\n      scopesRoute: true,\n      explicitChildNodes: true,\n      child: result,\n    );\n  }\n\n  @override\n  Widget buildTransitions(BuildContext context, Animation<double> animation,\n      Animation<double> secondaryAnimation, Widget child) {\n    final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;\n    return theme.buildTransitions<T>(\n        this, context, animation, secondaryAnimation, child);\n  }\n}\n"
  },
  {
    "path": "lib/models/browser_model.dart",
    "content": "import 'dart:convert';\nimport 'dart:async';\nimport 'dart:io';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:window_manager_plus/window_manager_plus.dart';\nimport '../main.dart';\nimport 'web_archive_model.dart';\nimport 'window_model.dart';\n\nimport 'favorite_model.dart';\n\nimport 'search_engine_model.dart';\nimport 'package:collection/collection.dart';\n\nclass BrowserSettings {\n  SearchEngineModel searchEngine;\n  bool homePageEnabled;\n  String customUrlHomePage;\n  bool debuggingEnabled;\n\n  BrowserSettings(\n      {this.searchEngine = GoogleSearchEngine,\n      this.homePageEnabled = false,\n      this.customUrlHomePage = \"\",\n      this.debuggingEnabled = false});\n\n  BrowserSettings copy() {\n    return BrowserSettings(\n        searchEngine: searchEngine,\n        homePageEnabled: homePageEnabled,\n        customUrlHomePage: customUrlHomePage,\n        debuggingEnabled: debuggingEnabled);\n  }\n\n  static BrowserSettings? fromMap(Map<String, dynamic>? map) {\n    return map != null\n        ? BrowserSettings(\n            searchEngine: SearchEngines[map[\"searchEngineIndex\"]],\n            homePageEnabled: map[\"homePageEnabled\"],\n            customUrlHomePage: map[\"customUrlHomePage\"],\n            debuggingEnabled: map[\"debuggingEnabled\"])\n        : null;\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"searchEngineIndex\": SearchEngines.indexOf(searchEngine),\n      \"homePageEnabled\": homePageEnabled,\n      \"customUrlHomePage\": customUrlHomePage,\n      \"debuggingEnabled\": debuggingEnabled\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n\nclass BrowserModel extends ChangeNotifier {\n  final List<FavoriteModel> _favorites = [];\n  final Map<String, WebArchiveModel> _webArchives = {};\n  BrowserSettings _settings = BrowserSettings();\n\n  bool _showTabScroller = false;\n\n  bool get showTabScroller => _showTabScroller;\n\n  set showTabScroller(bool value) {\n    if (value != _showTabScroller) {\n      _showTabScroller = value;\n      notifyListeners();\n    }\n  }\n\n  BrowserModel();\n\n  UnmodifiableListView<FavoriteModel> get favorites =>\n      UnmodifiableListView(_favorites);\n\n  UnmodifiableMapView<String, WebArchiveModel> get webArchives =>\n      UnmodifiableMapView(_webArchives);\n\n  Future<void> openWindow(WindowModel? windowModel) async {\n    if (Util.isMobile()) {\n      return;\n    }\n\n    final window = await WindowManagerPlus.createWindow(windowModel != null ? [windowModel.id] : null);\n    if (window != null) {\n      if (kDebugMode) {\n        print(\"Window created: $window}\");\n      }\n    } else {\n      if (kDebugMode) {\n        print(\"Cannot create window\");\n      }\n    }\n\n    notifyListeners();\n  }\n\n  Future<void> removeWindow(WindowModel window) async {\n    await window.removeInfo();\n  }\n\n  Future<void> removeAllWindows() async {\n    final count = await db?.rawDelete('DELETE FROM windows');\n    if (count == null && kDebugMode) {\n      print(\"Cannot delete windows\");\n    }\n  }\n\n  bool containsFavorite(FavoriteModel favorite) {\n    return _favorites.contains(favorite) ||\n        _favorites\n                .map((e) => e)\n                .firstWhereOrNull((element) => element.url == favorite.url) !=\n            null;\n  }\n\n  void addFavorite(FavoriteModel favorite) {\n    _favorites.add(favorite);\n    notifyListeners();\n  }\n\n  void addFavorites(List<FavoriteModel> favorites) {\n    _favorites.addAll(favorites);\n    notifyListeners();\n  }\n\n  void clearFavorites() {\n    _favorites.clear();\n    notifyListeners();\n  }\n\n  void removeFavorite(FavoriteModel favorite) {\n    if (!_favorites.remove(favorite)) {\n      var favToRemove = _favorites\n          .map((e) => e)\n          .firstWhereOrNull((element) => element.url == favorite.url);\n      _favorites.remove(favToRemove);\n    }\n\n    notifyListeners();\n  }\n\n  void addWebArchive(String url, WebArchiveModel webArchiveModel) {\n    _webArchives.putIfAbsent(url, () => webArchiveModel);\n    notifyListeners();\n  }\n\n  void addWebArchives(Map<String, WebArchiveModel> webArchives) {\n    _webArchives.addAll(webArchives);\n    notifyListeners();\n  }\n\n  void removeWebArchive(WebArchiveModel webArchive) {\n    var path = webArchive.path;\n    if (path != null) {\n      final webArchiveFile = File(path);\n      try {\n        webArchiveFile.deleteSync();\n      } finally {\n        _webArchives.remove(webArchive.url.toString());\n      }\n      notifyListeners();\n    }\n  }\n\n  void clearWebArchives() {\n    _webArchives.forEach((key, webArchive) {\n      var path = webArchive.path;\n      if (path != null) {\n        final webArchiveFile = File(path);\n        try {\n          webArchiveFile.deleteSync();\n        } finally {\n          _webArchives.remove(key);\n        }\n      }\n    });\n\n    notifyListeners();\n  }\n\n  BrowserSettings getSettings() {\n    return _settings.copy();\n  }\n\n  void updateSettings(BrowserSettings settings) {\n    _settings = settings;\n    notifyListeners();\n  }\n\n  Future<List<WindowModel>> getWindows() async {\n    final List<WindowModel> windows = [];\n    final windowsMap = await db?.rawQuery('SELECT * FROM windows');\n    if (windowsMap == null) {\n      return windows;\n    }\n\n    for (final w in windowsMap) {\n      final wId = w['id'] as String;\n      if (wId.startsWith('window_')) {\n        final source = w['json'] as String;\n        Map<String, dynamic> wBrowserData = json.decode(source);\n        windows.add(WindowModel.fromMap(wBrowserData));\n      }\n    }\n\n    return windows;\n  }\n\n  DateTime _lastTrySave = DateTime.now();\n  Timer? _timerSave;\n\n  Future<void> save() async {\n    _timerSave?.cancel();\n\n    if (DateTime.now().difference(_lastTrySave) >=\n        const Duration(milliseconds: 400)) {\n      _lastTrySave = DateTime.now();\n      await flush();\n    } else {\n      _lastTrySave = DateTime.now();\n      _timerSave = Timer(const Duration(milliseconds: 500), () {\n        save();\n      });\n    }\n  }\n\n  Future<void> flush() async {\n    final browser =\n        await db?.rawQuery('SELECT * FROM browser WHERE id = ?', [1]);\n    int? count;\n    if (browser == null || browser.length == 0) {\n      count = await db?.rawInsert('INSERT INTO browser(id, json) VALUES(?, ?)',\n          [1, json.encode(toJson())]);\n    } else {\n      count = await db?.rawUpdate('UPDATE browser SET json = ? WHERE id = ?',\n          [json.encode(toJson()), 1]);\n    }\n\n    if ((count == null || count == 0) && kDebugMode) {\n      print(\"Cannot insert/update browser 1\");\n    }\n  }\n\n  Future<void> restore() async {\n    final browsers =\n        await db?.rawQuery('SELECT * FROM browser WHERE id = ?', [1]);\n    if (browsers == null || browsers.length == 0) {\n      return;\n    }\n    final browser = browsers[0];\n    Map<String, dynamic> browserData = json.decode(browser['json'] as String);\n    try {\n      clearFavorites();\n      clearWebArchives();\n\n      List<Map<String, dynamic>> favoritesList =\n          browserData[\"favorites\"]?.cast<Map<String, dynamic>>() ?? [];\n      List<FavoriteModel> favorites =\n          favoritesList.map((e) => FavoriteModel.fromMap(e)!).toList();\n\n      Map<String, dynamic> webArchivesMap =\n          browserData[\"webArchives\"]?.cast<String, dynamic>() ?? {};\n      Map<String, WebArchiveModel> webArchives = webArchivesMap.map(\n          (key, value) => MapEntry(\n              key, WebArchiveModel.fromMap(value?.cast<String, dynamic>())!));\n\n      BrowserSettings settings = BrowserSettings.fromMap(\n              browserData[\"settings\"]?.cast<String, dynamic>()) ??\n          BrowserSettings();\n\n      addFavorites(favorites);\n      addWebArchives(webArchives);\n      updateSettings(settings);\n    } catch (e) {\n      if (kDebugMode) {\n        print(e);\n      }\n      return;\n    }\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"favorites\": _favorites.map((e) => e.toMap()).toList(),\n      \"webArchives\":\n          _webArchives.map((key, value) => MapEntry(key, value.toMap())),\n      \"settings\": _settings.toMap()\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n"
  },
  {
    "path": "lib/models/favorite_model.dart",
    "content": "import 'package:flutter_inappwebview/flutter_inappwebview.dart';\n\nclass FavoriteModel {\n  WebUri? url;\n  String? title;\n  Favicon? favicon;\n\n  FavoriteModel({required this.url, required this.title, this.favicon});\n\n  static FavoriteModel? fromMap(Map<String, dynamic>? map) {\n    return map != null\n        ? FavoriteModel(\n            url: map[\"url\"] != null ? WebUri(map[\"url\"]) : null,\n            title: map[\"title\"],\n            favicon: map[\"favicon\"] != null\n                ? Favicon(\n                    url: WebUri(map[\"favicon\"][\"url\"]),\n                    rel: map[\"favicon\"][\"rel\"],\n                    width: map[\"favicon\"][\"width\"],\n                    height: map[\"favicon\"][\"height\"],\n                  )\n                : null)\n        : null;\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"url\": url?.toString(),\n      \"title\": title,\n      \"favicon\": favicon?.toMap()\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n"
  },
  {
    "path": "lib/models/search_engine_model.dart",
    "content": "class SearchEngineModel {\n  final String name;\n  final String assetIcon;\n  final String url;\n  final String searchUrl;\n\n  const SearchEngineModel(\n      {required this.name,\n      required this.url,\n      required this.searchUrl,\n      required this.assetIcon});\n\n  static SearchEngineModel? fromMap(Map<String, dynamic>? map) {\n    return map != null\n        ? SearchEngineModel(\n            name: map[\"name\"],\n            assetIcon: map[\"assetIcon\"],\n            url: map[\"url\"],\n            searchUrl: map[\"searchUrl\"])\n        : null;\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"name\": name,\n      \"assetIcon\": assetIcon,\n      \"url\": url,\n      \"searchUrl\": searchUrl\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n\n// ignore: constant_identifier_names\nconst GoogleSearchEngine = SearchEngineModel(\n    name: \"Google\",\n    url: \"https://www.google.com/\",\n    searchUrl: \"https://www.google.com/search?q=\",\n    assetIcon: \"assets/images/google_logo.png\");\n\n// ignore: constant_identifier_names\nconst YahooSearchEngine = SearchEngineModel(\n    name: \"Yahoo\",\n    url: \"https://yahoo.com/\",\n    searchUrl: \"https://search.yahoo.com/search?p=\",\n    assetIcon: \"assets/images/yahoo_logo.png\");\n\n// ignore: constant_identifier_names\nconst BingSearchEngine = SearchEngineModel(\n    name: \"Bing\",\n    url: \"https://www.bing.com/\",\n    searchUrl: \"https://www.bing.com/search?q=\",\n    assetIcon: \"assets/images/bing_logo.png\");\n\n// ignore: constant_identifier_names\nconst DuckDuckGoSearchEngine = SearchEngineModel(\n    name: \"DuckDuckGo\",\n    url: \"https://duckduckgo.com/\",\n    searchUrl: \"https://duckduckgo.com/?q=\",\n    assetIcon: \"assets/images/duckduckgo_logo.png\");\n\n// ignore: constant_identifier_names\nconst EcosiaSearchEngine = SearchEngineModel(\n    name: \"Ecosia\",\n    url: \"https://www.ecosia.org/\",\n    searchUrl: \"https://www.ecosia.org/search?q=\",\n    assetIcon: \"assets/images/ecosia_logo.png\");\n\n// ignore: constant_identifier_names\nconst SearchEngines = <SearchEngineModel>[\n  GoogleSearchEngine,\n  YahooSearchEngine,\n  BingSearchEngine,\n  DuckDuckGoSearchEngine,\n  EcosiaSearchEngine\n];\n"
  },
  {
    "path": "lib/models/web_archive_model.dart",
    "content": "import 'package:flutter_inappwebview/flutter_inappwebview.dart';\n\nclass WebArchiveModel {\n  WebUri? url;\n  String? title;\n  Favicon? favicon;\n  String? path;\n  DateTime timestamp;\n\n  WebArchiveModel(\n      {this.url, this.title, this.favicon, this.path, required this.timestamp});\n\n  static WebArchiveModel? fromMap(Map<String, dynamic>? map) {\n    return map != null\n        ? WebArchiveModel(\n            url: map[\"url\"] != null ? WebUri(map[\"url\"]) : null,\n            title: map[\"title\"],\n            path: map[\"path\"],\n            timestamp: DateTime.fromMicrosecondsSinceEpoch(map[\"timestamp\"]),\n            favicon: map[\"favicon\"] != null\n                ? Favicon(\n                    url: WebUri(map[\"favicon\"][\"url\"]),\n                    rel: map[\"favicon\"][\"rel\"],\n                    width: map[\"favicon\"][\"width\"],\n                    height: map[\"favicon\"][\"height\"],\n                  )\n                : null)\n        : null;\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"url\": url?.toString(),\n      \"title\": title,\n      \"favicon\": favicon?.toMap(),\n      \"path\": path,\n      \"timestamp\": timestamp.millisecondsSinceEpoch\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n"
  },
  {
    "path": "lib/models/webview_model.dart",
    "content": "import 'package:collection/collection.dart';\n\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\n\nclass WebViewModel extends ChangeNotifier {\n  int? _tabIndex;\n  WebUri? _url;\n  String? _title;\n  Favicon? _favicon;\n  late double _progress;\n  late bool _loaded;\n  late bool _isDesktopMode;\n  late bool _isIncognitoMode;\n  late List<Widget> _javaScriptConsoleResults;\n  late List<String> _javaScriptConsoleHistory;\n  late List<LoadedResource> _loadedResources;\n  late bool _isSecure;\n  int? windowId;\n  InAppWebViewSettings? settings;\n  InAppWebViewController? webViewController;\n  PullToRefreshController? pullToRefreshController;\n  FindInteractionController? findInteractionController;\n  Uint8List? screenshot;\n  bool needsToCompleteInitialLoad;\n  final DateTime _createdTime;\n  DateTime _lastOpenedTime;\n\n  final keepAlive = InAppWebViewKeepAlive();\n\n  WebViewModel(\n      {int? tabIndex,\n      WebUri? url,\n      String? title,\n      Favicon? favicon,\n      double progress = 0.0,\n      bool loaded = false,\n      bool isDesktopMode = false,\n      bool isIncognitoMode = false,\n      List<Widget>? javaScriptConsoleResults,\n      List<String>? javaScriptConsoleHistory,\n      List<LoadedResource>? loadedResources,\n      bool isSecure = false,\n      DateTime? createdTime,\n      DateTime? lastOpenedTime,\n      this.windowId,\n      this.settings,\n      this.webViewController,\n      this.pullToRefreshController,\n      this.findInteractionController,\n      this.needsToCompleteInitialLoad = true})\n      : _createdTime = createdTime ?? DateTime.now(),\n        _lastOpenedTime = lastOpenedTime ?? DateTime.now(),\n        _tabIndex = tabIndex,\n        _url = url,\n        _favicon = favicon,\n        _progress = progress,\n        _loaded = loaded,\n        _isDesktopMode = isDesktopMode,\n        _isIncognitoMode = isIncognitoMode,\n        _javaScriptConsoleResults = javaScriptConsoleResults ?? <Widget>[],\n        _javaScriptConsoleHistory = javaScriptConsoleHistory ?? <String>[],\n        _loadedResources = loadedResources ?? <LoadedResource>[],\n        _isSecure = isSecure {\n    settings = settings ?? InAppWebViewSettings();\n  }\n\n  int? get tabIndex => _tabIndex;\n\n  set tabIndex(int? value) {\n    if (value != _tabIndex) {\n      _tabIndex = value;\n      notifyListeners();\n    }\n  }\n\n  WebUri? get url => _url;\n\n  set url(WebUri? value) {\n    if (value != _url) {\n      _url = value;\n      notifyListeners();\n    }\n  }\n\n  String? get title => _title;\n\n  set title(String? value) {\n    if (value != _title) {\n      _title = value;\n      notifyListeners();\n    }\n  }\n\n  Favicon? get favicon => _favicon;\n\n  set favicon(Favicon? value) {\n    if (value != _favicon) {\n      _favicon = value;\n      notifyListeners();\n    }\n  }\n\n  double get progress => _progress;\n\n  set progress(double value) {\n    if (value != _progress) {\n      _progress = value;\n      notifyListeners();\n    }\n  }\n\n  bool get loaded => _loaded;\n\n  set loaded(bool value) {\n    if (value != _loaded) {\n      _loaded = value;\n      notifyListeners();\n    }\n  }\n\n  bool get isDesktopMode => _isDesktopMode;\n\n  set isDesktopMode(bool value) {\n    if (value != _isDesktopMode) {\n      _isDesktopMode = value;\n      notifyListeners();\n    }\n  }\n\n  bool get isIncognitoMode => _isIncognitoMode;\n\n  set isIncognitoMode(bool value) {\n    if (value != _isIncognitoMode) {\n      _isIncognitoMode = value;\n      notifyListeners();\n    }\n  }\n\n  DateTime get createdTime => _createdTime;\n\n  DateTime get lastOpenedTime => _lastOpenedTime;\n\n  set lastOpenedTime(DateTime value) {\n    if (value != _lastOpenedTime) {\n      _lastOpenedTime = value;\n      notifyListeners();\n    }\n  }\n\n  UnmodifiableListView<Widget> get javaScriptConsoleResults =>\n      UnmodifiableListView(_javaScriptConsoleResults);\n\n  setJavaScriptConsoleResults(List<Widget> value) {\n    if (!const IterableEquality().equals(value, _javaScriptConsoleResults)) {\n      _javaScriptConsoleResults = value;\n      notifyListeners();\n    }\n  }\n\n  void addJavaScriptConsoleResults(Widget value) {\n    _javaScriptConsoleResults.add(value);\n    notifyListeners();\n  }\n\n  UnmodifiableListView<String> get javaScriptConsoleHistory =>\n      UnmodifiableListView(_javaScriptConsoleHistory);\n\n  setJavaScriptConsoleHistory(List<String> value) {\n    if (!const IterableEquality().equals(value, _javaScriptConsoleHistory)) {\n      _javaScriptConsoleHistory = value;\n      notifyListeners();\n    }\n  }\n\n  void addJavaScriptConsoleHistory(String value) {\n    _javaScriptConsoleHistory.add(value);\n    notifyListeners();\n  }\n\n  UnmodifiableListView<LoadedResource> get loadedResources =>\n      UnmodifiableListView(_loadedResources);\n\n  setLoadedResources(List<LoadedResource> value) {\n    if (!const IterableEquality().equals(value, _loadedResources)) {\n      _loadedResources = value;\n      notifyListeners();\n    }\n  }\n\n  void addLoadedResources(LoadedResource value) {\n    _loadedResources.add(value);\n    notifyListeners();\n  }\n\n  bool get isSecure => _isSecure;\n\n  set isSecure(bool value) {\n    if (value != _isSecure) {\n      _isSecure = value;\n      notifyListeners();\n    }\n  }\n\n  void updateWithValue(WebViewModel webViewModel) {\n    tabIndex = webViewModel.tabIndex;\n    url = webViewModel.url;\n    title = webViewModel.title;\n    favicon = webViewModel.favicon;\n    progress = webViewModel.progress;\n    loaded = webViewModel.loaded;\n    isDesktopMode = webViewModel.isDesktopMode;\n    isIncognitoMode = webViewModel.isIncognitoMode;\n    setJavaScriptConsoleResults(\n        webViewModel._javaScriptConsoleResults.toList());\n    setJavaScriptConsoleHistory(\n        webViewModel._javaScriptConsoleHistory.toList());\n    setLoadedResources(webViewModel._loadedResources.toList());\n    isSecure = webViewModel.isSecure;\n    settings = webViewModel.settings;\n    webViewController = webViewModel.webViewController;\n    pullToRefreshController = webViewModel.pullToRefreshController;\n    findInteractionController = webViewModel.findInteractionController;\n  }\n\n  static WebViewModel? fromMap(Map<String, dynamic>? map) {\n    return map != null\n        ? WebViewModel(\n            tabIndex: map[\"tabIndex\"],\n            url: map[\"url\"] != null ? WebUri(map[\"url\"]) : null,\n            title: map[\"title\"],\n            favicon: map[\"favicon\"] != null\n                ? Favicon(\n                    url: WebUri(map[\"favicon\"][\"url\"]),\n                    rel: map[\"favicon\"][\"rel\"],\n                    width: map[\"favicon\"][\"width\"],\n                    height: map[\"favicon\"][\"height\"],\n                  )\n                : null,\n            progress: map[\"progress\"],\n            isDesktopMode: map[\"isDesktopMode\"],\n            isIncognitoMode: map[\"isIncognitoMode\"],\n            javaScriptConsoleHistory:\n                map[\"javaScriptConsoleHistory\"]?.cast<String>(),\n            isSecure: map[\"isSecure\"],\n            settings: InAppWebViewSettings.fromMap(map[\"settings\"]),\n            createdTime: map[\"createdTime\"] != null\n                ? DateTime.tryParse(map[\"createdTime\"])\n                : null,\n            lastOpenedTime: map[\"lastOpenedTime\"] != null\n                ? DateTime.tryParse(map[\"lastOpenedTime\"])\n                : null,\n          )\n        : null;\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"tabIndex\": _tabIndex,\n      \"url\": _url?.toString(),\n      \"title\": _title,\n      \"favicon\": _favicon?.toMap(),\n      \"progress\": _progress,\n      \"isDesktopMode\": _isDesktopMode,\n      \"isIncognitoMode\": _isIncognitoMode,\n      \"javaScriptConsoleHistory\": _javaScriptConsoleHistory,\n      \"isSecure\": _isSecure,\n      \"settings\": settings?.toMap(),\n      \"screenshot\": screenshot,\n      \"createdTime\": _createdTime.toIso8601String(),\n      \"lastOpenedTime\": _lastOpenedTime.toIso8601String(),\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n"
  },
  {
    "path": "lib/models/window_model.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:math';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/widgets.dart';\nimport 'package:flutter_browser/main.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/webview_tab.dart';\nimport 'package:uuid/uuid.dart';\nimport 'package:collection/collection.dart';\n\nimport '../util.dart';\n\nclass WindowModel extends ChangeNotifier {\n  String _id;\n  String _name = '';\n  DateTime _updatedTime;\n  final DateTime _createdTime;\n  final List<WebViewTab> _webViewTabs = [];\n  int _currentTabIndex = -1;\n  late WebViewModel _currentWebViewModel;\n  bool _shouldSave = false;\n  bool _showTabScroller = false;\n\n  bool get showTabScroller => _showTabScroller;\n\n  set showTabScroller(bool value) {\n    if (value != _showTabScroller) {\n      _showTabScroller = value;\n      notifyListeners();\n    }\n  }\n\n  bool get shouldSave => _shouldSave;\n\n  set shouldSave(bool value) {\n    _shouldSave = value;\n    if (_shouldSave) {\n      saveInfo();\n    } else {\n      removeInfo();\n    }\n    notifyListeners();\n  }\n\n  DateTime get createdTime => _createdTime;\n\n  DateTime get updatedTime => _updatedTime;\n\n  WindowModel(\n      {String? id,\n      String? name,\n      bool? shouldSave,\n      DateTime? updatedTime,\n      DateTime? createdTime})\n      : _id = id ?? 'window_${const Uuid().v4()}',\n        _name = name ?? '',\n        _shouldSave = Util.isMobile() ? true : (shouldSave ?? false),\n        _createdTime = createdTime ?? DateTime.now(),\n        _updatedTime = updatedTime ?? DateTime.now() {\n    _currentWebViewModel = WebViewModel();\n  }\n\n  String get id => _id;\n\n  UnmodifiableListView<WebViewTab> get webViewTabs =>\n      UnmodifiableListView(_webViewTabs);\n\n  String get name => _name;\n\n  set name(String value) {\n    _name = value;\n\n    notifyListeners();\n  }\n\n  void addTab(WebViewTab webViewTab) {\n    _webViewTabs.add(webViewTab);\n    _currentTabIndex = _webViewTabs.length - 1;\n    webViewTab.webViewModel.tabIndex = _currentTabIndex;\n    webViewTab.webViewModel.lastOpenedTime = DateTime.now();\n\n    _currentWebViewModel.updateWithValue(webViewTab.webViewModel);\n\n    notifyListeners();\n  }\n\n  void addTabs(List<WebViewTab> webViewTabs) {\n    for (var webViewTab in webViewTabs) {\n      _webViewTabs.add(webViewTab);\n      webViewTab.webViewModel.tabIndex = _webViewTabs.length - 1;\n    }\n    _currentTabIndex = _webViewTabs.length - 1;\n    if (_currentTabIndex >= 0) {\n      webViewTabs.last.webViewModel.lastOpenedTime = DateTime.now();\n      _currentWebViewModel.updateWithValue(webViewTabs.last.webViewModel);\n    }\n\n    notifyListeners();\n  }\n\n  void closeTab(int index) {\n    final webViewTab = _webViewTabs[index];\n    _webViewTabs.removeAt(index);\n    InAppWebViewController.disposeKeepAlive(webViewTab.webViewModel.keepAlive);\n\n    if (Util.isMobile() || _currentTabIndex >= _webViewTabs.length) {\n      _currentTabIndex = _webViewTabs.length - 1;\n    }\n\n    for (int i = index; i < _webViewTabs.length; i++) {\n      _webViewTabs[i].webViewModel.tabIndex = i;\n    }\n\n    if (_currentTabIndex >= 0) {\n      _currentWebViewModel\n          .updateWithValue(_webViewTabs[_currentTabIndex].webViewModel);\n    } else {\n      _currentWebViewModel.updateWithValue(WebViewModel());\n    }\n\n    notifyListeners();\n  }\n\n  void showTab(int index) {\n    if (_currentTabIndex != index) {\n      _currentTabIndex = index;\n      final webViewModel = _webViewTabs[_currentTabIndex].webViewModel;\n      webViewModel.lastOpenedTime = DateTime.now();\n      _currentWebViewModel.updateWithValue(webViewModel);\n\n      notifyListeners();\n    }\n  }\n\n  void closeAllTabs() {\n    for (final webViewTab in _webViewTabs) {\n      InAppWebViewController.disposeKeepAlive(\n          webViewTab.webViewModel.keepAlive);\n    }\n    _webViewTabs.clear();\n    _currentTabIndex = -1;\n    _currentWebViewModel.updateWithValue(WebViewModel());\n\n    notifyListeners();\n  }\n\n  int getCurrentTabIndex() {\n    return _currentTabIndex;\n  }\n\n  WebViewTab? getCurrentTab() {\n    return _currentTabIndex >= 0 ? _webViewTabs[_currentTabIndex] : null;\n  }\n\n  void notifyWebViewTabUpdated() {\n    notifyListeners();\n  }\n\n  void setCurrentWebViewModel(WebViewModel webViewModel) {\n    _currentWebViewModel = webViewModel;\n  }\n\n  DateTime _lastTrySave = DateTime.now();\n  Timer? _timerSave;\n\n  Future<void> saveInfo() async {\n    _timerSave?.cancel();\n\n    if (!_shouldSave) {\n      return;\n    }\n\n    if (DateTime.now().difference(_lastTrySave) >=\n        const Duration(milliseconds: 400)) {\n      _lastTrySave = DateTime.now();\n      await flushInfo();\n    } else {\n      _lastTrySave = DateTime.now();\n      _timerSave = Timer(const Duration(milliseconds: 500), () {\n        saveInfo();\n      });\n    }\n  }\n\n  Future<void> removeInfo() async {\n    final count = await db?.rawDelete('DELETE FROM windows WHERE id = ?', [id]);\n    if ((count == null || count == 0) && kDebugMode) {\n      print(\"Cannot delete window $id\");\n    }\n  }\n\n  Future<void> flushInfo() async {\n    if (!_shouldSave) {\n      return;\n    }\n    _updatedTime = DateTime.now();\n    final window =\n        await db?.rawQuery('SELECT * FROM windows WHERE id = ?', [id]);\n    int? count;\n    if (window == null || window.isEmpty) {\n      count = await db?.rawInsert('INSERT INTO windows(id, json) VALUES(?, ?)',\n          [id, json.encode(toJson())]);\n    } else {\n      count = await db?.rawUpdate('UPDATE windows SET json = ? WHERE id = ?',\n          [json.encode(toJson()), id]);\n    }\n    if ((count == null || count == 0) && kDebugMode) {\n      print(\"Cannot insert/update window $id\");\n    }\n  }\n\n  Future<void> restoreInfo() async {\n    try {\n      List<Map<String, Object?>>? windows;\n\n      if (Util.isDesktop()) {\n        if (windowModelId != null) {\n          windows = await db\n              ?.rawQuery('SELECT * FROM windows WHERE id = ?', [windowModelId]);\n        } else {\n          return;\n        }\n      } else {\n        windows = await db?.rawQuery('SELECT * FROM windows');\n      }\n\n      if (windows == null || windows.isEmpty) {\n        return;\n      }\n\n      final w = windows[0];\n      _id = w['id'] as String;\n\n      final source = w['json'] as String;\n      final browserData = json.decode(source);\n\n      _shouldSave = browserData[\"shouldSave\"] ?? false;\n\n      closeAllTabs();\n\n      List<Map<String, dynamic>> webViewTabList =\n          browserData[\"webViewTabs\"]?.cast<Map<String, dynamic>>() ?? [];\n      List<WebViewTab> webViewTabs = webViewTabList\n          .map((e) => WebViewTab(\n                key: GlobalKey(),\n                webViewModel: WebViewModel.fromMap(e)!,\n              ))\n          .toList();\n      webViewTabs.sort((a, b) =>\n          a.webViewModel.tabIndex!.compareTo(b.webViewModel.tabIndex!));\n\n      addTabs(webViewTabs);\n\n      int currentTabIndex = browserData[\"currentTabIndex\"] ?? _currentTabIndex;\n      currentTabIndex = min(currentTabIndex, _webViewTabs.length - 1);\n\n      if (currentTabIndex >= 0) {\n        showTab(currentTabIndex);\n      }\n\n      await flushInfo();\n    } catch (e) {\n      if (kDebugMode) {\n        print(e);\n      }\n      return;\n    }\n  }\n\n  static WindowModel fromMap(Map<String, dynamic> map) {\n    final window = WindowModel(\n        id: map[\"id\"],\n        name: map[\"name\"],\n        shouldSave: map[\"shouldSave\"],\n        updatedTime: map[\"updatedTime\"] != null\n            ? DateTime.tryParse(map[\"updatedTime\"])\n            : null,\n        createdTime: map[\"createdTime\"] != null\n            ? DateTime.tryParse(map[\"createdTime\"])\n            : null);\n    List<Map<String, dynamic>> webViewTabList =\n        map[\"webViewTabs\"]?.cast<Map<String, dynamic>>() ?? [];\n    List<WebViewTab> webViewTabs = webViewTabList\n        .map((e) => WebViewTab(\n              key: GlobalKey(),\n              webViewModel: WebViewModel.fromMap(e)!,\n            ))\n        .toList();\n    webViewTabs.sort(\n        (a, b) => a.webViewModel.tabIndex!.compareTo(b.webViewModel.tabIndex!));\n    window.addTabs(webViewTabs);\n    return window;\n  }\n\n  Map<String, dynamic> toMap() {\n    return {\n      \"id\": _id,\n      \"name\": _name,\n      \"webViewTabs\": _webViewTabs.map((e) => e.webViewModel.toMap()).toList(),\n      \"currentTabIndex\": _currentTabIndex,\n      \"currentWebViewModel\": _currentWebViewModel.toMap(),\n      \"shouldSave\": _shouldSave,\n      \"updatedTime\": _updatedTime.toIso8601String(),\n      \"createdTime\": _createdTime.toIso8601String()\n    };\n  }\n\n  Map<String, dynamic> toJson() {\n    return toMap();\n  }\n\n  @override\n  String toString() {\n    return toMap().toString();\n  }\n}\n"
  },
  {
    "path": "lib/multiselect_dialog.dart",
    "content": "import 'package:flutter/material.dart';\n\nconst EdgeInsets _defaultInsetPadding =\n    EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0);\n\nclass MultiSelectDialogItem<V> {\n  const MultiSelectDialogItem({required this.value, required this.label});\n\n  final V value;\n  final String label;\n}\n\nclass MultiSelectDialog<V> extends StatefulWidget {\n  const MultiSelectDialog(\n      {super.key,\n      this.title,\n      this.titlePadding,\n      this.titleTextStyle,\n      this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),\n      this.contentTextStyle,\n      this.actionsPadding = EdgeInsets.zero,\n      this.actionsOverflowDirection,\n      this.actionsOverflowButtonSpacing,\n      this.buttonPadding,\n      this.backgroundColor,\n      this.elevation,\n      this.semanticLabel,\n      this.insetPadding = _defaultInsetPadding,\n      this.clipBehavior = Clip.none,\n      this.shape,\n      this.items,\n      this.initialSelectedValues});\n\n  final Widget? title;\n  final EdgeInsetsGeometry? titlePadding;\n  final TextStyle? titleTextStyle;\n  final EdgeInsetsGeometry contentPadding;\n  final TextStyle? contentTextStyle;\n  final EdgeInsetsGeometry actionsPadding;\n  final VerticalDirection? actionsOverflowDirection;\n  final double? actionsOverflowButtonSpacing;\n  final EdgeInsetsGeometry? buttonPadding;\n  final Color? backgroundColor;\n  final double? elevation;\n  final String? semanticLabel;\n  final EdgeInsets insetPadding;\n  final Clip clipBehavior;\n  final ShapeBorder? shape;\n  final List<MultiSelectDialogItem<V>>? items;\n  final Set<V>? initialSelectedValues;\n\n  @override\n  State<StatefulWidget> createState() => _MultiSelectDialogState<V>();\n}\n\nclass _MultiSelectDialogState<V> extends State<MultiSelectDialog<V>> {\n  final _selectedValues = <V>{};\n\n  @override\n  void initState() {\n    super.initState();\n    if (widget.initialSelectedValues != null) {\n      _selectedValues.addAll(widget.initialSelectedValues!);\n    }\n  }\n\n  void _onItemCheckedChange(V itemValue, bool checked) {\n    setState(() {\n      if (checked) {\n        _selectedValues.add(itemValue);\n      } else {\n        _selectedValues.remove(itemValue);\n      }\n    });\n  }\n\n  void _onCancelTap() {\n    Navigator.pop(context);\n  }\n\n  void _onSubmitTap() {\n    Navigator.pop(context, _selectedValues);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AlertDialog(\n      title: widget.title,\n      content: SingleChildScrollView(\n        child: ListTileTheme(\n          contentPadding: const EdgeInsets.fromLTRB(14.0, 0.0, 24.0, 0.0),\n          child: ListBody(\n            children: widget.items?.map(_buildItem).toList() ?? <Widget>[],\n          ),\n        ),\n      ),\n      actions: <Widget>[\n        ElevatedButton(\n          onPressed: _onCancelTap,\n          child: const Text('CANCEL'),\n        ),\n        ElevatedButton(\n          onPressed: _onSubmitTap,\n          child: const Text('OK'),\n        )\n      ],\n      titlePadding: widget.titlePadding,\n      titleTextStyle: widget.titleTextStyle,\n      contentPadding: widget.contentPadding,\n      contentTextStyle: widget.contentTextStyle,\n      actionsPadding: widget.actionsPadding,\n      actionsOverflowDirection: widget.actionsOverflowDirection,\n      actionsOverflowButtonSpacing: widget.actionsOverflowButtonSpacing,\n      buttonPadding: widget.buttonPadding,\n      backgroundColor: widget.backgroundColor,\n      elevation: widget.elevation,\n      semanticLabel: widget.semanticLabel,\n      insetPadding: widget.insetPadding,\n      clipBehavior: widget.clipBehavior,\n      shape: widget.shape,\n    );\n  }\n\n  Widget _buildItem(MultiSelectDialogItem<V> item) {\n    final checked = _selectedValues.contains(item.value);\n    return CheckboxListTile(\n      value: checked,\n      title: Text(item.label),\n      controlAffinity: ListTileControlAffinity.leading,\n      onChanged: (checked) =>\n          checked != null ? _onItemCheckedChange(item.value, checked) : null,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/developers/javascript_console.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/javascript_console_result.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:provider/provider.dart';\n\nimport '../../models/window_model.dart';\n\nclass JavaScriptConsole extends StatefulWidget {\n  const JavaScriptConsole({super.key});\n\n  @override\n  State<JavaScriptConsole> createState() => _JavaScriptConsoleState();\n}\n\nclass _JavaScriptConsoleState extends State<JavaScriptConsole> {\n  final TextEditingController _customJavaScriptController =\n      TextEditingController();\n  final ScrollController _scrollController = ScrollController();\n\n  int currentJavaScriptHistory = 0;\n\n  @override\n  void dispose() {\n    _customJavaScriptController.dispose();\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return _buildJavaScriptConsole();\n  }\n\n  Widget _buildJavaScriptConsole() {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        Flexible(\n          child: Selector<WebViewModel, List<Widget>>(\n            selector: (context, webViewModel) =>\n                webViewModel.javaScriptConsoleResults,\n            builder: (context, javaScriptConsoleResults, child) {\n              return ListView.builder(\n                controller: _scrollController,\n                itemCount: javaScriptConsoleResults.length,\n                itemBuilder: (context, index) {\n                  return javaScriptConsoleResults[index];\n                },\n              );\n            },\n          ),\n        ),\n        const Divider(),\n        SizedBox(\n          height: 75.0,\n          child: Row(\n            children: <Widget>[\n              Flexible(\n                child: TextField(\n                  expands: true,\n                  onSubmitted: (value) {\n                    evaluateJavaScript(value);\n                  },\n                  controller: _customJavaScriptController,\n                  keyboardType: TextInputType.multiline,\n                  maxLines: null,\n                  decoration: const InputDecoration(\n                      hintText: \"document.querySelector('body') ...\",\n                      prefixIcon:\n                          Icon(Icons.keyboard_arrow_right, color: Colors.blue),\n                      border: InputBorder.none),\n                ),\n              ),\n              IconButton(\n                icon: const Icon(Icons.play_arrow),\n                onPressed: () {\n                  evaluateJavaScript(_customJavaScriptController.text);\n                },\n              ),\n              Selector<WebViewModel, List<String>>(\n                  selector: (context, webViewModel) =>\n                      webViewModel.javaScriptConsoleHistory,\n                  builder: (context, javaScriptConsoleHistory, child) {\n                    currentJavaScriptHistory = javaScriptConsoleHistory.length;\n\n                    return Column(\n                      mainAxisSize: MainAxisSize.max,\n                      children: <Widget>[\n                        SizedBox(\n                          height: 35.0,\n                          child: IconButton(\n                            icon: const Icon(Icons.keyboard_arrow_up),\n                            onPressed: () {\n                              currentJavaScriptHistory--;\n                              if (currentJavaScriptHistory < 0) {\n                                currentJavaScriptHistory = 0;\n                              } else {\n                                _customJavaScriptController.text =\n                                    javaScriptConsoleHistory[\n                                        currentJavaScriptHistory];\n                              }\n                            },\n                          ),\n                        ),\n                        SizedBox(\n                          height: 35.0,\n                          child: IconButton(\n                            icon: const Icon(Icons.keyboard_arrow_down),\n                            onPressed: () {\n                              if (currentJavaScriptHistory + 1 >=\n                                  javaScriptConsoleHistory.length) {\n                                currentJavaScriptHistory =\n                                    javaScriptConsoleHistory.length;\n                                _customJavaScriptController.text = \"\";\n                              } else {\n                                currentJavaScriptHistory++;\n                                _customJavaScriptController.text =\n                                    javaScriptConsoleHistory[\n                                        currentJavaScriptHistory];\n                              }\n                            },\n                          ),\n                        )\n                      ],\n                    );\n                  }),\n              IconButton(\n                icon: const Icon(Icons.cancel),\n                onPressed: () {\n                  final windowModel = Provider.of<WindowModel>(context, listen: false);\n                  var webViewModel = windowModel.getCurrentTab()?.webViewModel;\n                  if (webViewModel != null) {\n                    webViewModel.setJavaScriptConsoleResults([]);\n\n                    var currentWebViewModel =\n                        Provider.of<WebViewModel>(context, listen: false);\n                    currentWebViewModel.updateWithValue(webViewModel);\n                  }\n                },\n              )\n            ],\n          ),\n        )\n      ],\n    );\n  }\n\n  void evaluateJavaScript(String source) async {\n    final windowModel = Provider.of<WindowModel>(context, listen: false);\n    final webViewModel = windowModel.getCurrentTab()?.webViewModel;\n\n    if (webViewModel != null) {\n      var currentWebViewModel =\n          Provider.of<WebViewModel>(context, listen: false);\n\n      if (source.isNotEmpty &&\n          (webViewModel.javaScriptConsoleHistory.isEmpty ||\n              (webViewModel.javaScriptConsoleHistory.isNotEmpty &&\n                  webViewModel.javaScriptConsoleHistory.last != source))) {\n        webViewModel.addJavaScriptConsoleHistory(source);\n        currentWebViewModel.updateWithValue(webViewModel);\n      }\n\n      var result = await webViewModel.webViewController\n          ?.evaluateJavascript(source: source);\n\n      webViewModel.addJavaScriptConsoleResults(\n          JavaScriptConsoleResult(data: result.toString()));\n      currentWebViewModel.updateWithValue(webViewModel);\n\n      setState(() {\n        Future.delayed(const Duration(milliseconds: 100), () async {\n          await _scrollController.animateTo(\n              _scrollController.position.maxScrollExtent,\n              duration: const Duration(milliseconds: 100),\n              curve: Curves.ease);\n          // must be repeated, otherwise it won't scroll to the bottom sometimes\n          await _scrollController.animateTo(\n              _scrollController.position.maxScrollExtent,\n              duration: const Duration(milliseconds: 100),\n              curve: Curves.ease);\n        });\n      });\n    }\n  }\n}\n"
  },
  {
    "path": "lib/pages/developers/main.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/app_bar/custom_app_bar_wrapper.dart';\nimport 'package:flutter_browser/pages/developers/javascript_console.dart';\nimport 'package:flutter_browser/pages/developers/network_info.dart';\nimport 'package:flutter_browser/pages/developers/storage_manager.dart';\n\nclass DevelopersPage extends StatefulWidget {\n  const DevelopersPage({super.key});\n\n  @override\n  State<DevelopersPage> createState() => _DevelopersPageState();\n}\n\nclass _DevelopersPageState extends State<DevelopersPage> {\n  @override\n  Widget build(BuildContext context) {\n    return DefaultTabController(\n      length: 3,\n      child: Scaffold(\n        appBar: CustomAppBarWrapper(\n            appBar: AppBar(\n          bottom: TabBar(\n            onTap: (value) {\n              FocusScope.of(context).unfocus();\n            },\n            tabs: const [\n              Tab(\n                icon: Icon(Icons.code),\n                text: \"JavaScript Console\",\n              ),\n              Tab(\n                icon: Icon(Icons.network_check),\n                text: \"Network Info\",\n              ),\n              Tab(\n                icon: Icon(Icons.storage),\n                text: \"Storage Manager\",\n              ),\n            ],\n          ),\n          title: const Text('Developers'),\n        )),\n        body: const TabBarView(\n          physics: NeverScrollableScrollPhysics(),\n          children: [\n            JavaScriptConsole(),\n            NetworkInfo(),\n            StorageManager(),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/developers/network_info.dart",
    "content": "// import 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/custom_image.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:flutter_svg/flutter_svg.dart';\nimport 'package:mime/mime.dart';\nimport 'package:provider/provider.dart';\n// import 'package:charts_flutter/flutter.dart' as charts;\n\nclass NetworkInfo extends StatefulWidget {\n  const NetworkInfo({super.key});\n\n  @override\n  State<NetworkInfo> createState() => _NetworkInfoState();\n}\n\nclass _NetworkInfoState extends State<NetworkInfo> {\n  @override\n  Widget build(BuildContext context) {\n    return _buildNetworkInfo();\n  }\n\n  Widget _buildNetworkInfo() {\n    return LayoutBuilder(builder: (context, constraints) {\n      return Selector<WebViewModel, List<LoadedResource>>(\n          selector: (context, webViewModel) => webViewModel.loadedResources,\n          builder: (context, loadedResources, child) {\n            var textStyle = const TextStyle(fontSize: 14.0);\n\n            var listViewChildren = <Widget>[\n              // Container(\n              //   height: 200.0,\n              //   padding: EdgeInsets.only(left: 10.0),\n              //   child: charts.ScatterPlotChart(\n              //       _createChartData(loadedResources),\n              //       animate: false,\n              //       behaviors: [\n              //         charts.SlidingViewport(),\n              //         charts.PanAndZoomBehavior(),\n              //       ],\n              //       defaultRenderer:\n              //           charts.PointRendererConfig(pointRendererDecorators: [\n              //         charts.ComparisonPointsDecorator(\n              //             symbolRenderer: charts.CylinderSymbolRenderer())\n              //       ])),\n              // ),\n              Row(\n                children: <Widget>[\n                  Container(\n                    width: constraints.maxWidth / 3.0,\n                    alignment: Alignment.center,\n                    child: const Text(\n                      \"Name\",\n                      style: TextStyle(\n                          fontWeight: FontWeight.bold, fontSize: 16.0),\n                    ),\n                  ),\n                  Container(\n                    width: constraints.maxWidth / 4,\n                    alignment: Alignment.center,\n                    child: const Text(\n                      \"Domain\",\n                      style: TextStyle(\n                          fontWeight: FontWeight.bold, fontSize: 16.0),\n                    ),\n                  ),\n                  Container(\n                    width: constraints.maxWidth / 4,\n                    alignment: Alignment.center,\n                    child: const Text(\n                      \"Type\",\n                      style: TextStyle(\n                          fontWeight: FontWeight.bold, fontSize: 16.0),\n                    ),\n                  ),\n                  Flexible(\n                    child: Container(\n                      alignment: Alignment.center,\n                      child: const Text(\n                        \"Time\",\n                        style: TextStyle(\n                            fontWeight: FontWeight.bold, fontSize: 16.0),\n                      ),\n                    ),\n                  ),\n                ],\n              )\n            ];\n\n            listViewChildren\n                .addAll(loadedResources.reversed.map((loadedResoruce) {\n              var url = loadedResoruce.url ?? Uri.parse(\"about:blank\");\n              String path = url.path;\n              String resourceName = path.substring(path.lastIndexOf('/') + 1);\n\n              String domain = url.host.replaceFirst(\"www.\", \"\");\n\n              IconData iconData;\n              switch (loadedResoruce.initiatorType) {\n                case \"script\":\n                  iconData = Icons.format_align_left;\n                  break;\n                case \"css\":\n                  iconData = Icons.color_lens;\n                  break;\n                case \"xmlhttprequest\":\n                  iconData = Icons.http;\n                  break;\n                case \"link\":\n                  iconData = Icons.link;\n                  break;\n                default:\n                  iconData = Icons.insert_drive_file;\n              }\n\n              Widget icon;\n              var mimeType = lookupMimeType(url.toString());\n\n              if (mimeType != null &&\n                  mimeType.startsWith(\"image/\") &&\n                  mimeType != \"image/svg+xml\") {\n                // icon = CachedNetworkImage(\n                //   imageUrl: url.toString(),\n                //   width: 20.0,\n                //   height: 20.0,\n                //   errorWidget: (context, url, error) {\n                //     return Icon(\n                //       Icons.broken_image,\n                //       size: 20.0,\n                //     );\n                //   },\n                // );\n                icon = CustomImage(url: url, maxWidth: 20.0, height: 20.0);\n              } else if (mimeType == \"image/svg+xml\") {\n                icon = SvgPicture.network(\n                  url.toString(),\n                  width: 20.0,\n                  height: 20.0,\n                );\n              } else {\n                icon = Icon(\n                  iconData,\n                  size: 20.0,\n                );\n              }\n\n              return Row(children: <Widget>[\n                InkWell(\n                    onTap: () {\n                      Clipboard.setData(\n                          ClipboardData(text: loadedResoruce.url?.toString() ?? ''));\n                    },\n                    child: Container(\n                      padding: const EdgeInsets.symmetric(\n                          vertical: 5.0, horizontal: 2.5),\n                      width: constraints.maxWidth / 3.0,\n                      child: Row(\n                        children: <Widget>[\n                          SizedBox(\n                            height: 20.0,\n                            width: 20.0,\n                            child: icon,\n                          ),\n                          const SizedBox(\n                            width: 10.0,\n                          ),\n                          Expanded(\n                            child: Text(\n                              resourceName,\n                              overflow: TextOverflow.ellipsis,\n                              style: textStyle,\n                            ),\n                          )\n                        ],\n                      ),\n                    )),\n                Container(\n                  width: constraints.maxWidth / 4,\n                  alignment: Alignment.centerRight,\n                  padding: const EdgeInsets.symmetric(\n                      vertical: 5.0, horizontal: 2.5),\n                  child: Text(domain,\n                      overflow: TextOverflow.ellipsis, style: textStyle),\n                ),\n                Container(\n                  width: constraints.maxWidth / 4,\n                  padding: const EdgeInsets.symmetric(\n                      vertical: 5.0, horizontal: 2.5),\n                  alignment: Alignment.center,\n                  child: Text(loadedResoruce.initiatorType ?? \"\",\n                      style: textStyle),\n                ),\n                Flexible(\n                    child: Container(\n                  padding: const EdgeInsets.symmetric(\n                      vertical: 5.0, horizontal: 2.5),\n                  child: Text(\n                      (loadedResoruce.duration != null)\n                          ? \"${loadedResoruce.duration!.toStringAsFixed(2)} ms\"\n                          : \"\",\n                      style: textStyle),\n                ))\n              ]);\n            }).toList());\n\n            return ListView.builder(\n              itemCount: listViewChildren.length,\n              itemBuilder: (context, index) {\n                return listViewChildren[index];\n              },\n            );\n          });\n    });\n  }\n\n  // List<charts.Series> seriesList;\n  //\n  // /// Create one series with sample hard coded data.\n  // static List<charts.Series<LoadedResource, double>> _createChartData(\n  //     List<LoadedResource> data) {\n  //   return [\n  //     new charts.Series<LoadedResource, double>(\n  //       id: 'LoadedResource',\n  //       // Providing a color function is optional.\n  //       colorFn: (LoadedResource loadedResource, _) {\n  //         return charts.Color(\n  //           r: ((loadedResource.startTime + loadedResource.duration) * 0xFFFFFF)\n  //               .toInt(),\n  //           b: (loadedResource.startTime * 0xFFFFFF).toInt(),\n  //           g: (loadedResource.duration * 0xFFFFFF).toInt(),\n  //         );\n  //       },\n  //       domainFn: (LoadedResource loadedResource, _) =>\n  //           loadedResource.startTime + loadedResource.duration,\n  //       domainLowerBoundFn: (LoadedResource loadedResource, _) =>\n  //           loadedResource.startTime,\n  //       domainUpperBoundFn: (LoadedResource loadedResource, _) =>\n  //           loadedResource.startTime + loadedResource.duration,\n  //       measureFn: (LoadedResource loadedResource, _) =>\n  //           data.indexOf(loadedResource),\n  //       measureLowerBoundFn: (LoadedResource loadedResource, _) =>\n  //           loadedResource.duration,\n  //       measureUpperBoundFn: (LoadedResource loadedResource, _) =>\n  //           loadedResource.duration,\n  //       radiusPxFn: (LoadedResource loadedResource, _) => 2,\n  //       data: data,\n  //     )\n  //   ];\n  // }\n}\n"
  },
  {
    "path": "lib/pages/developers/storage_manager.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/main.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nclass StorageManager extends StatefulWidget {\n  const StorageManager({super.key});\n\n  @override\n  State<StorageManager> createState() => _StorageManagerState();\n}\n\nclass _StorageManagerState extends State<StorageManager> {\n  final CookieManager _cookieManager = CookieManager.instance(webViewEnvironment: webViewEnvironment);\n  final WebStorageManager? _webStorageManager = !Util.isWindows() ? WebStorageManager.instance() : null;\n  final HttpAuthCredentialDatabase? _httpAuthCredentialDatabase = !Util.isWindows() ? HttpAuthCredentialDatabase.instance() : null;\n\n  var cookieNameTrackingEdit = <bool>[];\n  var cookieValueTrackingEdit = <bool>[];\n  var localStorageKeyTrackingEdit = <bool>[];\n  var localStorageValueTrackingEdit = <bool>[];\n  var sessionStorageKeyTrackingEdit = <bool>[];\n  var sessionStorageValueTrackingEdit = <bool>[];\n\n  final TextEditingController _newCookieNameController =\n      TextEditingController();\n  final TextEditingController _newCookieValueController =\n      TextEditingController();\n  final TextEditingController _newCookiePathController =\n      TextEditingController();\n  final TextEditingController _newCookieDomainController =\n      TextEditingController();\n\n  final TextEditingController _newLocalStorageKeyController =\n      TextEditingController();\n  final TextEditingController _newLocalStorageValueController =\n      TextEditingController();\n\n  final TextEditingController _newSessionStorageKeyController =\n      TextEditingController();\n  final TextEditingController _newSessionStorageValueController =\n      TextEditingController();\n\n  bool _newCookieIsSecure = false;\n  DateTime? _newCookieExpiresDate;\n\n  final _newCookieFormKey = GlobalKey<FormState>();\n  final _newLocalStorageItemFormKey = GlobalKey<FormState>();\n  final _newSessionStorageItemFormKey = GlobalKey<FormState>();\n\n  @override\n  void initState() {\n    super.initState();\n\n    cookieNameTrackingEdit = [];\n    cookieValueTrackingEdit = [];\n    localStorageKeyTrackingEdit = [];\n    localStorageValueTrackingEdit = [];\n    sessionStorageKeyTrackingEdit = [];\n    sessionStorageValueTrackingEdit = [];\n\n    _newCookiePathController.text = \"/\";\n  }\n\n  @override\n  void dispose() {\n    _newCookieNameController.dispose();\n    _newCookieValueController.dispose();\n    _newCookiePathController.dispose();\n    _newCookieDomainController.dispose();\n    _newLocalStorageKeyController.dispose();\n    _newLocalStorageValueController.dispose();\n    _newSessionStorageKeyController.dispose();\n    _newSessionStorageValueController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return _buildStorageManager();\n  }\n\n  Widget _buildStorageManager() {\n    return LayoutBuilder(\n      builder: (context, constraints) {\n        var entryItems = <Widget>[\n          _buildCookiesExpansionTile(constraints),\n          _buildWebLocalStorageExpansionTile(constraints),\n          _buildWebSessionStorageExpansionTile(constraints),\n        ];\n\n        if (!Util.isWindows()) {\n          entryItems.add(\n            _buildHttpAuthCredentialDatabaseExpansionTile(constraints));\n        }\n\n        if (Util.isAndroid()) {\n          entryItems.add(_buildAndroidWebStorageExpansionTile(constraints));\n        } else if (Util.isIOS() || Util.isMacOS()) {\n          entryItems.add(_buildIOSWebStorageExpansionTile(constraints));\n        }\n\n        return ListView.builder(\n          itemCount: entryItems.length,\n          itemBuilder: (context, index) {\n            return entryItems[index];\n          },\n        );\n      },\n    );\n  }\n\n  Widget _buildCookiesExpansionTile(BoxConstraints constraints) {\n    return Selector<WebViewModel, WebUri>(\n      selector: (context, webViewModel) => webViewModel.url!,\n      builder: (context, url, child) {\n        return FutureBuilder(\n          future: _cookieManager.getCookies(url: url),\n          builder: (context, snapshot) {\n            if (!snapshot.hasData) {\n              return Container();\n            }\n\n            List<Cookie> cookies = snapshot.data ?? <Cookie>[];\n\n            var rows = <DataRow>[];\n            if (cookieValueTrackingEdit.length != cookies.length) {\n              cookieNameTrackingEdit = List.filled(cookies.length, false);\n              cookieValueTrackingEdit = List.filled(cookies.length, false);\n            }\n\n            rows.addAll(cookies.map((cookie) {\n              var index = cookies.indexOf(cookie);\n              return DataRow(cells: <DataCell>[\n                _buildDataCellEditable(\n                    width: constraints.maxWidth / 3,\n                    onFieldSubmitted: (newValue) async {\n                      var updateCookie = await _cookieManager.getCookie(\n                          url: url, name: cookie.name);\n                      await _cookieManager.deleteCookie(\n                          url: url, name: cookie.name);\n                      await _cookieManager.setCookie(\n                          url: url,\n                          name: cookie.name,\n                          value: updateCookie?.value ?? \"\");\n                    },\n                    initialValue: cookie.name,\n                    index: index,\n                    trackingEditStatus: cookieNameTrackingEdit),\n                _buildDataCellEditable(\n                    width: constraints.maxWidth / 3,\n                    onFieldSubmitted: (newValue) async {\n                      await _cookieManager.setCookie(\n                          url: url, name: cookie.name, value: newValue);\n                    },\n                    initialValue: cookie.value,\n                    index: index,\n                    trackingEditStatus: cookieValueTrackingEdit),\n                DataCell(IconButton(\n                  icon: const Icon(Icons.cancel),\n                  onPressed: () async {\n                    await _cookieManager.deleteCookie(url: url, name: cookie.name);\n                    setState(() { });\n                  },\n                ))\n              ]);\n            }).toList());\n\n            return ExpansionTile(\n              onExpansionChanged: (value) {\n                FocusScope.of(context).unfocus();\n              },\n              title: const Text(\n                \"Cookies\",\n                style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),\n              ),\n              children: <Widget>[\n                SizedBox(\n                  width: constraints.minWidth,\n                  child: DataTable(\n                    columnSpacing: 0.0,\n                    columns: const <DataColumn>[\n                      DataColumn(\n                        label: Text(\n                          \"Name\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Value\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Delete\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                    ],\n                    rows: rows,\n                  ),\n                ),\n                Form(\n                  key: _newCookieFormKey,\n                  child: Container(\n                    padding: const EdgeInsets.all(10.0),\n                    child: Column(\n                      children: <Widget>[\n                        Row(\n                          children: <Widget>[\n                            Expanded(\n                              child: Container(\n                                padding: const EdgeInsets.only(right: 10.0),\n                                child: TextFormField(\n                                  controller: _newCookieNameController,\n                                  decoration: const InputDecoration(\n                                      labelText: \"Cookie Name\"),\n                                  validator: (value) {\n                                    if (value == null || value.isEmpty) {\n                                      return 'Please enter some text';\n                                    }\n                                    return null;\n                                  },\n                                ),\n                              ),\n                            ),\n                            Expanded(\n                              child: TextFormField(\n                                controller: _newCookieValueController,\n                                decoration: const InputDecoration(\n                                    labelText: \"Cookie Value\"),\n                                validator: (value) {\n                                  if (value == null || value.isEmpty) {\n                                    return 'Please enter some text';\n                                  }\n                                  return null;\n                                },\n                              ),\n                            ),\n                          ],\n                        ),\n                        Row(\n                          children: <Widget>[\n                            Expanded(\n                              child: Container(\n                                padding: const EdgeInsets.only(right: 10.0),\n                                child: TextFormField(\n                                  controller: _newCookieDomainController,\n                                  decoration: const InputDecoration(\n                                      labelText: \"Cookie Domain\"),\n                                ),\n                              ),\n                            ),\n                            Expanded(\n                              child: TextFormField(\n                                controller: _newCookiePathController,\n                                decoration: const InputDecoration(\n                                    labelText: \"Cookie Path\"),\n                                validator: (value) {\n                                  if (value == null || value.isEmpty) {\n                                    return 'Please enter some text';\n                                  }\n                                  return null;\n                                },\n                              ),\n                            ),\n                          ],\n                        ),\n                        Row(\n                          children: <Widget>[\n                            Expanded(\n                              child: Row(\n                                children: <Widget>[\n                                  Expanded(\n                                    child: ListTile(\n                                      title: const Text(\"Expires in:\"),\n                                      subtitle: Text(\n                                          _newCookieExpiresDate != null\n                                              ? _newCookieExpiresDate!\n                                                  .toIso8601String()\n                                              : \"Select a date ...\"),\n                                      onTap: () async {\n                                        FocusScope.of(context).unfocus();\n                                        _newCookieExpiresDate = await showDatePicker(\n                                            context: context,\n                                            initialDate: _newCookieExpiresDate,\n                                            firstDate: DateTime.now(),\n                                            lastDate: DateTime(9999),\n                                        );\n                                        setState(() { });\n                                      },\n                                    ),\n                                  ),\n                                  Center(\n                                    child: IconButton(\n                                      icon: const Icon(Icons.clear),\n                                      onPressed: () {\n                                        setState(() {\n                                          _newCookieExpiresDate = null;\n                                        });\n                                      },\n                                    ),\n                                  ),\n                                ],\n                              ),\n                            ),\n                            Expanded(\n                                child: CheckboxListTile(\n                              title: const Text(\"Is Secure?\"),\n                              value: _newCookieIsSecure,\n                              onChanged: (newValue) {\n                                setState(() {\n                                  _newCookieIsSecure = newValue!;\n                                });\n                              }, //  <-- leading Checkbox\n                            )),\n                          ],\n                        ),\n                        SizedBox(\n                            width: MediaQuery.of(context).size.width,\n                            child: TextButton(\n                              child: const Text(\"Add Cookie\"),\n                              onPressed: () async {\n                                if (_newCookieFormKey.currentState != null &&\n                                    _newCookieFormKey.currentState!\n                                        .validate()) {\n                                  final expiresDate = _newCookieExpiresDate?.millisecondsSinceEpoch;\n\n                                  await _cookieManager.setCookie(\n                                      url: url,\n                                      name: _newCookieNameController.text,\n                                      value: _newCookieValueController.text,\n                                      domain: _newCookieDomainController\n                                              .text.isEmpty\n                                          ? null\n                                          : _newCookieDomainController.text,\n                                      isSecure: _newCookieIsSecure,\n                                      path: _newCookiePathController.text,\n                                      expiresDate: expiresDate);\n\n                                  setState(() {\n                                    _newCookieFormKey.currentState!.reset();\n                                  });\n                                }\n                              },\n                            ))\n                      ],\n                    ),\n                  ),\n                ),\n                Row(\n                  children: <Widget>[\n                    Expanded(\n                      child: TextButton(\n                        child: const Text(\"Clear cookies\"),\n                        onPressed: () async {\n                          await _cookieManager.deleteCookies(url: url);\n                          setState(() {});\n                        },\n                      ),\n                    ),\n                    Expanded(\n                      child: TextButton(\n                        child: const Text(\"Clear all\"),\n                        onPressed: () async {\n                          await _cookieManager.deleteAllCookies();\n                          setState(() {});\n                        },\n                      ),\n                    )\n                  ],\n                )\n              ],\n            );\n          },\n        );\n      },\n    );\n  }\n\n  Widget _buildWebLocalStorageExpansionTile(BoxConstraints constraints) {\n    return Consumer<WebViewModel>(\n      builder: (context, webViewModel, child) {\n        var webViewController = webViewModel.webViewController;\n\n        return FutureBuilder(\n          future: webViewController?.webStorage.localStorage.getItems(),\n          builder: (context, snapshot) {\n            if (!snapshot.hasData) {\n              return Container();\n            }\n\n            List<WebStorageItem> webStorageItems =\n                snapshot.data ?? <WebStorageItem>[];\n\n            var rows = <DataRow>[];\n            if (localStorageValueTrackingEdit.length !=\n                webStorageItems.length) {\n              localStorageKeyTrackingEdit =\n                  List.filled(webStorageItems.length, false);\n              localStorageValueTrackingEdit =\n                  List.filled(webStorageItems.length, false);\n            }\n\n            rows.addAll(webStorageItems.map((webStorageItem) {\n              var index = webStorageItems.indexOf(webStorageItem);\n              return DataRow(cells: <DataCell>[\n                _buildDataCellEditable(\n                    width: constraints.maxWidth / 3,\n                    onFieldSubmitted: (newValue) async {\n                      var updateItemValue = await webViewController\n                          ?.webStorage.localStorage\n                          .getItem(key: webStorageItem.key!);\n                      await webViewController?.webStorage.localStorage\n                          .removeItem(key: webStorageItem.key!);\n                      await webViewController?.webStorage.localStorage\n                          .setItem(key: newValue, value: updateItemValue);\n                    },\n                    initialValue: webStorageItem.key!,\n                    index: index,\n                    trackingEditStatus: localStorageKeyTrackingEdit),\n                _buildDataCellEditable(\n                    width: constraints.maxWidth / 3,\n                    onFieldSubmitted: (newValue) async {\n                      await webViewController?.webStorage.localStorage\n                          .setItem(key: webStorageItem.key!, value: newValue);\n                    },\n                    initialValue: webStorageItem.value,\n                    index: index,\n                    trackingEditStatus: localStorageValueTrackingEdit),\n                DataCell(IconButton(\n                  icon: const Icon(Icons.cancel),\n                  onPressed: () async {\n                    await webViewController?.webStorage.localStorage\n                        .removeItem(key: webStorageItem.key!);\n                    setState(() {});\n                  },\n                ))\n              ]);\n            }).toList());\n\n            return ExpansionTile(\n              onExpansionChanged: (value) {\n                FocusScope.of(context).unfocus();\n              },\n              title: const Text(\n                \"Local Storage\",\n                style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),\n              ),\n              children: <Widget>[\n                SizedBox(\n                  width: constraints.minWidth,\n                  child: DataTable(\n                    columnSpacing: 0.0,\n                    columns: const <DataColumn>[\n                      DataColumn(\n                        label: Text(\n                          \"Key\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Value\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Delete\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                    ],\n                    rows: rows,\n                  ),\n                ),\n                _buildAddNewWebStorageItem(\n                  formKey: _newLocalStorageItemFormKey,\n                  nameController: _newLocalStorageKeyController,\n                  valueController: _newLocalStorageValueController,\n                  labelName: \"Local Item Key\",\n                  labelValue: \"Local Item Value\",\n                  onAdded: (name, value) {\n                    webViewController?.webStorage.localStorage\n                        .setItem(key: name, value: value);\n                  },\n                ),\n                SizedBox(\n                  width: MediaQuery.of(context).size.width,\n                  child: TextButton(\n                    child: const Text(\"Clear items\"),\n                    onPressed: () async {\n                      await webViewController?.webStorage.localStorage.clear();\n                      setState(() {});\n                    },\n                  ),\n                ),\n              ],\n            );\n          },\n        );\n      },\n    );\n  }\n\n  Widget _buildWebSessionStorageExpansionTile(BoxConstraints constraints) {\n    return Consumer<WebViewModel>(\n      builder: (context, webViewModel, child) {\n        var webViewController = webViewModel.webViewController;\n\n        return FutureBuilder(\n          future: webViewController?.webStorage.sessionStorage.getItems(),\n          builder: (context, snapshot) {\n            if (!snapshot.hasData) {\n              return Container();\n            }\n\n            List<WebStorageItem> webStorageItems =\n                snapshot.data ?? <WebStorageItem>[];\n\n            var rows = <DataRow>[];\n\n            if (sessionStorageValueTrackingEdit.length !=\n                webStorageItems.length) {\n              sessionStorageKeyTrackingEdit =\n                  List.filled(webStorageItems.length, false);\n              sessionStorageValueTrackingEdit =\n                  List.filled(webStorageItems.length, false);\n            }\n\n            rows.addAll(webStorageItems.map((webStorageItem) {\n              var index = webStorageItems.indexOf(webStorageItem);\n\n              return DataRow(cells: <DataCell>[\n                _buildDataCellEditable(\n                    width: constraints.maxWidth / 3,\n                    onFieldSubmitted: (newValue) async {\n                      var updateItemValue = await webViewController\n                          ?.webStorage.sessionStorage\n                          .getItem(key: webStorageItem.key!);\n                      await webViewController?.webStorage.sessionStorage\n                          .removeItem(key: webStorageItem.key!);\n                      await webViewController?.webStorage.sessionStorage\n                          .setItem(key: newValue, value: updateItemValue);\n                    },\n                    initialValue: webStorageItem.key!,\n                    index: index,\n                    trackingEditStatus: sessionStorageKeyTrackingEdit),\n                _buildDataCellEditable(\n                    width: constraints.maxWidth / 3,\n                    onFieldSubmitted: (newValue) async {\n                      await webViewController?.webStorage.sessionStorage\n                          .setItem(key: webStorageItem.key!, value: newValue);\n                    },\n                    initialValue: webStorageItem.value,\n                    index: index,\n                    trackingEditStatus: sessionStorageValueTrackingEdit),\n                DataCell(IconButton(\n                  icon: const Icon(Icons.cancel),\n                  onPressed: () async {\n                    await webViewController?.webStorage.sessionStorage\n                        .removeItem(key: webStorageItem.key!);\n                    setState(() {});\n                  },\n                ))\n              ]);\n            }).toList());\n\n            return ExpansionTile(\n              onExpansionChanged: (value) {\n                FocusScope.of(context).unfocus();\n              },\n              title: const Text(\n                \"Session Storage\",\n                style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),\n              ),\n              children: <Widget>[\n                SizedBox(\n                  width: constraints.minWidth,\n                  child: DataTable(\n                    columnSpacing: 0.0,\n                    columns: const <DataColumn>[\n                      DataColumn(\n                        label: Text(\n                          \"Key\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Value\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Delete\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                    ],\n                    rows: rows,\n                  ),\n                ),\n                _buildAddNewWebStorageItem(\n                  formKey: _newSessionStorageItemFormKey,\n                  nameController: _newSessionStorageKeyController,\n                  valueController: _newSessionStorageValueController,\n                  labelName: \"Session Item Key\",\n                  labelValue: \"Session Item Value\",\n                  onAdded: (name, value) async {\n                    await webViewController?.webStorage.sessionStorage\n                        .setItem(key: name, value: value);\n                    setState(() {});\n                  },\n                ),\n                SizedBox(\n                  width: MediaQuery.of(context).size.width,\n                  child: TextButton(\n                    child: const Text(\"Clear items\"),\n                    onPressed: () async {\n                      await webViewController?.webStorage.sessionStorage\n                          .clear();\n                      setState(() {});\n                    },\n                  ),\n                ),\n              ],\n            );\n          },\n        );\n      },\n    );\n  }\n\n  Widget _buildAndroidWebStorageExpansionTile(BoxConstraints constraints) {\n    var children = <Widget>[\n      ListTile(\n        title: const Text(\"Quota\"),\n        subtitle: Selector<WebViewModel, Uri>(\n          selector: (context, webViewModel) => webViewModel.url!,\n          builder: (context, url, child) {\n            return FutureBuilder(\n              future: _webStorageManager\n                  ?.getQuotaForOrigin(origin: url.origin),\n              builder: (context, snapshot) {\n                return Text(snapshot.hasData ? snapshot.data.toString() : \"\");\n              },\n            );\n          },\n        ),\n      ),\n      Selector<WebViewModel, Uri>(\n          selector: (context, webViewModel) => webViewModel.url!,\n          builder: (context, url, child) {\n            return ListTile(\n              title: const Text(\"Usage\"),\n              subtitle: FutureBuilder(\n                future: _webStorageManager\n                    ?.getUsageForOrigin(origin: url.origin),\n                builder: (context, snapshot) {\n                  return Text(snapshot.hasData ? snapshot.data.toString() : \"\");\n                },\n              ),\n              trailing: IconButton(\n                icon: const Icon(Icons.clear),\n                onPressed: () async {\n                  await _webStorageManager\n                      ?.deleteOrigin(origin: url.origin);\n                  setState(() {});\n                },\n              ),\n            );\n          }),\n    ];\n\n    return ExpansionTile(\n      onExpansionChanged: (value) {\n        FocusScope.of(context).unfocus();\n      },\n      title: const Text(\n        \"Web Storage Android\",\n        style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),\n      ),\n      children: children,\n    );\n  }\n\n  Widget _buildIOSWebStorageExpansionTile(BoxConstraints constraints) {\n    return FutureBuilder(\n      future: _webStorageManager\n          ?.fetchDataRecords(dataTypes: WebsiteDataType.ALL),\n      builder: (context, snapshot) {\n        List<WebsiteDataRecord> dataRecords = snapshot.hasData\n            ? (snapshot.data as List<WebsiteDataRecord>)\n            : <WebsiteDataRecord>[];\n\n        var rows = <DataRow>[];\n\n        rows.addAll(dataRecords.map((dataRecord) {\n          var textStyle = const TextStyle(fontSize: 12.0);\n\n          return DataRow(cells: <DataCell>[\n            DataCell(\n              SizedBox(\n                width: constraints.maxWidth / 3,\n                child: Text(dataRecord.displayName ?? \"\",\n                    style: textStyle, softWrap: true),\n              ),\n              onTap: () {\n                Clipboard.setData(ClipboardData(text: dataRecord.displayName ?? ''));\n              },\n            ),\n            DataCell(\n              SizedBox(\n                width: constraints.maxWidth / 3,\n                child: Text(dataRecord.dataTypes?.join(\", \") ?? \"\",\n                    style: textStyle, softWrap: true),\n              ),\n              onTap: () {\n                showDialog(\n                  context: context,\n                  builder: (context) {\n                    return AlertDialog(\n                      content: Text(dataRecord.dataTypes?.join(\",\\n\") ?? \"\",\n                          style: textStyle, softWrap: true),\n                    );\n                  },\n                );\n              },\n            ),\n            DataCell(IconButton(\n              icon: const Icon(Icons.cancel),\n              onPressed: () async {\n                if (dataRecord.dataTypes != null) {\n                  await _webStorageManager?.removeDataFor(\n                      dataTypes: dataRecord.dataTypes!,\n                      dataRecords: [dataRecord]);\n                }\n                setState(() {});\n              },\n            ))\n          ]);\n        }).toList());\n\n        return ExpansionTile(\n          onExpansionChanged: (value) {\n            FocusScope.of(context).unfocus();\n          },\n          title: const Text(\n            \"Web Storage iOS\",\n            style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),\n          ),\n          children: <Widget>[\n            SizedBox(\n              width: constraints.minWidth,\n              child: DataTable(\n                columnSpacing: 0.0,\n                columns: const <DataColumn>[\n                  DataColumn(\n                    label: Text(\n                      \"Display Name\",\n                      style: TextStyle(\n                          fontWeight: FontWeight.bold, fontSize: 16.0),\n                    ),\n                  ),\n                  DataColumn(\n                    label: Text(\n                      \"Data Types\",\n                      style: TextStyle(\n                          fontWeight: FontWeight.bold, fontSize: 16.0),\n                    ),\n                  ),\n                  DataColumn(\n                    label: Text(\n                      \"Delete\",\n                      style: TextStyle(\n                          fontWeight: FontWeight.bold, fontSize: 16.0),\n                    ),\n                  ),\n                ],\n                rows: rows,\n              ),\n            ),\n            SizedBox(\n                width: MediaQuery.of(context).size.width,\n                child: TextButton(\n                  child: const Text(\"Clear all\"),\n                  onPressed: () async {\n                    await _webStorageManager?.removeDataModifiedSince(\n                        dataTypes: WebsiteDataType.ALL,\n                        date: DateTime.fromMillisecondsSinceEpoch(0));\n                    setState(() {});\n                  },\n                ))\n          ],\n        );\n      },\n    );\n  }\n\n  Widget _buildHttpAuthCredentialDatabaseExpansionTile(\n      BoxConstraints constraints) {\n    return FutureBuilder(\n      future: _httpAuthCredentialDatabase?.getAllAuthCredentials(),\n      builder: (context, snapshot) {\n        if (!snapshot.hasData) {\n          return Container();\n        }\n\n        List<URLProtectionSpaceHttpAuthCredentials>\n            protectionSpaceHttpAuthCredentials =\n            snapshot.data ?? <URLProtectionSpaceHttpAuthCredentials>[];\n\n        var textStyle = const TextStyle(fontSize: 16.0);\n\n        var dataTables = <Widget>[];\n\n        for (var protectionSpaceHttpAuthCredential\n            in protectionSpaceHttpAuthCredentials) {\n          var protectionSpace =\n              protectionSpaceHttpAuthCredential.protectionSpace;\n          var rows = <DataRow>[];\n\n          if (protectionSpaceHttpAuthCredential.credentials != null) {\n            rows.addAll(protectionSpaceHttpAuthCredential.credentials!\n                .map((httpAuthCredential) {\n              return DataRow(cells: <DataCell>[\n                DataCell(\n                  SizedBox(\n                    width: constraints.maxWidth / 3,\n                    child: Text(httpAuthCredential.username ?? \"\",\n                        style: textStyle, softWrap: true),\n                  ),\n                  onTap: () {\n                    Clipboard.setData(\n                        ClipboardData(text: httpAuthCredential.username ?? ''));\n                  },\n                ),\n                DataCell(\n                  SizedBox(\n                    width: constraints.maxWidth / 3,\n                    child: Text(httpAuthCredential.password ?? \"\",\n                        style: textStyle, softWrap: true),\n                  ),\n                  onTap: () {\n                    Clipboard.setData(\n                        ClipboardData(text: httpAuthCredential.password ?? ''));\n                  },\n                ),\n                DataCell(IconButton(\n                  icon: const Icon(Icons.cancel),\n                  onPressed: () async {\n                    if (protectionSpace != null) {\n                      await _httpAuthCredentialDatabase\n                          ?.removeHttpAuthCredential(\n                              protectionSpace: protectionSpace,\n                              credential: httpAuthCredential);\n                    }\n                    setState(() {});\n                  },\n                ))\n              ]);\n            }).toList());\n          }\n\n          dataTables.add(Column(\n            children: <Widget>[\n              const Text(\n                \"Protection Space\",\n                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),\n              ),\n              const SizedBox(\n                height: 10.0,\n              ),\n              Text(\n                  \"Protocol: ${protectionSpace?.protocol ?? \"\"}, Host: ${protectionSpace?.host ?? \"\"}, Port: ${protectionSpace?.port != null && protectionSpace!.port! > 0 ? protectionSpace.port.toString() : \"\"}, Realm: ${protectionSpace?.realm ?? \"\"}\"),\n              SizedBox(\n                  width: constraints.minWidth,\n                  child: DataTable(\n                    columnSpacing: 0.0,\n                    columns: const <DataColumn>[\n                      DataColumn(\n                        label: Text(\n                          \"Username\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Password\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                      DataColumn(\n                        label: Text(\n                          \"Delete\",\n                          style: TextStyle(\n                              fontWeight: FontWeight.bold, fontSize: 16.0),\n                        ),\n                      ),\n                    ],\n                    rows: rows,\n                  ))\n            ],\n          ));\n        }\n\n        var children = <Widget>[];\n        children.addAll(dataTables);\n        children.addAll(<Widget>[\n          TextButton(\n            child: const Text(\"Clear all\"),\n            onPressed: () async {\n              await _httpAuthCredentialDatabase?.clearAllAuthCredentials();\n              setState(() {});\n            },\n          ),\n        ]);\n\n        return ExpansionTile(\n          onExpansionChanged: (value) {\n            FocusScope.of(context).unfocus();\n          },\n          title: const Text(\n            \"Http Auth Credentials Database\",\n            style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),\n          ),\n          children: children,\n        );\n      },\n    );\n  }\n\n  DataCell _buildDataCellEditable(\n      {required double width,\n      required int index,\n      required List<bool> trackingEditStatus,\n      String? initialValue,\n      Future<void> Function(String newValue)? onFieldSubmitted}) {\n    return DataCell(\n      SizedBox(\n          width: width,\n          child: Builder(\n            builder: (context) {\n              return !trackingEditStatus[index]\n                  ? Text(\n                      initialValue ?? \"\",\n                      style: const TextStyle(fontSize: 16.0),\n                      softWrap: true,\n                      overflow: TextOverflow.ellipsis,\n                      maxLines: 3,\n                    )\n                  : TextFormField(\n                      initialValue: initialValue,\n                      autofocus: false,\n                      onFieldSubmitted: (newValue) async {\n                        if (newValue != initialValue &&\n                            onFieldSubmitted != null) {\n                          await onFieldSubmitted(newValue);\n                        }\n                        setState(() {\n                          trackingEditStatus[index] = false;\n                        });\n                      },\n                    );\n            },\n          )),\n      onTap: () {\n        setState(() {\n          trackingEditStatus[index] = !trackingEditStatus[index];\n        });\n      },\n    );\n  }\n\n  Widget _buildAddNewWebStorageItem(\n      {required GlobalKey<FormState> formKey,\n      required TextEditingController nameController,\n      required TextEditingController valueController,\n      required String labelName,\n      required String labelValue,\n      Function(String name, String value)? onAdded}) {\n    return Form(\n      key: formKey,\n      child: Row(\n        children: <Widget>[\n          Expanded(\n            child: Container(\n              padding: const EdgeInsets.symmetric(horizontal: 10.0),\n              child: TextFormField(\n                controller: nameController,\n                decoration: InputDecoration(labelText: labelName),\n                validator: (value) {\n                  if (value == null || value.isEmpty) {\n                    return 'Please enter some text';\n                  }\n                  return null;\n                },\n              ),\n            ),\n          ),\n          Expanded(\n            child: Container(\n              padding: const EdgeInsets.only(right: 10.0),\n              child: TextFormField(\n                controller: valueController,\n                decoration: InputDecoration(labelText: labelValue),\n                validator: (value) {\n                  if (value == null || value.isEmpty) {\n                    return 'Please enter some text';\n                  }\n                  return null;\n                },\n              ),\n            ),\n          ),\n          TextButton(\n            child: const Text(\"Add Item\"),\n            onPressed: () {\n              if (formKey.currentState!.validate()) {\n                setState(() {\n                  if (onAdded != null) {\n                    onAdded(nameController.text, valueController.text);\n                    formKey.currentState!.reset();\n                  }\n                });\n              }\n            },\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/settings/android_settings.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_colorpicker/flutter_colorpicker.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport '../../models/window_model.dart';\n\nclass AndroidSettings extends StatefulWidget {\n  const AndroidSettings({super.key});\n\n  @override\n  State<AndroidSettings> createState() => _AndroidSettingsState();\n}\n\nclass _AndroidSettingsState extends State<AndroidSettings> {\n  @override\n  Widget build(BuildContext context) {\n    return ListView(\n      children: _buildAndroidWebViewTabSettings(),\n    );\n  }\n\n  List<Widget> _buildAndroidWebViewTabSettings() {\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    if (windowModel.webViewTabs.isEmpty) {\n      return [];\n    }\n    var currentWebViewModel = Provider.of<WebViewModel>(context, listen: true);\n    var webViewController = currentWebViewModel.webViewController;\n\n    var widgets = <Widget>[\n      const ListTile(\n        title: Text(\"Current WebView Android Settings\"),\n        enabled: false,\n      ),\n      ListTile(\n        title: const Text(\"Text Zoom\"),\n        subtitle: const Text(\"Sets the text zoom of the page in percent.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.textZoom.toString(),\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.textZoom = int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Clear Session Cache\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should have the session cookie cache cleared before the new window is opened.\"),\n        value: currentWebViewModel.settings?.clearSessionCache ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.clearSessionCache = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Built In Zoom Controls\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should use its built-in zoom mechanisms.\"),\n        value: currentWebViewModel.settings?.builtInZoomControls ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.builtInZoomControls = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Display Zoom Controls\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should display on-screen zoom controls when using the built-in zoom mechanisms.\"),\n        value: currentWebViewModel.settings?.displayZoomControls ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.displayZoomControls = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Database storage API\"),\n        subtitle: const Text(\n            \"Sets whether the Database storage API should be enabled.\"),\n        value: currentWebViewModel.settings?.databaseEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.databaseEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"DOM storage API\"),\n        subtitle:\n            const Text(\"Sets whether the DOM storage API should be enabled.\"),\n        value: currentWebViewModel.settings?.domStorageEnabled ?? true,\n        onChanged: (value) {\n          setState(() {\n            currentWebViewModel.settings?.domStorageEnabled = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            windowModel.saveInfo();\n          });\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Use Wide View Port\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should enable support for the \\\"viewport\\\" HTML meta tag or should use a wide viewport.\"),\n        value: currentWebViewModel.settings?.useWideViewPort ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.useWideViewPort = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      const ListTile(\n        title: Text(\"Mixed Content Mode\"),\n        subtitle: Text(\n            \"Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 10.0),\n        alignment: Alignment.center,\n        child: DropdownButton<MixedContentMode>(\n          hint: const Text(\"Mixed Content Mode\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.mixedContentMode = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.mixedContentMode,\n          items: MixedContentMode.values.map((mixedContentMode) {\n            return DropdownMenuItem<MixedContentMode>(\n              value: mixedContentMode,\n              child: Text(\n                mixedContentMode.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Allow Content Access\"),\n        subtitle: const Text(\n            \"Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system.\"),\n        value: currentWebViewModel.settings?.allowContentAccess ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.allowContentAccess = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Allow File Access\"),\n        subtitle: const Text(\n            \"Enables or disables file access within WebView. Note that this enables or disables file system access only.\"),\n        value: currentWebViewModel.settings?.allowFileAccess ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.allowFileAccess = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      const ListTile(\n        title: Text(\"App Cache Path\"),\n        subtitle: Text(\n            \"Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 20.0),\n        alignment: Alignment.center,\n        child: TextFormField(\n          initialValue: currentWebViewModel.settings?.appCachePath,\n          keyboardType: TextInputType.text,\n          onFieldSubmitted: (value) async {\n            currentWebViewModel.settings?.appCachePath = value.trim();\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Block Network Image\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes).\"),\n        value: currentWebViewModel.settings?.blockNetworkImage ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.blockNetworkImage = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Block Network Loads\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should not load resources from the network.\"),\n        value: currentWebViewModel.settings?.blockNetworkLoads ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.blockNetworkLoads = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      const ListTile(\n        title: Text(\"Cache Mode\"),\n        subtitle: Text(\n            \"Overrides the way the cache is used. The way the cache is used is based on the navigation type.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 10.0),\n        alignment: Alignment.center,\n        child: DropdownButton<CacheMode>(\n          hint: const Text(\"Cache Mode\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.cacheMode = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.cacheMode,\n          items: CacheMode.values.map((cacheMode) {\n            return DropdownMenuItem<CacheMode>(\n              value: cacheMode,\n              child: Text(\n                cacheMode.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Cursive Font Family\"),\n        subtitle: const Text(\"Sets the cursive font family name.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.cursiveFontFamily,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.cursiveFontFamily = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Default Fixed Font Size\"),\n        subtitle: const Text(\"Sets the default fixed font size.\"),\n        trailing: SizedBox(\n          width: 50,\n          child: TextFormField(\n            initialValue:\n                currentWebViewModel.settings?.defaultFixedFontSize.toString(),\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.defaultFixedFontSize =\n                  int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Default Font Size\"),\n        subtitle: const Text(\"Sets the default font size.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue:\n                currentWebViewModel.settings?.defaultFontSize.toString(),\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.defaultFontSize = int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Default Text Encoding Name\"),\n        subtitle: const Text(\n            \"Sets the default text encoding name to use when decoding html pages.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.defaultTextEncodingName,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.defaultTextEncodingName = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      const ListTile(\n        title: Text(\"Disabled Action Mode Menu Items\"),\n        subtitle: Text(\n            \"Disables the action mode menu items according to menuItems flag.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 10.0),\n        alignment: Alignment.center,\n        child: DropdownButton<ActionModeMenuItem>(\n          hint: const Text(\"Action Mode Menu Items\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.disabledActionModeMenuItems = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.disabledActionModeMenuItems,\n          items: ActionModeMenuItem.values.map((actionModeMenuItem) {\n            return DropdownMenuItem<ActionModeMenuItem>(\n              value: actionModeMenuItem,\n              child: Text(\n                actionModeMenuItem.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Fantasy Font Family\"),\n        subtitle: const Text(\"Sets the fantasy font family name.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.fantasyFontFamily,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.fantasyFontFamily = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Fixed Font Family\"),\n        subtitle: const Text(\"Sets the fixed font family name.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.fixedFontFamily,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.fixedFontFamily = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Force Dark\"),\n        subtitle: const Text(\"Set the force dark mode for this WebView.\"),\n        trailing: DropdownButton<ForceDark>(\n          hint: const Text(\"Force Dark\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.forceDark = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.forceDark,\n          items: ForceDark.values.map((forceDark) {\n            return DropdownMenuItem<ForceDark>(\n              value: forceDark,\n              child: Text(\n                forceDark.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Geolocation Enabled\"),\n        subtitle: const Text(\"Sets whether Geolocation API is enabled.\"),\n        value: currentWebViewModel.settings?.geolocationEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.geolocationEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Layout Algorithm\"),\n        subtitle: const Text(\n            \"Sets the underlying layout algorithm. This will cause a re-layout of the WebView.\"),\n        trailing: DropdownButton<LayoutAlgorithm>(\n          hint: const Text(\"Layout Algorithm\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.layoutAlgorithm = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.layoutAlgorithm,\n          items: LayoutAlgorithm.values.map((layoutAlgorithm) {\n            return DropdownMenuItem<LayoutAlgorithm>(\n              value: layoutAlgorithm,\n              child: Text(\n                layoutAlgorithm.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Load With Overview Mode\"),\n        subtitle: const Text(\n            \"Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width.\"),\n        value: currentWebViewModel.settings?.loadWithOverviewMode ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.loadWithOverviewMode = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Loads Images Automatically\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should load image resources. Note that this method controls loading of all images, including those embedded using the data URI scheme.\"),\n        value: currentWebViewModel.settings?.loadsImagesAutomatically ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.loadsImagesAutomatically = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Minimum Logical Font Size\"),\n        subtitle: const Text(\"Sets the minimum logical font size.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue:\n                currentWebViewModel.settings?.minimumLogicalFontSize.toString(),\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.minimumLogicalFontSize =\n                  int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Initial Scale\"),\n        subtitle: const Text(\n            \"Sets the initial scale for this WebView. 0 means default.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.initialScale.toString(),\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.initialScale = int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Need Initial Focus\"),\n        subtitle:\n            const Text(\"Tells the WebView whether it needs to set a node.\"),\n        value: currentWebViewModel.settings?.needInitialFocus ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.needInitialFocus = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Offscreen Pre Raster\"),\n        subtitle: const Text(\n            \"Sets whether this WebView should raster tiles when it is offscreen but attached to a window.\"),\n        value: currentWebViewModel.settings?.offscreenPreRaster ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.offscreenPreRaster = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Sans-Serif Font Family\"),\n        subtitle: const Text(\"Sets the sans-serif font family name.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.sansSerifFontFamily,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.sansSerifFontFamily = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Serif Font Family\"),\n        subtitle: const Text(\"Sets the serif font family name.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.serifFontFamily,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.serifFontFamily = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Standard Font Family\"),\n        subtitle: const Text(\"Sets the standard font family name.\"),\n        trailing: SizedBox(\n          width: MediaQuery.of(context).size.width / 3,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.standardFontFamily,\n            keyboardType: TextInputType.text,\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.standardFontFamily = value;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Save Form Data\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should save form data. In Android O, the platform has implemented a fully functional Autofill feature to store form data.\"),\n        value: currentWebViewModel.settings?.saveFormData ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.saveFormData = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Third Party Cookies Enabled\"),\n        subtitle: const Text(\n            \"Sets whether the Webview should enable third party cookies.\"),\n        value: currentWebViewModel.settings?.thirdPartyCookiesEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.thirdPartyCookiesEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Hardware Acceleration\"),\n        subtitle: const Text(\n            \"Sets whether the Webview should enable Hardware Acceleration.\"),\n        value: currentWebViewModel.settings?.hardwareAcceleration ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.hardwareAcceleration = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Support Multiple Windows\"),\n        subtitle: const Text(\n            \"Sets whether the WebView whether supports multiple windows.\"),\n        value: currentWebViewModel.settings?.supportMultipleWindows ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.supportMultipleWindows = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      const ListTile(\n        title: Text(\"Over Scroll Mode\"),\n        subtitle: Text(\"Sets the WebView's over-scroll mode.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 10.0),\n        alignment: Alignment.center,\n        child: DropdownButton<OverScrollMode>(\n          hint: const Text(\"Over Scroll Mode\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.overScrollMode = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.overScrollMode,\n          items: OverScrollMode.values.map((overScrollMode) {\n            return DropdownMenuItem<OverScrollMode>(\n              value: overScrollMode,\n              child: Text(\n                overScrollMode.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Network Available\"),\n        subtitle: const Text(\"Informs WebView of the network state.\"),\n        value: currentWebViewModel.settings?.networkAvailable ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.networkAvailable = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      const ListTile(\n        title: Text(\"Scroll Bar Style\"),\n        subtitle: Text(\"Specify the style of the scrollbars.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 10.0),\n        alignment: Alignment.center,\n        child: DropdownButton<ScrollBarStyle>(\n          hint: const Text(\"Scroll Bar Style\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.scrollBarStyle = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.scrollBarStyle,\n          items: ScrollBarStyle.values.map((scrollBarStyle) {\n            return DropdownMenuItem<ScrollBarStyle>(\n              value: scrollBarStyle,\n              child: Text(\n                scrollBarStyle.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      const ListTile(\n        title: Text(\"Vertical Scrollbar Position\"),\n        subtitle: Text(\"Set the position of the vertical scroll bar.\"),\n      ),\n      Container(\n        padding: const EdgeInsets.only(\n            left: 20.0, top: 0.0, right: 20.0, bottom: 10.0),\n        alignment: Alignment.center,\n        child: DropdownButton<VerticalScrollbarPosition>(\n          hint: const Text(\"Vertical Scrollbar Position\"),\n          onChanged: (value) async {\n            currentWebViewModel.settings?.verticalScrollbarPosition = value;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            currentWebViewModel.settings =\n                await webViewController?.getSettings();\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.verticalScrollbarPosition,\n          items:\n              VerticalScrollbarPosition.values.map((verticalScrollbarPosition) {\n            return DropdownMenuItem<VerticalScrollbarPosition>(\n              value: verticalScrollbarPosition,\n              child: Text(\n                verticalScrollbarPosition.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Scroll Bar Default Delay Before Fade\"),\n        subtitle: const Text(\n            \"Defines the delay in milliseconds that a scrollbar waits before fade out.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue: currentWebViewModel\n                    .settings?.scrollBarDefaultDelayBeforeFade\n                    ?.toString() ??\n                \"0\",\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.scrollBarDefaultDelayBeforeFade =\n                  int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Scrollbar Fading Enabled\"),\n        subtitle: const Text(\n            \"Define whether scrollbars will fade when the view is not scrolling.\"),\n        value: currentWebViewModel.settings?.scrollbarFadingEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.scrollbarFadingEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Scroll Bar Fade Duration\"),\n        subtitle:\n            const Text(\"Define the scrollbar fade duration in milliseconds.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.scrollBarFadeDuration\n                    ?.toString() ??\n                \"0\",\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.scrollBarFadeDuration =\n                  int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Vertical Scrollbar Thumb Color\"),\n        subtitle: const Text(\"Sets the vertical scrollbar thumb color.\"),\n        trailing: SizedBox(\n            width: 140.0,\n            child: ElevatedButton(\n              child: Text(\n                currentWebViewModel.settings?.verticalScrollbarThumbColor\n                        ?.toString() ??\n                    'Pick a color!',\n                style: const TextStyle(fontSize: 12.5),\n              ),\n              onPressed: () {\n                showDialog(\n                  context: context,\n                  builder: (context) {\n                    return AlertDialog(\n                      content: SingleChildScrollView(\n                        child: ColorPicker(\n                          pickerColor: const Color(0xffffffff),\n                          onColorChanged: (value) async {\n                            currentWebViewModel\n                                .settings?.verticalScrollbarThumbColor = value;\n                            webViewController?.setSettings(\n                                settings: currentWebViewModel.settings ??\n                                    InAppWebViewSettings());\n                            currentWebViewModel.settings =\n                                await webViewController?.getSettings();\n                            windowModel.saveInfo();\n                            setState(() {});\n                          },\n                          labelTypes: const [\n                            ColorLabelType.rgb,\n                            ColorLabelType.hsv,\n                            ColorLabelType.hsl\n                          ],\n                          pickerAreaHeightPercent: 0.8,\n                        ),\n                      ),\n                    );\n                  },\n                );\n              },\n            )),\n      ),\n      ListTile(\n        title: const Text(\"Vertical Scrollbar Track Color\"),\n        subtitle: const Text(\"Sets the vertical scrollbar track color.\"),\n        trailing: SizedBox(\n            width: 140.0,\n            child: ElevatedButton(\n              child: Text(\n                currentWebViewModel.settings?.verticalScrollbarTrackColor\n                        ?.toString() ??\n                    'Pick a color!',\n                style: const TextStyle(fontSize: 12.5),\n              ),\n              onPressed: () {\n                showDialog(\n                  context: context,\n                  builder: (context) {\n                    return AlertDialog(\n                      content: SingleChildScrollView(\n                        child: ColorPicker(\n                          pickerColor: const Color(0xffffffff),\n                          onColorChanged: (value) async {\n                            currentWebViewModel\n                                .settings?.verticalScrollbarTrackColor = value;\n                            webViewController?.setSettings(\n                                settings: currentWebViewModel.settings ??\n                                    InAppWebViewSettings());\n                            currentWebViewModel.settings =\n                                await webViewController?.getSettings();\n                            windowModel.saveInfo();\n                            setState(() {});\n                          },\n                          labelTypes: const [\n                            ColorLabelType.rgb,\n                            ColorLabelType.hsv,\n                            ColorLabelType.hsl\n                          ],\n                          pickerAreaHeightPercent: 0.8,\n                        ),\n                      ),\n                    );\n                  },\n                );\n              },\n            )),\n      ),\n      ListTile(\n        title: const Text(\"Horizontal Scrollbar Thumb Color\"),\n        subtitle: const Text(\"Sets the horizontal scrollbar thumb color.\"),\n        trailing: SizedBox(\n            width: 140.0,\n            child: ElevatedButton(\n              child: Text(\n                currentWebViewModel.settings?.horizontalScrollbarThumbColor\n                        ?.toString() ??\n                    'Pick a color!',\n                style: const TextStyle(fontSize: 12.5),\n              ),\n              onPressed: () {\n                showDialog(\n                  context: context,\n                  builder: (context) {\n                    return AlertDialog(\n                      content: SingleChildScrollView(\n                        child: ColorPicker(\n                          pickerColor: const Color(0xffffffff),\n                          onColorChanged: (value) async {\n                            currentWebViewModel.settings\n                                ?.horizontalScrollbarThumbColor = value;\n                            webViewController?.setSettings(\n                                settings: currentWebViewModel.settings ??\n                                    InAppWebViewSettings());\n                            currentWebViewModel.settings =\n                                await webViewController?.getSettings();\n                            windowModel.saveInfo();\n                            setState(() {});\n                          },\n                          labelTypes: const [\n                            ColorLabelType.rgb,\n                            ColorLabelType.hsv,\n                            ColorLabelType.hsl\n                          ],\n                          pickerAreaHeightPercent: 0.8,\n                        ),\n                      ),\n                    );\n                  },\n                );\n              },\n            )),\n      ),\n      ListTile(\n        title: const Text(\"Horizontal Scrollbar Track Color\"),\n        subtitle: const Text(\"Sets the horizontal scrollbar track color.\"),\n        trailing: SizedBox(\n            width: 140.0,\n            child: ElevatedButton(\n              child: Text(\n                currentWebViewModel.settings?.horizontalScrollbarTrackColor\n                        ?.toString() ??\n                    'Pick a color!',\n                style: const TextStyle(fontSize: 12.5),\n              ),\n              onPressed: () {\n                showDialog(\n                  context: context,\n                  builder: (context) {\n                    return AlertDialog(\n                      content: SingleChildScrollView(\n                        child: ColorPicker(\n                          pickerColor: const Color(0xffffffff),\n                          onColorChanged: (value) async {\n                            currentWebViewModel.settings\n                                ?.horizontalScrollbarTrackColor = value;\n                            webViewController?.setSettings(\n                                settings: currentWebViewModel.settings ??\n                                    InAppWebViewSettings());\n                            currentWebViewModel.settings =\n                                await webViewController?.getSettings();\n                            windowModel.saveInfo();\n                            setState(() {});\n                          },\n                          labelTypes: const [\n                            ColorLabelType.rgb,\n                            ColorLabelType.hsv,\n                            ColorLabelType.hsl\n                          ],\n                          pickerAreaHeightPercent: 0.8,\n                        ),\n                      ),\n                    );\n                  },\n                );\n              },\n            )),\n      ),\n    ];\n\n    return widgets;\n  }\n}\n"
  },
  {
    "path": "lib/pages/settings/cross_platform_settings.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/search_engine_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:package_info_plus/package_info_plus.dart';\nimport 'package:provider/provider.dart';\n\nimport '../../models/window_model.dart';\nimport '../../project_info_popup.dart';\n\nclass CrossPlatformSettings extends StatefulWidget {\n  const CrossPlatformSettings({super.key});\n\n  @override\n  State<CrossPlatformSettings> createState() => _CrossPlatformSettingsState();\n}\n\nclass _CrossPlatformSettingsState extends State<CrossPlatformSettings> {\n  final TextEditingController _customHomePageController =\n      TextEditingController();\n  final TextEditingController _customUserAgentController =\n      TextEditingController();\n\n  @override\n  void dispose() {\n    _customHomePageController.dispose();\n    _customUserAgentController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    final children = _buildBaseSettings();\n    if (windowModel.webViewTabs.isNotEmpty) {\n      children.addAll(_buildWebViewTabSettings());\n    }\n\n    return ListView(\n      children: children,\n    );\n  }\n\n  List<Widget> _buildBaseSettings() {\n    final browserModel = Provider.of<BrowserModel>(context, listen: true);\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    final settings = browserModel.getSettings();\n\n    var widgets = <Widget>[\n      const ListTile(\n        title: Text(\"General Settings\"),\n        enabled: false,\n      ),\n      ListTile(\n        title: const Text(\"Search Engine\"),\n        subtitle: Text(settings.searchEngine.name),\n        trailing: DropdownButton<SearchEngineModel>(\n          hint: const Text(\"Search Engine\"),\n          onChanged: (value) {\n            setState(() {\n              if (value != null) {\n                settings.searchEngine = value;\n              }\n              browserModel.updateSettings(settings);\n            });\n          },\n          value: settings.searchEngine,\n          items: SearchEngines.map((searchEngine) {\n            return DropdownMenuItem(\n              value: searchEngine,\n              child: Text(searchEngine.name),\n            );\n          }).toList(),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Home page\"),\n        subtitle: Text(settings.homePageEnabled\n            ? (settings.customUrlHomePage.isEmpty\n                ? \"ON\"\n                : settings.customUrlHomePage)\n            : \"OFF\"),\n        onTap: () {\n          _customHomePageController.text = settings.customUrlHomePage;\n\n          showDialog(\n            context: context,\n            builder: (context) {\n              return AlertDialog(\n                contentPadding: const EdgeInsets.all(0.0),\n                content: Column(\n                  mainAxisSize: MainAxisSize.min,\n                  children: <Widget>[\n                    StatefulBuilder(\n                      builder: (context, setState) {\n                        return SwitchListTile(\n                          title: Text(settings.homePageEnabled ? \"ON\" : \"OFF\"),\n                          value: settings.homePageEnabled,\n                          onChanged: (value) {\n                            setState(() {\n                              settings.homePageEnabled = value;\n                              browserModel.updateSettings(settings);\n                            });\n                          },\n                        );\n                      },\n                    ),\n                    StatefulBuilder(builder: (context, setState) {\n                      return ListTile(\n                        enabled: settings.homePageEnabled,\n                        title: Row(\n                          mainAxisAlignment: MainAxisAlignment.end,\n                          children: <Widget>[\n                            Expanded(\n                              child: TextField(\n                                onSubmitted: (value) {\n                                  setState(() {\n                                    settings.customUrlHomePage = value;\n                                    browserModel.updateSettings(settings);\n                                    Navigator.pop(context);\n                                  });\n                                },\n                                keyboardType: TextInputType.url,\n                                decoration: const InputDecoration(\n                                    hintText: 'Custom URL Home Page'),\n                                controller: _customHomePageController,\n                              ),\n                            )\n                          ],\n                        ),\n                      );\n                    })\n                  ],\n                ),\n              );\n            },\n          );\n        },\n      ),\n      FutureBuilder(\n        future: InAppWebViewController.getDefaultUserAgent(),\n        builder: (context, snapshot) {\n          var deafultUserAgent = \"\";\n          if (snapshot.hasData) {\n            deafultUserAgent = snapshot.data as String;\n          }\n\n          return ListTile(\n            title: const Text(\"Default User Agent\"),\n            subtitle: Text(deafultUserAgent),\n            onLongPress: () {\n              Clipboard.setData(ClipboardData(text: deafultUserAgent));\n            },\n          );\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Debugging Enabled\"),\n        subtitle: const Text(\n            \"Enables debugging of web contents loaded into any WebViews of this application. On iOS < 16.4, the debugging mode is always enabled.\"),\n        value: settings.debuggingEnabled,\n        onChanged: (value) {\n          setState(() {\n            settings.debuggingEnabled = value;\n            browserModel.updateSettings(settings);\n            if (windowModel.webViewTabs.isNotEmpty) {\n              var webViewModel = windowModel.getCurrentTab()?.webViewModel;\n              if (Util.isAndroid()) {\n                InAppWebViewController.setWebContentsDebuggingEnabled(\n                    settings.debuggingEnabled);\n              }\n              webViewModel?.settings?.isInspectable = settings.debuggingEnabled;\n              webViewModel?.webViewController?.setSettings(\n                  settings: webViewModel.settings ?? InAppWebViewSettings());\n              windowModel.saveInfo();\n            }\n          });\n        },\n      ),\n      FutureBuilder(\n        future: PackageInfo.fromPlatform(),\n        builder: (context, snapshot) {\n          String packageDescription = \"\";\n          if (snapshot.hasData) {\n            PackageInfo packageInfo = snapshot.data as PackageInfo;\n            packageDescription =\n                \"Package Name: ${packageInfo.packageName}\\nVersion: ${packageInfo.version}\\nBuild Number: ${packageInfo.buildNumber}\";\n          }\n          return ListTile(\n            title: const Text(\"Flutter Browser Package Info\"),\n            subtitle: Text(packageDescription),\n            onLongPress: () {\n              Clipboard.setData(ClipboardData(text: packageDescription));\n            },\n          );\n        },\n      ),\n      ListTile(\n        leading: Container(\n          height: 35,\n          width: 35,\n          margin: const EdgeInsets.only(top: 6.0, left: 6.0),\n          child: const CircleAvatar(\n              backgroundImage: AssetImage(\"assets/icon/icon.png\")),\n        ),\n        title: const Text(\"Flutter InAppWebView Project\"),\n        subtitle: const Text(\n            \"https://github.com/pichillilorenzo/flutter_inappwebview\"),\n        trailing: const Icon(Icons.arrow_forward),\n        onLongPress: () {\n          showGeneralDialog(\n            context: context,\n            barrierDismissible: false,\n            pageBuilder: (context, animation, secondaryAnimation) {\n              return const ProjectInfoPopup();\n            },\n            transitionDuration: const Duration(milliseconds: 300),\n          );\n        },\n        onTap: () {\n          showGeneralDialog(\n            context: context,\n            barrierDismissible: false,\n            pageBuilder: (context, animation, secondaryAnimation) {\n              return const ProjectInfoPopup();\n            },\n            transitionDuration: const Duration(milliseconds: 300),\n          );\n        },\n      )\n    ];\n\n    if (Util.isAndroid()) {\n      widgets.addAll(<Widget>[\n        FutureBuilder(\n          future: InAppWebViewController.getCurrentWebViewPackage(),\n          builder: (context, snapshot) {\n            String packageDescription = \"\";\n            if (snapshot.hasData) {\n              WebViewPackageInfo packageInfo =\n                  snapshot.data as WebViewPackageInfo;\n              packageDescription =\n                  \"${packageInfo.packageName ?? \"\"} - ${packageInfo.versionName ?? \"\"}\";\n            }\n            return ListTile(\n              title: const Text(\"WebView Package Info\"),\n              subtitle: Text(packageDescription),\n              onLongPress: () {\n                Clipboard.setData(ClipboardData(text: packageDescription));\n              },\n            );\n          },\n        )\n      ]);\n    }\n\n    return widgets;\n  }\n\n  List<Widget> _buildWebViewTabSettings() {\n    var windowModel = Provider.of<WindowModel>(context, listen: true);\n    var currentWebViewModel = Provider.of<WebViewModel>(context, listen: true);\n    var webViewController = currentWebViewModel.webViewController;\n\n    var widgets = <Widget>[\n      const ListTile(\n        title: Text(\"Current WebView Settings\"),\n        enabled: false,\n      ),\n      SwitchListTile(\n        title: const Text(\"JavaScript Enabled\"),\n        subtitle:\n            const Text(\"Sets whether the WebView should enable JavaScript.\"),\n        value: currentWebViewModel.settings?.javaScriptEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.javaScriptEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Cache Enabled\"),\n        subtitle:\n            const Text(\"Sets whether the WebView should use browser caching.\"),\n        value: currentWebViewModel.settings?.cacheEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.cacheEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      StatefulBuilder(\n        builder: (context, setState) {\n          return ListTile(\n            title: const Text(\"Custom User Agent\"),\n            subtitle: Text(\n                currentWebViewModel.settings?.userAgent?.isNotEmpty ?? false\n                    ? currentWebViewModel.settings!.userAgent!\n                    : \"Set a custom user agent ...\"),\n            onTap: () {\n              _customUserAgentController.text =\n                  currentWebViewModel.settings?.userAgent ?? \"\";\n\n              showDialog(\n                context: context,\n                builder: (context) {\n                  return AlertDialog(\n                    contentPadding: const EdgeInsets.all(0.0),\n                    content: Column(\n                      mainAxisSize: MainAxisSize.min,\n                      children: <Widget>[\n                        ListTile(\n                          title: Row(\n                            mainAxisAlignment: MainAxisAlignment.end,\n                            children: <Widget>[\n                              Expanded(\n                                child: TextField(\n                                  onSubmitted: (value) async {\n                                    currentWebViewModel.settings?.userAgent =\n                                        value;\n                                    webViewController?.setSettings(\n                                        settings:\n                                            currentWebViewModel.settings ??\n                                                InAppWebViewSettings());\n                                    currentWebViewModel.settings =\n                                        await webViewController?.getSettings();\n                                    windowModel.saveInfo();\n                                    setState(() {\n                                      Navigator.pop(context);\n                                    });\n                                  },\n                                  decoration: const InputDecoration(\n                                      hintText: 'Custom User Agent'),\n                                  controller: _customUserAgentController,\n                                  keyboardType: TextInputType.multiline,\n                                  textInputAction: TextInputAction.go,\n                                  maxLines: null,\n                                ),\n                              )\n                            ],\n                          ),\n                        )\n                      ],\n                    ),\n                  );\n                },\n              );\n            },\n          );\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Support Zoom\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should not support zooming using its on-screen zoom controls and gestures.\"),\n        value: currentWebViewModel.settings?.supportZoom ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.supportZoom = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Media Playback Requires User Gesture\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should prevent HTML5 audio or video from autoplaying.\"),\n        value: currentWebViewModel.settings?.mediaPlaybackRequiresUserGesture ??\n            true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.mediaPlaybackRequiresUserGesture =\n              value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Vertical ScrollBar Enabled\"),\n        subtitle: const Text(\n            \"Sets whether the vertical scrollbar should be drawn or not.\"),\n        value: currentWebViewModel.settings?.verticalScrollBarEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.verticalScrollBarEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Horizontal ScrollBar Enabled\"),\n        subtitle: const Text(\n            \"Sets whether the horizontal scrollbar should be drawn or not.\"),\n        value: currentWebViewModel.settings?.horizontalScrollBarEnabled ?? true,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.horizontalScrollBarEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Disable Vertical Scroll\"),\n        subtitle: const Text(\n            \"Sets whether vertical scroll should be enabled or not.\"),\n        value: currentWebViewModel.settings?.disableVerticalScroll ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.disableVerticalScroll = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Disable Horizontal Scroll\"),\n        subtitle: const Text(\n            \"Sets whether horizontal scroll should be enabled or not.\"),\n        value: currentWebViewModel.settings?.disableHorizontalScroll ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.disableHorizontalScroll = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Disable Context Menu\"),\n        subtitle:\n            const Text(\"Sets whether context menu should be enabled or not.\"),\n        value: currentWebViewModel.settings?.disableContextMenu ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.disableContextMenu = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Minimum Font Size\"),\n        subtitle: const Text(\"Sets the minimum font size.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue:\n                currentWebViewModel.settings?.minimumFontSize.toString(),\n            keyboardType: const TextInputType.numberWithOptions(),\n            onFieldSubmitted: (value) async {\n              currentWebViewModel.settings?.minimumFontSize = int.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              currentWebViewModel.settings =\n                  await webViewController?.getSettings();\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Allow File Access From File URLs\"),\n        subtitle: const Text(\n            \"Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs.\"),\n        value:\n            currentWebViewModel.settings?.allowFileAccessFromFileURLs ?? false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.allowFileAccessFromFileURLs = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Allow Universal Access From File URLs\"),\n        subtitle: const Text(\n            \"Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin.\"),\n        value: currentWebViewModel.settings?.allowUniversalAccessFromFileURLs ??\n            false,\n        onChanged: (value) async {\n          currentWebViewModel.settings?.allowUniversalAccessFromFileURLs =\n              value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          currentWebViewModel.settings = await webViewController?.getSettings();\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n    ];\n\n    return widgets;\n  }\n}\n"
  },
  {
    "path": "lib/pages/settings/ios_settings.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/multiselect_dialog.dart';\nimport 'package:flutter_colorpicker/flutter_colorpicker.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport '../../models/window_model.dart';\n\nclass IOSSettings extends StatefulWidget {\n  const IOSSettings({super.key});\n\n  @override\n  State<IOSSettings> createState() => _IOSSettingsState();\n}\n\nclass _IOSSettingsState extends State<IOSSettings> {\n  @override\n  Widget build(BuildContext context) {\n    return ListView(\n      children: _buildIOSWebViewSettings(),\n    );\n  }\n\n  List<Widget> _buildIOSWebViewSettings() {\n    final windowModel = Provider.of<WindowModel>(context, listen: true);\n    if (windowModel.webViewTabs.isEmpty) {\n      return [];\n    }\n    var currentWebViewModel = Provider.of<WebViewModel>(context, listen: true);\n    var webViewController = currentWebViewModel.webViewController;\n\n    var widgets = <Widget>[\n      const ListTile(\n        title: Text(\"Current WebView iOS Settings\"),\n        enabled: false,\n      ),\n      SwitchListTile(\n        title: const Text(\"Disallow Over Scroll\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should bounce when the scrolling has reached an edge of the content\"),\n        value: currentWebViewModel.settings?.disallowOverScroll ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.disallowOverScroll = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Enable Viewport Scale\"),\n        subtitle: const Text(\n            \"Enable to allow a viewport meta tag to either disable or restrict the range of user scaling.\"),\n        value: currentWebViewModel.settings?.enableViewportScale ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.enableViewportScale = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Suppresses Incremental Rendering\"),\n        subtitle: const Text(\n            \"Sets wheter the WebView should suppresses content rendering until it is fully loaded into memory.\"),\n        value: currentWebViewModel.settings?.suppressesIncrementalRendering ??\n            false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.suppressesIncrementalRendering = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Allows Air Play For Media Playback\"),\n        subtitle: const Text(\"Enable AirPlay.\"),\n        value:\n            currentWebViewModel.settings?.allowsAirPlayForMediaPlayback ?? true,\n        onChanged: (value) {\n          currentWebViewModel.settings?.allowsAirPlayForMediaPlayback = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Allows Back Forward Navigation Gestures\"),\n        subtitle: const Text(\n            \"Enable to allow the horizontal swipe gestures trigger back-forward list navigations.\"),\n        value:\n            currentWebViewModel.settings?.allowsBackForwardNavigationGestures ??\n                true,\n        onChanged: (value) {\n          currentWebViewModel.settings?.allowsBackForwardNavigationGestures =\n              value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Ignores Viewport Scale Limits\"),\n        subtitle: const Text(\n            \"Sets whether the WebView should always allow scaling of the webpage, regardless of the author's intent.\"),\n        value:\n            currentWebViewModel.settings?.ignoresViewportScaleLimits ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.ignoresViewportScaleLimits = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Allows Inline Media Playback\"),\n        subtitle: const Text(\n            \"Enable to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls.\"),\n        value: currentWebViewModel.settings?.allowsInlineMediaPlayback ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.allowsInlineMediaPlayback = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Allows Picture In Picture Media Playback\"),\n        subtitle:\n            const Text(\"Enable to allow HTML5 videos play picture-in-picture.\"),\n        value:\n            currentWebViewModel.settings?.allowsPictureInPictureMediaPlayback ??\n                true,\n        onChanged: (value) {\n          currentWebViewModel.settings?.allowsPictureInPictureMediaPlayback =\n              value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Selection Granularity\"),\n        subtitle: const Text(\n            \"Sets the level of granularity with which the user can interactively select content in the web view.\"),\n        trailing: DropdownButton<SelectionGranularity>(\n          hint: const Text(\"Granularity\"),\n          onChanged: (value) {\n            currentWebViewModel.settings?.selectionGranularity = value!;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.selectionGranularity,\n          items: SelectionGranularity.values.map((selectionGranularity) {\n            return DropdownMenuItem<SelectionGranularity>(\n              value: selectionGranularity,\n              child: Text(\n                selectionGranularity.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Data Detector Types\"),\n        subtitle: const Text(\n            \"Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value.\"),\n        trailing: Container(\n          constraints:\n              BoxConstraints(maxWidth: MediaQuery.of(context).size.width / 2),\n          child: Text(currentWebViewModel.settings?.dataDetectorTypes\n                  ?.map((e) => e.toString())\n                  .join(\", \") ??\n              \"\"),\n        ),\n        onTap: () async {\n          final dataDetectoryTypesSelected =\n              await showDialog<Set<DataDetectorTypes>>(\n            context: context,\n            builder: (BuildContext context) {\n              return MultiSelectDialog(\n                title: const Text(\"Data Detector Types\"),\n                items: DataDetectorTypes.values.map((dataDetectorType) {\n                  return MultiSelectDialogItem<DataDetectorTypes>(\n                      value: dataDetectorType,\n                      label: dataDetectorType.toString());\n                }).toList(),\n                initialSelectedValues:\n                    currentWebViewModel.settings?.dataDetectorTypes?.toSet(),\n              );\n            },\n          );\n          if (dataDetectoryTypesSelected != null) {\n            currentWebViewModel.settings?.dataDetectorTypes =\n                dataDetectoryTypesSelected.toList();\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            windowModel.saveInfo();\n            setState(() {});\n          }\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Shared Cookies Enabled\"),\n        subtitle: const Text(\n            \"Sets if shared cookies from \\\"HTTPCookieStorage.shared\\\" should used for every load request in the WebView.\"),\n        value: currentWebViewModel.settings?.sharedCookiesEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.sharedCookiesEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Automatically Adjusts Scroll Indicator Insets\"),\n        subtitle: const Text(\n            \"Configures whether the scroll indicator insets are automatically adjusted by the system.\"),\n        value: currentWebViewModel\n                .settings?.automaticallyAdjustsScrollIndicatorInsets ??\n            false,\n        onChanged: (value) {\n          currentWebViewModel\n              .settings?.automaticallyAdjustsScrollIndicatorInsets = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Accessibility Ignores Invert Colors\"),\n        subtitle: const Text(\n            \"Sets whether the WebView ignores an accessibility request to invert its colors.\"),\n        value: currentWebViewModel.settings?.accessibilityIgnoresInvertColors ??\n            false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.accessibilityIgnoresInvertColors =\n              value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Deceleration Rate\"),\n        subtitle: const Text(\n            \"Determines the rate of deceleration after the user lifts their finger.\"),\n        trailing: DropdownButton<ScrollViewDecelerationRate>(\n          hint: const Text(\"Deceleration\"),\n          onChanged: (value) {\n            currentWebViewModel.settings?.decelerationRate = value!;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.decelerationRate,\n          items: ScrollViewDecelerationRate.values.map((decelerationRate) {\n            return DropdownMenuItem<ScrollViewDecelerationRate>(\n              value: decelerationRate,\n              child: Text(\n                decelerationRate.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Always Bounce Vertical\"),\n        subtitle: const Text(\n            \"Determines whether bouncing always occurs when vertical scrolling reaches the end of the content.\"),\n        value: currentWebViewModel.settings?.alwaysBounceVertical ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.alwaysBounceVertical = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Always Bounce Horizontal\"),\n        subtitle: const Text(\n            \"Determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view.\"),\n        value: currentWebViewModel.settings?.alwaysBounceHorizontal ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.alwaysBounceHorizontal = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Scrolls To Top\"),\n        subtitle:\n            const Text(\"Sets whether the scroll-to-top gesture is enabled.\"),\n        value: currentWebViewModel.settings?.scrollsToTop ?? true,\n        onChanged: (value) {\n          currentWebViewModel.settings?.scrollsToTop = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Is Paging Enabled\"),\n        subtitle: const Text(\n            \"Determines whether paging is enabled for the scroll view.\"),\n        value: currentWebViewModel.settings?.isPagingEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.isPagingEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Maximum Zoom Scale\"),\n        subtitle: const Text(\n            \"A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue:\n                currentWebViewModel.settings?.maximumZoomScale.toString(),\n            keyboardType: const TextInputType.numberWithOptions(decimal: true),\n            onFieldSubmitted: (value) {\n              currentWebViewModel.settings?.maximumZoomScale =\n                  double.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Minimum Zoom Scale\"),\n        subtitle: const Text(\n            \"A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue:\n                currentWebViewModel.settings?.minimumZoomScale.toString(),\n            keyboardType: const TextInputType.numberWithOptions(decimal: true),\n            onFieldSubmitted: (value) {\n              currentWebViewModel.settings?.minimumZoomScale =\n                  double.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Content Inset Adjustment Behavior\"),\n        subtitle: const Text(\n            \"Configures how safe area insets are added to the adjusted content inset.\"),\n        trailing: DropdownButton<ScrollViewContentInsetAdjustmentBehavior>(\n          onChanged: (value) {\n            currentWebViewModel.settings?.contentInsetAdjustmentBehavior =\n                value!;\n            webViewController?.setSettings(\n                settings:\n                    currentWebViewModel.settings ?? InAppWebViewSettings());\n            windowModel.saveInfo();\n            setState(() {});\n          },\n          value: currentWebViewModel.settings?.contentInsetAdjustmentBehavior,\n          items: ScrollViewContentInsetAdjustmentBehavior.values\n              .map((decelerationRate) {\n            return DropdownMenuItem<ScrollViewContentInsetAdjustmentBehavior>(\n              value: decelerationRate,\n              child: Text(\n                decelerationRate.toString(),\n                style: const TextStyle(fontSize: 12.5),\n              ),\n            );\n          }).toList(),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Is Directional Lock Enabled\"),\n        subtitle: const Text(\n            \"A Boolean value that determines whether scrolling is disabled in a particular direction.\"),\n        value: currentWebViewModel.settings?.isDirectionalLockEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.isDirectionalLockEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Media Type\"),\n        subtitle:\n            const Text(\"The media type for the contents of the web view.\"),\n        trailing: SizedBox(\n          width: 100.0,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.mediaType?.toString(),\n            onFieldSubmitted: (value) {\n              currentWebViewModel.settings?.mediaType =\n                  value.isNotEmpty ? value : null;\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      ListTile(\n        title: const Text(\"Page Zoom\"),\n        subtitle: const Text(\n            \"The scale factor by which the web view scales content relative to its bounds.\"),\n        trailing: SizedBox(\n          width: 50.0,\n          child: TextFormField(\n            initialValue: currentWebViewModel.settings?.pageZoom.toString(),\n            keyboardType: const TextInputType.numberWithOptions(decimal: true),\n            onFieldSubmitted: (value) {\n              currentWebViewModel.settings?.pageZoom = double.parse(value);\n              webViewController?.setSettings(\n                  settings:\n                      currentWebViewModel.settings ?? InAppWebViewSettings());\n              windowModel.saveInfo();\n              setState(() {});\n            },\n          ),\n        ),\n      ),\n      SwitchListTile(\n        title: const Text(\"Apple Pay API Enabled\"),\n        subtitle: const Text(\n            \"Indicates if Apple Pay API should be enabled on the next page load (JavaScript won't work).\"),\n        value: currentWebViewModel.settings?.applePayAPIEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.applePayAPIEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      ListTile(\n        title: const Text(\"Under Page Background Color\"),\n        subtitle: const Text(\"Sets the color the web view displays behind the active page, visible when the user scrolls beyond the bounds of the page.\"),\n        trailing: SizedBox(\n            width: 140.0,\n            child: ElevatedButton(\n              child: Text(\n                currentWebViewModel.settings?.underPageBackgroundColor\n                    ?.toString() ??\n                    'Pick a color!',\n                style: const TextStyle(fontSize: 12.5),\n              ),\n              onPressed: () {\n                showDialog(\n                  context: context,\n                  builder: (context) {\n                    return AlertDialog(\n                      content: SingleChildScrollView(\n                        child: ColorPicker(\n                          pickerColor: const Color(0xffffffff),\n                          onColorChanged: (value) async {\n                            currentWebViewModel.settings\n                                ?.underPageBackgroundColor = value;\n                            webViewController?.setSettings(\n                                settings: currentWebViewModel.settings ??\n                                    InAppWebViewSettings());\n                            currentWebViewModel.settings =\n                            await webViewController?.getSettings();\n                            windowModel.saveInfo();\n                            setState(() {});\n                          },\n                          labelTypes: const [\n                            ColorLabelType.rgb,\n                            ColorLabelType.hsv,\n                            ColorLabelType.hsl\n                          ],\n                          pickerAreaHeightPercent: 0.8,\n                        ),\n                      ),\n                    );\n                  },\n                );\n              },\n            )),\n      ),\n      SwitchListTile(\n        title: const Text(\"Text Interaction Enabled\"),\n        subtitle: const Text(\n            \"Indicates whether text interaction is enabled or not.\"),\n        value: currentWebViewModel.settings?.isTextInteractionEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.isTextInteractionEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Site Specific Quirks Mode Enabled\"),\n        subtitle: const Text(\n            \"Indicates whether WebKit will apply built-in workarounds (quirks) to improve compatibility with certain known websites. You can disable site-specific quirks to help test your website without these workarounds.\"),\n        value: currentWebViewModel.settings?.isSiteSpecificQuirksModeEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.isSiteSpecificQuirksModeEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Upgrade Known Hosts To HTTPS\"),\n        subtitle: const Text(\n            \"Indicates whether HTTP requests to servers known to support HTTPS should be automatically upgraded to HTTPS requests.\"),\n        value: currentWebViewModel.settings?.upgradeKnownHostsToHTTPS ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.upgradeKnownHostsToHTTPS = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Element Fullscreen Enabled\"),\n        subtitle: const Text(\n            \"Indicates whether fullscreen API is enabled or not.\"),\n        value: currentWebViewModel.settings?.isElementFullscreenEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.isElementFullscreenEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n      SwitchListTile(\n        title: const Text(\"Find Interaction Enabled\"),\n        subtitle: const Text(\n            \"Indicates whether the web view's built-in find interaction native UI is enabled or not.\"),\n        value: currentWebViewModel.settings?.isFindInteractionEnabled ?? false,\n        onChanged: (value) {\n          currentWebViewModel.settings?.isFindInteractionEnabled = value;\n          webViewController?.setSettings(\n              settings: currentWebViewModel.settings ?? InAppWebViewSettings());\n          windowModel.saveInfo();\n          setState(() {});\n        },\n      ),\n    ];\n\n    return widgets;\n  }\n}\n"
  },
  {
    "path": "lib/pages/settings/main.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/pages/settings/android_settings.dart';\nimport 'package:flutter_browser/pages/settings/cross_platform_settings.dart';\nimport 'package:flutter_browser/pages/settings/ios_settings.dart';\nimport 'package:flutter_font_icons/flutter_font_icons.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport '../../app_bar/custom_app_bar_wrapper.dart';\nimport '../../custom_popup_menu_item.dart';\n\nclass PopupSettingsMenuActions {\n  // ignore: constant_identifier_names\n  static const String RESET_BROWSER_SETTINGS = \"Reset Browser Settings\";\n\n  // ignore: constant_identifier_names\n  static const String RESET_WEBVIEW_SETTINGS = \"Reset WebView Settings\";\n\n  static const List<String> choices = <String>[\n    RESET_BROWSER_SETTINGS,\n    RESET_WEBVIEW_SETTINGS,\n  ];\n}\n\nclass SettingsPage extends StatefulWidget {\n  const SettingsPage({super.key});\n\n  @override\n  State<SettingsPage> createState() => _SettingsPageState();\n}\n\nclass _SettingsPageState extends State<SettingsPage> {\n  @override\n  Widget build(BuildContext context) {\n    return DefaultTabController(\n        length: 3,\n        child: Scaffold(\n          appBar: CustomAppBarWrapper(\n              appBar: AppBar(\n            bottom: TabBar(\n                onTap: (value) {\n                  FocusScope.of(context).unfocus();\n                },\n                tabs: const [\n                  Tab(\n                    text: \"Cross-Platform\",\n                    icon: SizedBox(\n                      width: 25,\n                      height: 25,\n                      child: CircleAvatar(\n                        backgroundImage: AssetImage(\"assets/icon/icon.png\"),\n                      ),\n                    ),\n                  ),\n                  Tab(\n                    text: \"Android\",\n                    icon: Icon(\n                      Icons.android,\n                      color: Colors.green,\n                    ),\n                  ),\n                  Tab(\n                    text: \"iOS\",\n                    icon: Icon(AntDesign.apple1),\n                  ),\n                ]),\n            title: const Text(\n              \"Settings\",\n            ),\n            actions: <Widget>[\n              PopupMenuButton<String>(\n                onSelected: _popupMenuChoiceAction,\n                itemBuilder: (context) {\n                  const items = [\n                    CustomPopupMenuItem<String>(\n                      enabled: true,\n                      value: PopupSettingsMenuActions.RESET_BROWSER_SETTINGS,\n                      child: Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            Text(PopupSettingsMenuActions\n                                .RESET_BROWSER_SETTINGS),\n                            Icon(\n                              Foundation.web,\n                              color: Colors.black,\n                            )\n                          ]),\n                    ),\n                    CustomPopupMenuItem<String>(\n                      enabled: true,\n                      value: PopupSettingsMenuActions.RESET_WEBVIEW_SETTINGS,\n                      child: Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            Text(PopupSettingsMenuActions\n                                .RESET_WEBVIEW_SETTINGS),\n                            Icon(\n                              MaterialIcons.web,\n                              color: Colors.black,\n                            )\n                          ]),\n                    )\n                  ];\n\n                  return items;\n                },\n              )\n            ],\n          )),\n          body: const TabBarView(\n            physics: NeverScrollableScrollPhysics(),\n            children: [\n              CrossPlatformSettings(),\n              AndroidSettings(),\n              IOSSettings(),\n            ],\n          ),\n        ));\n  }\n\n  void _popupMenuChoiceAction(String choice) async {\n    switch (choice) {\n      case PopupSettingsMenuActions.RESET_BROWSER_SETTINGS:\n        var browserModel = Provider.of<BrowserModel>(context, listen: false);\n        setState(() {\n          browserModel.updateSettings(BrowserSettings());\n          browserModel.save();\n        });\n        break;\n      case PopupSettingsMenuActions.RESET_WEBVIEW_SETTINGS:\n        var browserModel = Provider.of<BrowserModel>(context, listen: false);\n        browserModel.getSettings();\n        var currentWebViewModel =\n            Provider.of<WebViewModel>(context, listen: false);\n        var webViewController = currentWebViewModel.webViewController;\n        await webViewController?.setSettings(\n            settings: InAppWebViewSettings(\n                incognito: currentWebViewModel.isIncognitoMode,\n                useOnDownloadStart: true,\n                useOnLoadResource: true,\n                safeBrowsingEnabled: true,\n                allowsLinkPreview: false,\n                isFraudulentWebsiteWarningEnabled: true));\n        currentWebViewModel.settings = await webViewController?.getSettings();\n        browserModel.save();\n        setState(() {});\n        break;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/popup_menu_actions.dart",
    "content": "import 'package:flutter_browser/util.dart';\n\nclass PopupMenuActions {\n  // ignore: constant_identifier_names\n  static const String OPEN_NEW_WINDOW = \"Open New Window\";\n  // ignore: constant_identifier_names\n  static const String SAVE_WINDOW = \"Save Window\";\n  // ignore: constant_identifier_names\n  static const String SAVED_WINDOWS = \"Saved Windows\";\n  // ignore: constant_identifier_names\n  static const String NEW_TAB = \"New tab\";\n  // ignore: constant_identifier_names\n  static const String NEW_INCOGNITO_TAB = \"New incognito tab\";\n  // ignore: constant_identifier_names\n  static const String FAVORITES = \"Favorites\";\n  // ignore: constant_identifier_names\n  static const String HISTORY = \"History\";\n  // ignore: constant_identifier_names\n  static const String WEB_ARCHIVES = \"Web Archives\";\n  // ignore: constant_identifier_names\n  static const String SHARE = \"Share\";\n  // ignore: constant_identifier_names\n  static const String FIND_ON_PAGE = \"Find on page\";\n  // ignore: constant_identifier_names\n  static const String DESKTOP_MODE = \"Desktop mode\";\n  // ignore: constant_identifier_names\n  static const String SETTINGS = \"Settings\";\n  // ignore: constant_identifier_names\n  static const String DEVELOPERS = \"Developers\";\n  // ignore: constant_identifier_names\n  static const String INAPPWEBVIEW_PROJECT = \"InAppWebView Project\";\n\n  static List<String> get choices {\n    if (Util.isMobile()) {\n      return [\n        NEW_TAB,\n        NEW_INCOGNITO_TAB,\n        FAVORITES,\n        HISTORY,\n        WEB_ARCHIVES,\n        SHARE,\n        FIND_ON_PAGE,\n        SETTINGS,\n        DEVELOPERS,\n        INAPPWEBVIEW_PROJECT\n      ];\n    }\n    return [\n      OPEN_NEW_WINDOW,\n      SAVE_WINDOW,\n      SAVED_WINDOWS,\n      NEW_TAB,\n      NEW_INCOGNITO_TAB,\n      FAVORITES,\n      HISTORY,\n      WEB_ARCHIVES,\n      SHARE,\n      FIND_ON_PAGE,\n      SETTINGS,\n      DEVELOPERS,\n      INAPPWEBVIEW_PROJECT\n    ];\n}\n}\n"
  },
  {
    "path": "lib/project_info_popup.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_browser/models/browser_model.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_browser/webview_tab.dart';\nimport 'package:flutter_font_icons/flutter_font_icons.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:provider/provider.dart';\n\nimport 'animated_flutter_browser_logo.dart';\nimport 'models/window_model.dart';\n\nclass ProjectInfoPopup extends StatefulWidget {\n  const ProjectInfoPopup({super.key});\n\n  @override\n  State<StatefulWidget> createState() => _ProjectInfoPopupState();\n}\n\nclass _ProjectInfoPopupState extends State<ProjectInfoPopup> {\n  @override\n  Widget build(BuildContext context) {\n    var children = <Widget>[\n      RichText(\n        text: const TextSpan(children: [\n          TextSpan(\n            text: \"Do you like this project? Give a \",\n            style: TextStyle(color: Colors.black),\n          ),\n          WidgetSpan(\n            alignment: PlaceholderAlignment.middle,\n            child: Icon(\n              Icons.star,\n              size: 25,\n              color: Colors.yellow,\n            ),\n          ),\n          TextSpan(text: \" to\", style: TextStyle(color: Colors.black))\n        ]),\n      ),\n      ElevatedButton.icon(\n        icon: const Icon(\n          MaterialCommunityIcons.github,\n          size: 40.0,\n        ),\n        style: ButtonStyle(\n            backgroundColor: MaterialStateColor.resolveWith(\n                (states) => Colors.grey.shade300)),\n        label: RichText(\n          text: const TextSpan(children: [\n            TextSpan(text: \"Github: \", style: TextStyle(color: Colors.black)),\n            TextSpan(\n                text: \"pichillilorenzo/flutter_inappwebview\",\n                style: TextStyle(color: Colors.blue))\n          ]),\n        ),\n        onPressed: () {\n          final windowModel = Provider.of<WindowModel>(context, listen: false);\n          windowModel.addTab(WebViewTab(\n            key: GlobalKey(),\n            webViewModel: WebViewModel(\n                url: WebUri(\n                    \"https://github.com/pichillilorenzo/flutter_inappwebview\")),\n          ));\n          Navigator.pop(context);\n        },\n      ),\n      RichText(\n        text: const TextSpan(children: [\n          TextSpan(text: \"and to\", style: TextStyle(color: Colors.black)),\n        ]),\n      ),\n      ElevatedButton.icon(\n        icon: const Icon(\n          MaterialCommunityIcons.github,\n          size: 40.0,\n        ),\n        style: ButtonStyle(\n            backgroundColor: MaterialStateColor.resolveWith(\n                (states) => Colors.grey.shade300)),\n        label: RichText(\n          text: const TextSpan(children: [\n            TextSpan(text: \"Github: \", style: TextStyle(color: Colors.black)),\n            TextSpan(\n                text: \"pichillilorenzo/flutter_browser_app\",\n                style: TextStyle(color: Colors.blue))\n          ]),\n        ),\n        onPressed: () {\n          final windowModel = Provider.of<WindowModel>(context, listen: false);\n          windowModel.addTab(WebViewTab(\n            key: GlobalKey(),\n            webViewModel: WebViewModel(\n                url: WebUri(\n                    \"https://github.com/pichillilorenzo/flutter_browser_app\")),\n          ));\n          Navigator.pop(context);\n        },\n      ),\n      const SizedBox(\n        height: 20.0,\n      ),\n      SizedBox(\n        width: 250.0,\n        child: RichText(\n          textAlign: TextAlign.center,\n          text: const TextSpan(children: [\n            TextSpan(\n              text:\n                  \"Also, if you want, you can support these projects with a donation. Thanks!\",\n              style: TextStyle(color: Colors.black),\n            ),\n          ]),\n        ),\n      ),\n    ];\n\n    if (Util.isIOS() || Util.isMacOS()) {\n      children.addAll(<Widget>[\n        const SizedBox(\n          height: 20.0,\n        ),\n        ElevatedButton.icon(\n          icon: const Icon(\n            Icons.arrow_back_ios,\n            size: 30.0,\n          ),\n          label: const Text(\n            \"Go Back\",\n            style: TextStyle(fontSize: 20.0),\n          ),\n          onPressed: () {\n            Navigator.pop(context);\n          },\n        ),\n      ]);\n    }\n\n    return Scaffold(\n      body: Center(\n        child: OrientationBuilder(\n          builder: (context, orientation) {\n            if (Orientation.landscape == orientation) {\n              var rowChildren = <Widget>[\n                const AnimatedFlutterBrowserLogo(),\n                const SizedBox(\n                  width: 80.0,\n                ),\n              ];\n              rowChildren.add(Column(\n                mainAxisSize: MainAxisSize.min,\n                crossAxisAlignment: CrossAxisAlignment.center,\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: children,\n              ));\n\n              return Row(\n                mainAxisSize: MainAxisSize.max,\n                crossAxisAlignment: CrossAxisAlignment.center,\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: rowChildren,\n              );\n            }\n\n            var columnChildren = <Widget>[\n              const AnimatedFlutterBrowserLogo(),\n              const SizedBox(\n                height: 80.0,\n              ),\n            ];\n            columnChildren.addAll(children);\n\n            return Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.center,\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: columnChildren,\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/tab_popup_menu_actions.dart",
    "content": "class TabPopupMenuActions {\n  // ignore: constant_identifier_names\n  static const String CLOSE_TABS = \"Close tabs\";\n  // ignore: constant_identifier_names\n  static const String NEW_TAB = \"New tab\";\n  // ignore: constant_identifier_names\n  static const String NEW_INCOGNITO_TAB = \"New incognito tab\";\n\n  static const List<String> choices = <String>[\n    CLOSE_TABS,\n    NEW_TAB,\n    NEW_INCOGNITO_TAB\n  ];\n}\n"
  },
  {
    "path": "lib/tab_viewer.dart",
    "content": "import 'dart:async';\nimport 'dart:math';\n\nimport 'package:flutter/material.dart';\n\nimport 'main.dart';\n\nclass ScrollableTab extends StatefulWidget {\n  final Widget child;\n  final double top;\n  final Function? onTap;\n\n  const ScrollableTab(\n      {super.key, required this.child, this.top = 0.0, this.onTap});\n\n  @override\n  State<ScrollableTab> createState() => _ScrollableTabState();\n}\n\nclass _ScrollableTabState extends State<ScrollableTab> {\n  @override\n  Widget build(BuildContext context) {\n    return Positioned(\n      top: widget.top,\n      height: MediaQuery.of(context).size.height,\n      width: MediaQuery.of(context).size.width,\n      child: GestureDetector(\n        onTap: () {\n          if (widget.onTap != null) {\n            widget.onTap!();\n          }\n        },\n        child: Transform.scale(\n          scale: 0.95,\n          child: Column(\n            children: <Widget>[\n              Expanded(\n                child: widget.child,\n              )\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass TabViewer extends StatefulWidget {\n  final List<Widget> children;\n  final int currentIndex;\n  final Function(int index)? onTap;\n\n  const TabViewer(\n      {super.key, required this.children, this.onTap, this.currentIndex = 0});\n\n  @override\n  State<TabViewer> createState() => _TabViewerState();\n}\n\nclass _TabViewerState extends State<TabViewer>\n    with SingleTickerProviderStateMixin {\n  List<double> positions = [];\n  int focusedIndex = 0;\n  bool initialized = false;\n  Timer? _timer;\n  double decelerationRate = 1.5;\n\n  @override\n  void initState() {\n    super.initState();\n    positions = List.filled(widget.children.length, 0.0, growable: true);\n\n    focusedIndex = widget.currentIndex;\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    if (!initialized) {\n      initialized = true;\n      initialize();\n    }\n  }\n\n  void initialize() {\n    for (var i = 0; i < widget.children.length; i++) {\n      if (widget.currentIndex == i) {\n        if (widget.currentIndex == 0) {\n          positions[widget.currentIndex] = TAB_VIEWER_TOP_OFFSET_1;\n        } else if (widget.currentIndex == 1) {\n          positions[widget.currentIndex] = TAB_VIEWER_TOP_OFFSET_2;\n        } else if (widget.currentIndex >= 2) {\n          positions[widget.currentIndex] = TAB_VIEWER_TOP_OFFSET_3;\n        }\n      } else {\n        if (i < widget.currentIndex) {\n          if (i == 0) {\n            positions[i] = TAB_VIEWER_TOP_OFFSET_1;\n          } else if (i == 1) {\n            positions[i] = TAB_VIEWER_TOP_OFFSET_2;\n          } else if (i >= 2) {\n            positions[i] = TAB_VIEWER_TOP_OFFSET_3;\n          }\n        } else {\n          if (i == positions.length - 1) {\n            positions[i] =\n                MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_1;\n          } else if (i == positions.length - 2) {\n            positions[i] =\n                MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_2;\n          } else if (i <= positions.length - 3) {\n            positions[i] =\n                MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_3;\n          }\n        }\n      }\n    }\n  }\n\n  @override\n  void didUpdateWidget(TabViewer oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    var diffLength = oldWidget.children.length - widget.children.length;\n    if (diffLength > 0) {\n      _timer?.cancel();\n      positions.removeRange(\n          positions.length - diffLength - 1, positions.length - 1);\n      focusedIndex = focusedIndex - 1 < 0 ? 0 : focusedIndex - 1;\n      if (positions.length == 1) {\n        positions[0] = TAB_VIEWER_TOP_OFFSET_1;\n      }\n    }\n  }\n\n  @override\n  void dispose() {\n    _timer?.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: GestureDetector(\n          onVerticalDragUpdate: (details) {\n            setState(() {\n              _timer?.cancel();\n              updatePositions(details.delta.dy);\n            });\n          },\n          onVerticalDragEnd: (details) {\n            var dy = details.velocity.pixelsPerSecond.dy / (1000 / 16);\n            var deceleration = dy > 0 ? -decelerationRate : decelerationRate;\n\n            _timer = Timer.periodic(const Duration(milliseconds: 16), (timer) {\n              if (positions.isEmpty ||\n                  (deceleration < 0 && dy <= 0) ||\n                  (deceleration >= 0 && dy >= 0)) {\n                _timer?.cancel();\n                return;\n              }\n\n              setState(() {\n                updatePositions(dy);\n              });\n              dy = dy + deceleration;\n            });\n          },\n          child: Stack(\n            children: widget.children.map((tab) {\n              var index = widget.children.indexOf(tab);\n              var opacity = 0.2;\n              if (index == focusedIndex &&\n                  index != 0 &&\n                  index != positions.length - 1) {\n                opacity = 0.15;\n              } else if ((index > 2 &&\n                      positions[index] <= TAB_VIEWER_TOP_OFFSET_3) ||\n                  (index < positions.length - 3 &&\n                      positions[index] >=\n                          MediaQuery.of(context).size.height -\n                              TAB_VIEWER_BOTTOM_OFFSET_3)) {\n                opacity = 0.0;\n              }\n\n              double scale = 1.0;\n              if (positions[index] < TAB_VIEWER_TOP_SCALE_TOP_OFFSET) {\n                scale =\n                    (positions[index] / TAB_VIEWER_TOP_SCALE_TOP_OFFSET) + 0.85;\n                if (scale > 1) {\n                  scale = 1.0;\n                }\n              } else if (positions[index] >\n                  MediaQuery.of(context).size.height -\n                      TAB_VIEWER_TOP_SCALE_BOTTOM_OFFSET) {\n                var diff = MediaQuery.of(context).size.height -\n                    TAB_VIEWER_BOTTOM_OFFSET_1 -\n                    positions[index];\n                scale = (diff / TAB_VIEWER_TOP_SCALE_BOTTOM_OFFSET) + 0.7;\n                if (scale > 1) {\n                  scale = 1.0;\n                }\n              } else {\n                scale = 1.0;\n              }\n\n              return ScrollableTab(\n                onTap: () {\n                  if (widget.onTap != null) {\n                    widget.onTap!(index);\n                  }\n                },\n                top: positions[index],\n                child: Transform(\n                  transform: Matrix4.identity()..scale(scale, scale),\n                  alignment: Alignment.topCenter,\n                  child: Container(\n                      decoration: BoxDecoration(boxShadow: [\n                        BoxShadow(\n                          color: Colors.black.withOpacity(opacity),\n                          spreadRadius: 5,\n                          blurRadius: 5,\n                        ),\n                      ]),\n                      child: tab),\n                ),\n              );\n            }).toList(),\n          )),\n    );\n  }\n\n  void updatePositions(double dy) {\n    positions[focusedIndex] =\n        focusedIndex != 0 ? positions[focusedIndex] + dy : 0.0;\n    if (focusedIndex == 0 &&\n        positions[focusedIndex] <= TAB_VIEWER_TOP_OFFSET_1) {\n      positions[focusedIndex] = TAB_VIEWER_TOP_OFFSET_1;\n      focusedIndex = min(positions.length - 1, focusedIndex);\n    } else if (focusedIndex == 1 &&\n        positions[focusedIndex] < TAB_VIEWER_TOP_OFFSET_2) {\n      positions[focusedIndex] = TAB_VIEWER_TOP_OFFSET_2;\n      focusedIndex = min(positions.length - 1, focusedIndex + 1);\n    } else if (focusedIndex >= 2 &&\n        positions[focusedIndex] < TAB_VIEWER_TOP_OFFSET_3) {\n      positions[focusedIndex] = TAB_VIEWER_TOP_OFFSET_3;\n      focusedIndex = min(positions.length - 1, focusedIndex + 1);\n    } else if (focusedIndex == positions.length - 1 &&\n        positions[focusedIndex] >\n            MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_1) {\n      positions[focusedIndex] =\n          MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_1;\n      focusedIndex = max(0, focusedIndex - 1);\n    } else if (focusedIndex == positions.length - 2 &&\n        positions[focusedIndex] >\n            MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_2) {\n      positions[focusedIndex] =\n          MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_2;\n      focusedIndex = max(0, focusedIndex - 1);\n    } else if (focusedIndex <= positions.length - 3 &&\n        positions[focusedIndex] >\n            MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_3) {\n      positions[focusedIndex] =\n          MediaQuery.of(context).size.height - TAB_VIEWER_BOTTOM_OFFSET_3;\n      focusedIndex = max(0, focusedIndex - 1);\n    }\n    if (focusedIndex == 0 &&\n        dy < 0 &&\n        positions.isNotEmpty &&\n        focusedIndex + 1 < positions.length) {\n      focusedIndex++;\n      positions[focusedIndex] = positions[focusedIndex] + dy;\n    }\n\n    if (focusedIndex == 0) {\n      _timer?.cancel();\n    } else if (focusedIndex == positions.length - 1 &&\n        positions[focusedIndex] <= TAB_VIEWER_TOP_OFFSET_3) {\n      _timer?.cancel();\n    } else if (focusedIndex == positions.length - 2 &&\n        positions.length == 2 &&\n        positions[focusedIndex] <= TAB_VIEWER_TOP_OFFSET_2) {\n      _timer?.cancel();\n    }\n  }\n}\n"
  },
  {
    "path": "lib/tab_viewer_popup_menu_actions.dart",
    "content": "class TabViewerPopupMenuActions {\n  // ignore: constant_identifier_names\n  static const String NEW_TAB = \"New tab\";\n  // ignore: constant_identifier_names\n  static const String NEW_INCOGNITO_TAB = \"New incognito tab\";\n  // ignore: constant_identifier_names\n  static const String CLOSE_ALL_TABS = \"Close all tabs\";\n  // ignore: constant_identifier_names\n  static const String SETTINGS = \"Settings\";\n\n  static const List<String> choices = <String>[\n    NEW_TAB,\n    NEW_INCOGNITO_TAB,\n    CLOSE_ALL_TABS,\n    SETTINGS,\n  ];\n}\n"
  },
  {
    "path": "lib/util.dart",
    "content": "import 'package:flutter/foundation.dart';\n\nclass Util {\n  static bool urlIsSecure(Uri url) {\n    return (url.scheme == \"https\") || Util.isLocalizedContent(url);\n  }\n\n  static bool isLocalizedContent(Uri url) {\n    return (url.scheme == \"file\" ||\n        url.scheme == \"chrome\" ||\n        url.scheme == \"data\" ||\n        url.scheme == \"javascript\" ||\n        url.scheme == \"about\");\n  }\n\n  static bool isMobile() {\n    return isAndroid() || isIOS();\n  }\n\n  static bool isAndroid() {\n    return !kIsWeb && defaultTargetPlatform == TargetPlatform.android;\n  }\n\n  static bool isIOS() {\n    return !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;\n  }\n\n  static bool isDesktop() {\n    return !isMobile();\n  }\n\n  static bool isMacOS() {\n    return !kIsWeb && defaultTargetPlatform == TargetPlatform.macOS;\n  }\n\n  static bool isWindows() {\n    return !kIsWeb && defaultTargetPlatform == TargetPlatform.windows;\n  }\n}"
  },
  {
    "path": "lib/webview_tab.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_browser/main.dart';\nimport 'package:flutter_browser/models/webview_model.dart';\nimport 'package:flutter_browser/util.dart';\nimport 'package:flutter_downloader/flutter_downloader.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:provider/provider.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nimport 'javascript_console_result.dart';\nimport 'long_press_alert_dialog.dart';\nimport 'models/browser_model.dart';\nimport 'models/window_model.dart';\n\nfinal webViewTabStateKey = GlobalKey<_WebViewTabState>();\n\nclass WebViewTab extends StatefulWidget {\n  const WebViewTab({super.key, required this.webViewModel});\n\n  final WebViewModel webViewModel;\n\n  @override\n  State<WebViewTab> createState() => _WebViewTabState();\n}\n\nclass _WebViewTabState extends State<WebViewTab> with WidgetsBindingObserver {\n  InAppWebViewController? _webViewController;\n  PullToRefreshController? _pullToRefreshController;\n  FindInteractionController? _findInteractionController;\n  bool _isWindowClosed = false;\n  final FocusNode _focusNode = FocusNode();\n\n  final TextEditingController _httpAuthUsernameController =\n      TextEditingController();\n  final TextEditingController _httpAuthPasswordController =\n      TextEditingController();\n\n  @override\n  void initState() {\n    WidgetsBinding.instance.addObserver(this);\n    super.initState();\n\n    if (Util.isIOS() || Util.isAndroid()) {\n      _pullToRefreshController = PullToRefreshController(\n        settings: PullToRefreshSettings(color: Colors.blue),\n        onRefresh: () async {\n          if ([TargetPlatform.iOS].contains(defaultTargetPlatform)) {\n            _webViewController?.loadUrl(\n                urlRequest:\n                    URLRequest(url: await _webViewController?.getUrl()));\n          } else {\n            _webViewController?.reload();\n          }\n        },\n      );\n    }\n\n    if (Util.isIOS() || Util.isAndroid() || Util.isMacOS()) {\n      _findInteractionController = FindInteractionController();\n    }\n  }\n\n  @override\n  void dispose() {\n    _webViewController = null;\n    widget.webViewModel.webViewController = null;\n    widget.webViewModel.pullToRefreshController = null;\n    widget.webViewModel.findInteractionController = null;\n\n    _httpAuthUsernameController.dispose();\n    _httpAuthPasswordController.dispose();\n\n    _focusNode.dispose();\n\n    WidgetsBinding.instance.removeObserver(this);\n\n    super.dispose();\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    if (_webViewController != null && (Util.isAndroid() || Util.isWindows())) {\n      if (state == AppLifecycleState.paused) {\n        pauseAll();\n      } else {\n        resumeAll();\n      }\n    }\n  }\n\n  void pauseAll() {\n    if (Util.isAndroid() || Util.isWindows()) {\n      _webViewController?.pause();\n    }\n    pauseTimers();\n  }\n\n  void resumeAll() {\n    if (Util.isAndroid() || Util.isWindows()) {\n      _webViewController?.resume();\n    }\n    resumeTimers();\n  }\n\n  void pause() {\n    if (Util.isAndroid() || Util.isWindows()) {\n      _webViewController?.pause();\n    }\n  }\n\n  void resume() {\n    if (Util.isAndroid() || Util.isWindows()) {\n      _webViewController?.resume();\n    }\n  }\n\n  void pauseTimers() {\n    if (!Util.isWindows()) {\n      _webViewController?.pauseTimers();\n    }\n  }\n\n  void resumeTimers() {\n    if (!Util.isWindows()) {\n      _webViewController?.resumeTimers();\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CallbackShortcuts(\n        bindings: <ShortcutActivator, VoidCallback>{\n          LogicalKeySet(LogicalKeyboardKey.meta, LogicalKeyboardKey.keyR): () {\n            _webViewController?.reload();\n          }\n        },\n        child: Focus(\n            autofocus: true,\n            focusNode: _focusNode,\n            child: Container(\n              color: Colors.white,\n              child: _buildWebView(),\n            )));\n  }\n\n  InAppWebView _buildWebView() {\n    var browserModel = Provider.of<BrowserModel>(context, listen: true);\n    var windowModel = Provider.of<WindowModel>(context, listen: true);\n    var settings = browserModel.getSettings();\n    var currentWebViewModel = Provider.of<WebViewModel>(context, listen: true);\n\n    if (Util.isAndroid()) {\n      InAppWebViewController.setWebContentsDebuggingEnabled(\n          settings.debuggingEnabled);\n    }\n\n    var initialSettings = widget.webViewModel.settings!;\n    initialSettings.isInspectable = settings.debuggingEnabled;\n    initialSettings.useOnDownloadStart = true;\n    initialSettings.useOnLoadResource = true;\n    initialSettings.useShouldOverrideUrlLoading = true;\n    initialSettings.javaScriptCanOpenWindowsAutomatically = true;\n    if (Util.isIOS() || Util.isAndroid()) {\n      initialSettings.userAgent =\n          \"Mozilla/5.0 (Linux; Android 9; LG-H870 Build/PKQ1.190522.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36\";\n    }\n    initialSettings.transparentBackground = true;\n\n    initialSettings.safeBrowsingEnabled = true;\n    initialSettings.disableDefaultErrorPage = true;\n    initialSettings.supportMultipleWindows = true;\n    initialSettings.verticalScrollbarThumbColor =\n        const Color.fromRGBO(0, 0, 0, 0.5);\n    initialSettings.horizontalScrollbarThumbColor =\n        const Color.fromRGBO(0, 0, 0, 0.5);\n\n    initialSettings.allowsLinkPreview = false;\n    initialSettings.isFraudulentWebsiteWarningEnabled = true;\n    initialSettings.disableLongPressContextMenuOnLinks = true;\n    initialSettings.allowingReadAccessTo = WebUri('file://$WEB_ARCHIVE_DIR/');\n\n    return InAppWebView(\n      keepAlive: widget.webViewModel.keepAlive,\n      webViewEnvironment: webViewEnvironment,\n      initialUrlRequest: URLRequest(url: widget.webViewModel.url),\n      initialSettings: initialSettings,\n      windowId: widget.webViewModel.windowId,\n      pullToRefreshController: _pullToRefreshController,\n      findInteractionController: _findInteractionController,\n      onWebViewCreated: (controller) async {\n        initialSettings.transparentBackground = false;\n        await controller.setSettings(settings: initialSettings);\n\n        _webViewController = controller;\n        widget.webViewModel.webViewController = controller;\n        widget.webViewModel.pullToRefreshController = _pullToRefreshController;\n        widget.webViewModel.findInteractionController =\n            _findInteractionController;\n\n        if (Util.isAndroid()) {\n          controller.startSafeBrowsing();\n        }\n\n        widget.webViewModel.settings = await controller.getSettings();\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n      },\n      onLoadStart: (controller, url) async {\n        widget.webViewModel.isSecure = Util.urlIsSecure(url!);\n        widget.webViewModel.url = url;\n        widget.webViewModel.loaded = false;\n        widget.webViewModel.setLoadedResources([]);\n        widget.webViewModel.setJavaScriptConsoleResults([]);\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        } else if (widget.webViewModel.needsToCompleteInitialLoad) {\n          controller.stopLoading();\n        }\n\n        windowModel.notifyWebViewTabUpdated();\n      },\n      onLoadStop: (controller, url) async {\n        _pullToRefreshController?.endRefreshing();\n\n        widget.webViewModel.url = url;\n        widget.webViewModel.favicon = null;\n        widget.webViewModel.loaded = true;\n\n        var sslCertificateFuture = controller.getCertificate();\n        var titleFuture = controller.getTitle();\n        var faviconsFuture = controller.getFavicons();\n\n        var sslCertificate = await sslCertificateFuture;\n        if (sslCertificate == null && !Util.isLocalizedContent(url!)) {\n          widget.webViewModel.isSecure = false;\n        }\n\n        widget.webViewModel.title = await titleFuture;\n\n        List<Favicon>? favicons;\n        try {\n          favicons = await faviconsFuture;\n        } catch (e) {\n          if (kDebugMode) {\n            print(e);\n          }\n        }\n        if (favicons != null && favicons.isNotEmpty) {\n          for (var fav in favicons) {\n            if (widget.webViewModel.favicon == null) {\n              widget.webViewModel.favicon = fav;\n            } else {\n              if ((widget.webViewModel.favicon!.width == null &&\n                      !widget.webViewModel.favicon!.url\n                          .toString()\n                          .endsWith(\"favicon.ico\")) ||\n                  (fav.width != null &&\n                      widget.webViewModel.favicon!.width != null &&\n                      fav.width! > widget.webViewModel.favicon!.width!)) {\n                widget.webViewModel.favicon = fav;\n              }\n            }\n          }\n        }\n\n        if (isCurrentTab(currentWebViewModel)) {\n          widget.webViewModel.needsToCompleteInitialLoad = false;\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n\n          var screenshotData = controller\n              .takeScreenshot(\n                  screenshotConfiguration: ScreenshotConfiguration(\n                      compressFormat: CompressFormat.JPEG, quality: 20))\n              .timeout(\n                const Duration(milliseconds: 1500),\n                onTimeout: () => null,\n              );\n          widget.webViewModel.screenshot = await screenshotData;\n        }\n\n        windowModel.notifyWebViewTabUpdated();\n      },\n      onProgressChanged: (controller, progress) {\n        if (progress == 100) {\n          _pullToRefreshController?.endRefreshing();\n        }\n\n        widget.webViewModel.progress = progress / 100;\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n      },\n      onUpdateVisitedHistory: (controller, url, androidIsReload) async {\n        widget.webViewModel.url = url;\n        widget.webViewModel.title = await controller.getTitle();\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n        windowModel.notifyWebViewTabUpdated();\n      },\n      onLongPressHitTestResult: (controller, hitTestResult) async {\n        if (LongPressAlertDialog.hitTestResultSupported\n            .contains(hitTestResult.type)) {\n          var requestFocusNodeHrefResult =\n              await controller.requestFocusNodeHref();\n\n          if (requestFocusNodeHrefResult != null) {\n            showDialog(\n              context: context,\n              builder: (context) {\n                return LongPressAlertDialog(\n                  webViewModel: widget.webViewModel,\n                  hitTestResult: hitTestResult,\n                  requestFocusNodeHrefResult: requestFocusNodeHrefResult,\n                );\n              },\n            );\n          }\n        }\n      },\n      onConsoleMessage: (controller, consoleMessage) {\n        Color consoleTextColor = Colors.black;\n        Color consoleBackgroundColor = Colors.transparent;\n        IconData? consoleIconData;\n        Color? consoleIconColor;\n        if (consoleMessage.messageLevel == ConsoleMessageLevel.ERROR) {\n          consoleTextColor = Colors.red;\n          consoleIconData = Icons.report_problem;\n          consoleIconColor = Colors.red;\n        } else if (consoleMessage.messageLevel == ConsoleMessageLevel.TIP) {\n          consoleTextColor = Colors.blue;\n          consoleIconData = Icons.info;\n          consoleIconColor = Colors.blueAccent;\n        } else if (consoleMessage.messageLevel == ConsoleMessageLevel.WARNING) {\n          consoleBackgroundColor = const Color.fromRGBO(255, 251, 227, 1);\n          consoleIconData = Icons.report_problem;\n          consoleIconColor = Colors.orangeAccent;\n        }\n\n        widget.webViewModel.addJavaScriptConsoleResults(JavaScriptConsoleResult(\n          data: consoleMessage.message,\n          textColor: consoleTextColor,\n          backgroundColor: consoleBackgroundColor,\n          iconData: consoleIconData,\n          iconColor: consoleIconColor,\n        ));\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n      },\n      onLoadResource: (controller, resource) {\n        widget.webViewModel.addLoadedResources(resource);\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n      },\n      shouldOverrideUrlLoading: (controller, navigationAction) async {\n        var url = navigationAction.request.url;\n\n        if (url != null &&\n            ![\"http\", \"https\", \"file\", \"chrome\", \"data\", \"javascript\", \"about\"]\n                .contains(url.scheme)) {\n          if (await canLaunchUrl(url)) {\n            // Launch the App\n            await launchUrl(\n              url,\n            );\n            // and cancel the request\n            return NavigationActionPolicy.CANCEL;\n          }\n        }\n\n        return NavigationActionPolicy.ALLOW;\n      },\n      onDownloadStartRequest: (controller, url) async {\n        String path = url.url.path;\n        String fileName = path.substring(path.lastIndexOf('/') + 1);\n\n        await FlutterDownloader.enqueue(\n          url: url.toString(),\n          fileName: fileName,\n          savedDir: (await getTemporaryDirectory()).path,\n          showNotification: true,\n          openFileFromNotification: true,\n        );\n      },\n      onReceivedServerTrustAuthRequest: (controller, challenge) async {\n        var sslError = challenge.protectionSpace.sslError;\n        if (sslError != null && (sslError.code != null)) {\n          if ((Util.isIOS() || Util.isMacOS()) &&\n              sslError.code == SslErrorType.UNSPECIFIED) {\n            return ServerTrustAuthResponse(\n                action: ServerTrustAuthResponseAction.PROCEED);\n          }\n          widget.webViewModel.isSecure = false;\n          if (isCurrentTab(currentWebViewModel)) {\n            currentWebViewModel.updateWithValue(widget.webViewModel);\n          }\n          return ServerTrustAuthResponse(\n              action: ServerTrustAuthResponseAction.CANCEL);\n        }\n        return ServerTrustAuthResponse(\n            action: ServerTrustAuthResponseAction.PROCEED);\n      },\n      onReceivedError: (controller, request, error) async {\n        var isForMainFrame = request.isForMainFrame ?? false;\n        if (!isForMainFrame) {\n          return;\n        }\n\n        _pullToRefreshController?.endRefreshing();\n\n        if ((Util.isIOS() || Util.isMacOS() || Util.isWindows()) &&\n            error.type == WebResourceErrorType.CANCELLED) {\n          // NSURLErrorDomain\n          return;\n        }\n        if (Util.isWindows() &&\n            error.type == WebResourceErrorType.CONNECTION_ABORTED) {\n          // CONNECTION_ABORTED\n          return;\n        }\n\n        var errorUrl = request.url;\n\n        _webViewController?.loadData(data: \"\"\"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n    <style>\n    ${await InAppWebViewController.tRexRunnerCss}\n    </style>\n    <style>\n    .interstitial-wrapper {\n        box-sizing: border-box;\n        font-size: 1em;\n        line-height: 1.6em;\n        margin: 0 auto 0;\n        max-width: 600px;\n        width: 100%;\n    }\n    </style>\n</head>\n<body>\n    ${await InAppWebViewController.tRexRunnerHtml}\n    <div class=\"interstitial-wrapper\">\n      <h1>Website not available</h1>\n      <p>Could not load web pages at <strong>$errorUrl</strong> because:</p>\n      <p>${error.description}</p>\n    </div>\n</body>\n    \"\"\", baseUrl: errorUrl, historyUrl: errorUrl);\n\n        widget.webViewModel.url = errorUrl;\n        widget.webViewModel.isSecure = false;\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n      },\n      onTitleChanged: (controller, title) async {\n        widget.webViewModel.title = title;\n\n        if (isCurrentTab(currentWebViewModel)) {\n          currentWebViewModel.updateWithValue(widget.webViewModel);\n        }\n        windowModel.notifyWebViewTabUpdated();\n      },\n      onCreateWindow: (controller, createWindowRequest) async {\n        var webViewTab = WebViewTab(\n          key: GlobalKey(),\n          webViewModel: WebViewModel(\n              url: WebUri(\"about:blank\"),\n              windowId: createWindowRequest.windowId),\n        );\n\n        windowModel.addTab(webViewTab);\n\n        return true;\n      },\n      onCloseWindow: (controller) {\n        if (_isWindowClosed) {\n          return;\n        }\n        _isWindowClosed = true;\n        if (widget.webViewModel.tabIndex != null) {\n          windowModel.closeTab(widget.webViewModel.tabIndex!);\n        }\n      },\n      onPermissionRequest: (controller, permissionRequest) async {\n        return PermissionResponse(\n            resources: permissionRequest.resources,\n            action: PermissionResponseAction.GRANT);\n      },\n      onReceivedHttpAuthRequest: (controller, challenge) async {\n        var action = await createHttpAuthDialog(challenge);\n        return HttpAuthResponse(\n            username: _httpAuthUsernameController.text.trim(),\n            password: _httpAuthPasswordController.text,\n            action: action,\n            permanentPersistence: true);\n      },\n    );\n  }\n\n  bool isCurrentTab(WebViewModel currentWebViewModel) {\n    return currentWebViewModel.tabIndex == widget.webViewModel.tabIndex;\n  }\n\n  Future<HttpAuthResponseAction> createHttpAuthDialog(\n      URLAuthenticationChallenge challenge) async {\n    HttpAuthResponseAction action = HttpAuthResponseAction.CANCEL;\n\n    await showDialog(\n      context: context,\n      builder: (BuildContext context) {\n        return AlertDialog(\n          title: const Text(\"Login\"),\n          content: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            mainAxisSize: MainAxisSize.min,\n            children: <Widget>[\n              Text(challenge.protectionSpace.host),\n              TextField(\n                decoration: const InputDecoration(labelText: \"Username\"),\n                controller: _httpAuthUsernameController,\n              ),\n              TextField(\n                decoration: const InputDecoration(labelText: \"Password\"),\n                controller: _httpAuthPasswordController,\n                obscureText: true,\n              ),\n            ],\n          ),\n          actions: <Widget>[\n            ElevatedButton(\n              child: const Text(\"Cancel\"),\n              onPressed: () {\n                action = HttpAuthResponseAction.CANCEL;\n                Navigator.of(context).pop();\n              },\n            ),\n            ElevatedButton(\n              child: const Text(\"Ok\"),\n              onPressed: () {\n                action = HttpAuthResponseAction.PROCEED;\n                Navigator.of(context).pop();\n              },\n            ),\n          ],\n        );\n      },\n    );\n\n    return action;\n  }\n\n  void onShowTab() async {\n    resume();\n    if (widget.webViewModel.needsToCompleteInitialLoad) {\n      widget.webViewModel.needsToCompleteInitialLoad = false;\n      await widget.webViewModel.webViewController\n          ?.loadUrl(urlRequest: URLRequest(url: widget.webViewModel.url));\n    }\n  }\n\n  void onHideTab() async {\n    pause();\n  }\n}\n"
  },
  {
    "path": "macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\nimport dynamic_color\nimport flutter_inappwebview_macos\nimport multi_window_macos\nimport package_info_plus\nimport path_provider_foundation\nimport screen_retriever_macos\nimport share_plus\nimport sqflite_darwin\nimport sqlite3_flutter_libs\nimport url_launcher_macos\nimport window_manager_plus\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n  DynamicColorPlugin.register(with: registry.registrar(forPlugin: \"DynamicColorPlugin\"))\n  InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: \"InAppWebViewFlutterPlugin\"))\n  MultiWindowMacosPlugin.register(with: registry.registrar(forPlugin: \"MultiWindowMacosPlugin\"))\n  FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: \"FPPPackageInfoPlusPlugin\"))\n  PathProviderPlugin.register(with: registry.registrar(forPlugin: \"PathProviderPlugin\"))\n  ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: \"ScreenRetrieverMacosPlugin\"))\n  SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: \"SharePlusMacosPlugin\"))\n  SqflitePlugin.register(with: registry.registrar(forPlugin: \"SqflitePlugin\"))\n  Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: \"Sqlite3FlutterLibsPlugin\"))\n  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: \"UrlLauncherPlugin\"))\n  WindowManagerPlusPlugin.register(with: registry.registrar(forPlugin: \"WindowManagerPlusPlugin\"))\n}\n"
  },
  {
    "path": "macos/Podfile",
    "content": "platform :osx, '10.14'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\n  target 'RunnerTests' do\n    inherit! :search_paths\n  end\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport window_manager_plus\n\n@main\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return NSApp.windows.filter({$0 is MainFlutterWindow || $0 is WindowManagerPlusFlutterWindow}).count == 1\n  }\n}\n"
  },
  {
    "path": "macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_64.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_1024.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"EPT-qC-fAb\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"rJ0-wn-3NY\"/>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = flutter_browser_app\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterBrowserApp\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2024 com.pichillilorenzo. All rights reserved.\n"
  },
  {
    "path": "macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.device.audio-input</key>\n\t<true/>\n\t<key>com.apple.security.device.camera</key>\n\t<true/>\n\t<key>com.apple.security.files.user-selected.read-write</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n\t<key>com.apple.security.print</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport window_manager_plus\n\nclass MainFlutterWindow: NSWindow {\n    override func awakeFromNib() {\n        let flutterViewController = FlutterViewController()\n        let windowFrame = self.frame\n        self.contentViewController = flutterViewController\n        self.setFrame(windowFrame, display: true)\n        \n        RegisterGeneratedPlugins(registry: flutterViewController)\n        \n        WindowManagerPlusPlugin.RegisterGeneratedPlugins = RegisterGeneratedPlugins\n        \n        super.awakeFromNib()\n    }\n    \n    override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {\n        super.order(place, relativeTo: otherWin)\n        hiddenWindowAtLaunch()\n    }\n}\n"
  },
  {
    "path": "macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.device.audio-input</key>\n\t<true/>\n\t<key>com.apple.security.device.camera</key>\n\t<true/>\n\t<key>com.apple.security.files.user-selected.read-write</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n\t<key>com.apple.security.print</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n\t\t4BBFAC1E5D2C8B138DFCB298 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 186AED4F1B867B24EC5F7409 /* Pods_RunnerTests.framework */; };\n\t\t8ACE87770901D3176994D90E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD3E0B735AAB52185466A7E /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC10EC2044A3C60003C045;\n\t\t\tremoteInfo = Runner;\n\t\t};\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t186AED4F1B867B24EC5F7409 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t1B99D22D5C0D5D5EC821B75F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* flutter_browser_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = flutter_browser_app.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t35444F22AAB02DE48B55ED92 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t37A684FACC8BB37257824797 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t5CD3E0B735AAB52185466A7E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t8EBB53B2F38E1DC10F5FC510 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.debug.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9BC2BA5233543664622D15B0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tF241DF1F78E7835F1BB40829 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t331C80D2294CF70F00263BE5 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4BBFAC1E5D2C8B138DFCB298 /* Pods_RunnerTests.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t8ACE87770901D3176994D90E /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C80D6294CF71000263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t331C80D6294CF71000263BE5 /* RunnerTests */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\t69306C32DD5A79F4DD0FEFD6 /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* flutter_browser_app.app */,\n\t\t\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t69306C32DD5A79F4DD0FEFD6 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9BC2BA5233543664622D15B0 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t37A684FACC8BB37257824797 /* Pods-Runner.release.xcconfig */,\n\t\t\t\t1B99D22D5C0D5D5EC821B75F /* Pods-Runner.profile.xcconfig */,\n\t\t\t\t8EBB53B2F38E1DC10F5FC510 /* Pods-RunnerTests.debug.xcconfig */,\n\t\t\t\t35444F22AAB02DE48B55ED92 /* Pods-RunnerTests.release.xcconfig */,\n\t\t\t\tF241DF1F78E7835F1BB40829 /* Pods-RunnerTests.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t5CD3E0B735AAB52185466A7E /* Pods_Runner.framework */,\n\t\t\t\t186AED4F1B867B24EC5F7409 /* Pods_RunnerTests.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C80D4294CF70F00263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t335696EF5B568A74F3C82598 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t331C80D1294CF70F00263BE5 /* Sources */,\n\t\t\t\t331C80D2294CF70F00263BE5 /* Frameworks */,\n\t\t\t\t331C80D3294CF70F00263BE5 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t90E3942EEFEFF078F8BE9F9A /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t\t2389C4A27EFDA83FF2FD4804 /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* flutter_browser_app.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C80D4294CF70F00263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 33CC10EC2044A3C60003C045;\n\t\t\t\t\t};\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t331C80D4294CF70F00263BE5 /* RunnerTests */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C80D3294CF70F00263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t2389C4A27EFDA83FF2FD4804 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t335696EF5B568A74F3C82598 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n\t\t90E3942EEFEFF078F8BE9F9A /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C80D1294CF70F00263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC10EC2044A3C60003C045 /* Runner */;\n\t\t\ttargetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;\n\t\t};\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t331C80DB294CF71000263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 8EBB53B2F38E1DC10F5FC510 /* Pods-RunnerTests.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterBrowserApp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/flutter_browser_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_browser_app\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C80DC294CF71000263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 35444F22AAB02DE48B55ED92 /* Pods-RunnerTests.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterBrowserApp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/flutter_browser_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_browser_app\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C80DD294CF71000263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F241DF1F78E7835F1BB40829 /* Pods-RunnerTests.profile.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterBrowserApp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/flutter_browser_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_browser_app\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = PFP8UV45Y6;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = PFP8UV45Y6;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = PFP8UV45Y6;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C80DB294CF71000263BE5 /* Debug */,\n\t\t\t\t331C80DC294CF71000263BE5 /* Release */,\n\t\t\t\t331C80DD294CF71000263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"flutter_browser_app.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"flutter_browser_app.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C80D4294CF70F00263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"flutter_browser_app.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"flutter_browser_app.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/RunnerTests/RunnerTests.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "macos/packaging/dmg/make_config.yaml",
    "content": "title: Flutter Browser\ncontents:\n  - x: 448\n    y: 344\n    type: link\n    path: \"/Applications\"\n  - x: 192\n    y: 344\n    type: file\n    path: flutter_browser_app.app"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: flutter_browser\ndescription: A Full-Featured Mobile Browser App (such as the Google Chrome mobile browser) created using Flutter and the features offered by the flutter_inappwebview plugin.\n\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\n# In Windows, build-name is used as the major, minor, and patch parts\n# of the product and file versions while build-number is used as the build suffix.\nversion: 3.1.0+9\n\nenvironment:\n  sdk: ^3.5.0\n  flutter: \">=3.24.0\"\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  flutter:\n    sdk: flutter\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.8\n\n  # For material you.\n  dynamic_color: ^1.7.0\n\n  flutter_inappwebview: ^6.1.5\n  flutter_downloader: ^1.11.8\n  path_provider: ^2.1.4\n  permission_handler: ^11.3.1\n  provider: ^6.1.2\n  share_plus: ^10.0.2\n  # charts_flutter: ^0.9.0\n  crypto: ^3.0.5\n  package_info_plus: ^8.0.2\n  # flutter_icons: ^1.1.0\n  flutter_font_icons: ^2.2.7\n  flutter_svg: ^2.0.10+1\n  mime: ^1.0.6\n  url_launcher: ^6.3.0\n  cached_network_image: ^3.4.1\n  flutter_colorpicker: ^1.1.0\n  intl: any\n  uuid: ^4.5.0\n  collection: any\n  context_menus: ^2.0.0+1\n  multi_window: ^0.2.0\n  sqflite: ^2.3.3+2\n  sqflite_common_ffi: ^2.3.3+1\n  sqlite3_flutter_libs: ^0.5.24\n  window_manager_plus: ^1.0.5\n  path: any\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^4.0.0\n\nflutter_icons:\n  android: \"launcher_icon\"\n  ios: true\n  image_path: \"assets/icon/icon.png\"\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  assets:\n    - assets/images/google_logo.png\n    - assets/images/yahoo_logo.png\n    - assets/images/bing_logo.png\n    - assets/images/duckduckgo_logo.png\n    - assets/images/ecosia_logo.png\n    - assets/icon/icon.png\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware.\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility that Flutter provides. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:flutter_browser/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const FlutterBrowserApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  },
  {
    "path": "windows/.gitignore",
    "content": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "windows/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.14)\nproject(flutter_browser_app LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"flutter_browser_app\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(VERSION 3.14...3.25)\n\n# Define build configuration option.\nget_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\nif(IS_MULTICONFIG)\n  set(CMAKE_CONFIGURATION_TYPES \"Debug;Profile;Release\"\n    CACHE STRING \"\" FORCE)\nelse()\n  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n      STRING \"Flutter build mode\" FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n      \"Debug\" \"Profile\" \"Release\")\n  endif()\nendif()\n# Define settings for the Profile build mode.\nset(CMAKE_EXE_LINKER_FLAGS_PROFILE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_SHARED_LINKER_FLAGS_PROFILE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_C_FLAGS_PROFILE \"${CMAKE_C_FLAGS_RELEASE}\")\nset(CMAKE_CXX_FLAGS_PROFILE \"${CMAKE_CXX_FLAGS_RELEASE}\")\n\n# Use Unicode for all projects.\nadd_definitions(-DUNICODE -D_UNICODE)\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_17)\n  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd\"4100\")\n  target_compile_options(${TARGET} PRIVATE /EHsc)\n  target_compile_definitions(${TARGET} PRIVATE \"_HAS_EXCEPTIONS=0\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<CONFIG:Debug>:_DEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# Application build; see runner/CMakeLists.txt.\nadd_subdirectory(\"runner\")\n\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# Support files are copied into place next to the executable, so that it can\n# run in place. This is done instead of making a separate bundle (as on Linux)\n# so that building and running from within Visual Studio will work.\nset(BUILD_BUNDLE_DIR \"$<TARGET_FILE_DIR:${BINARY_NAME}>\")\n# Make the \"install\" step default, as it's required to run.\nset(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Copy the native assets provided by the build.dart from all packages.\nset(NATIVE_ASSETS_DIR \"${PROJECT_BUILD_DIR}native_assets/windows/\")\ninstall(DIRECTORY \"${NATIVE_ASSETS_DIR}\"\n   DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n   COMPONENT Runtime)\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\ninstall(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n"
  },
  {
    "path": "windows/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.14)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\nset(WRAPPER_ROOT \"${EPHEMERAL_DIR}/cpp_client_wrapper\")\n\n# Set fallback configurations for older versions of the flutter tool.\nif (NOT DEFINED FLUTTER_TARGET_PLATFORM)\n  set(FLUTTER_TARGET_PLATFORM \"windows-x64\")\nendif()\n\n# === Flutter Library ===\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/flutter_windows.dll\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/windows/app.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"flutter_export.h\"\n  \"flutter_windows.h\"\n  \"flutter_messenger.h\"\n  \"flutter_plugin_registrar.h\"\n  \"flutter_texture_registrar.h\"\n)\nlist(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND \"${EPHEMERAL_DIR}/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}.lib\")\nadd_dependencies(flutter flutter_assemble)\n\n# === Wrapper ===\nlist(APPEND CPP_WRAPPER_SOURCES_CORE\n  \"core_implementations.cc\"\n  \"standard_codec.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_PLUGIN\n  \"plugin_registrar.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_APP\n  \"flutter_engine.cc\"\n  \"flutter_view_controller.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND \"${WRAPPER_ROOT}/\")\n\n# Wrapper sources needed for a plugin.\nadd_library(flutter_wrapper_plugin STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n)\napply_standard_settings(flutter_wrapper_plugin)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  POSITION_INDEPENDENT_CODE ON)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_link_libraries(flutter_wrapper_plugin PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_plugin PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_plugin flutter_assemble)\n\n# Wrapper sources needed for the runner.\nadd_library(flutter_wrapper_app STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\napply_standard_settings(flutter_wrapper_app)\ntarget_link_libraries(flutter_wrapper_app PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_app PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_app flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nset(PHONY_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/_phony_\")\nset_source_files_properties(\"${PHONY_OUTPUT}\" PROPERTIES SYMBOLIC TRUE)\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}\n    ${CPP_WRAPPER_SOURCES_APP}\n    ${PHONY_OUTPUT}\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat\"\n      ${FLUTTER_TARGET_PLATFORM} $<CONFIG>\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\n"
  },
  {
    "path": "windows/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <dynamic_color/dynamic_color_plugin_c_api.h>\n#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>\n#include <permission_handler_windows/permission_handler_windows_plugin.h>\n#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>\n#include <share_plus/share_plus_windows_plugin_c_api.h>\n#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>\n#include <url_launcher_windows/url_launcher_windows.h>\n#include <window_manager_plus/window_manager_plus_plugin.h>\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n  DynamicColorPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"DynamicColorPluginCApi\"));\n  FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FlutterInappwebviewWindowsPluginCApi\"));\n  PermissionHandlerWindowsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"PermissionHandlerWindowsPlugin\"));\n  ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"ScreenRetrieverWindowsPluginCApi\"));\n  SharePlusWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"SharePlusWindowsPluginCApi\"));\n  Sqlite3FlutterLibsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"Sqlite3FlutterLibsPlugin\"));\n  UrlLauncherWindowsRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"UrlLauncherWindows\"));\n  WindowManagerPlusPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"WindowManagerPlusPlugin\"));\n}\n"
  },
  {
    "path": "windows/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter/plugin_registry.h>\n\n// Registers Flutter plugins.\nvoid RegisterPlugins(flutter::PluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "windows/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  dynamic_color\n  flutter_inappwebview_windows\n  permission_handler_windows\n  screen_retriever_windows\n  share_plus\n  sqlite3_flutter_libs\n  url_launcher_windows\n  window_manager_plus\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "windows/packaging/msix/make_config.yaml",
    "content": "display_name: Flutter Browser\npublisher_display_name: pichillilorenzo\nidentity_name: com.pichillilorenzo.flutter-browser\nlogo_path: assets/icon/icon.png\ncapabilities: internetClient, location, microphone, webcam\nlanguages: en-us\ninstall_certificate: \"false\""
  },
  {
    "path": "windows/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(runner LANGUAGES CXX)\n\n# Define the application target. To change its name, change BINARY_NAME in the\n# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer\n# work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME} WIN32\n  \"flutter_window.cpp\"\n  \"main.cpp\"\n  \"utils.cpp\"\n  \"win32_window.cpp\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n  \"Runner.rc\"\n  \"runner.exe.manifest\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add preprocessor definitions for the build version.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION=\\\"${FLUTTER_VERSION}\\\"\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}\")\n\n# Disable Windows macros that collide with C++ standard library functions.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"NOMINMAX\")\n\n# Add dependency libraries and include directories. Add any application-specific\n# dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)\ntarget_link_libraries(${BINARY_NAME} PRIVATE \"dwmapi.lib\")\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n"
  },
  {
    "path": "windows/runner/Runner.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE\nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE\nBEGIN\n    \"#include \"\"winres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE\nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_APP_ICON            ICON                    \"resources\\\\app_icon.ico\"\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)\n#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD\n#else\n#define VERSION_AS_NUMBER 1,0,0,0\n#endif\n\n#if defined(FLUTTER_VERSION)\n#define VERSION_AS_STRING FLUTTER_VERSION\n#else\n#define VERSION_AS_STRING \"1.0.0\"\n#endif\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION VERSION_AS_NUMBER\n PRODUCTVERSION VERSION_AS_NUMBER\n FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#ifdef _DEBUG\n FILEFLAGS VS_FF_DEBUG\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS VOS__WINDOWS32\n FILETYPE VFT_APP\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904e4\"\n        BEGIN\n            VALUE \"CompanyName\", \"com.pichillilorenzo\" \"\\0\"\n            VALUE \"FileDescription\", \"flutter_browser_app\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"flutter_browser_app\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2024 com.pichillilorenzo. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"flutter_browser_app.exe\" \"\\0\"\n            VALUE \"ProductName\", \"flutter_browser_app\" \"\\0\"\n            VALUE \"ProductVersion\", VERSION_AS_STRING \"\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1252\n    END\nEND\n\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n"
  },
  {
    "path": "windows/runner/flutter_window.cpp",
    "content": "#include \"flutter_window.h\"\n\n#include <optional>\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nFlutterWindow::FlutterWindow(const flutter::DartProject& project)\n    : project_(project) {}\n\nFlutterWindow::~FlutterWindow() {}\n\nbool FlutterWindow::OnCreate() {\n  if (!Win32Window::OnCreate()) {\n    return false;\n  }\n\n  RECT frame = GetClientArea();\n\n  // The size here must match the window dimensions to avoid unnecessary surface\n  // creation / destruction in the startup path.\n  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(\n      frame.right - frame.left, frame.bottom - frame.top, project_);\n  // Ensure that basic setup of the controller was successful.\n  if (!flutter_controller_->engine() || !flutter_controller_->view()) {\n    return false;\n  }\n  RegisterPlugins(flutter_controller_->engine());\n  SetChildContent(flutter_controller_->view()->GetNativeWindow());\n\n  flutter_controller_->engine()->SetNextFrameCallback([&]() {\n    // this->Show();\n  });\n\n  // Flutter can complete the first frame before the \"show window\" callback is\n  // registered. The following call ensures a frame is pending to ensure the\n  // window is shown. It is a no-op if the first frame hasn't completed yet.\n  flutter_controller_->ForceRedraw();\n\n  return true;\n}\n\nvoid FlutterWindow::OnDestroy() {\n  if (flutter_controller_) {\n    flutter_controller_ = nullptr;\n  }\n\n  Win32Window::OnDestroy();\n}\n\nLRESULT\nFlutterWindow::MessageHandler(HWND hwnd, UINT const message,\n                              WPARAM const wparam,\n                              LPARAM const lparam) noexcept {\n  // Give Flutter, including plugins, an opportunity to handle window messages.\n  if (flutter_controller_) {\n    std::optional<LRESULT> result =\n        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,\n                                                      lparam);\n    if (result) {\n      return *result;\n    }\n  }\n\n  switch (message) {\n    case WM_FONTCHANGE:\n      flutter_controller_->engine()->ReloadSystemFonts();\n      break;\n  }\n\n  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);\n}\n"
  },
  {
    "path": "windows/runner/flutter_window.h",
    "content": "#ifndef RUNNER_FLUTTER_WINDOW_H_\n#define RUNNER_FLUTTER_WINDOW_H_\n\n#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n\n#include <memory>\n\n#include \"win32_window.h\"\n\n// A window that does nothing but host a Flutter view.\nclass FlutterWindow : public Win32Window {\n public:\n  // Creates a new FlutterWindow hosting a Flutter view running |project|.\n  explicit FlutterWindow(const flutter::DartProject& project);\n  virtual ~FlutterWindow();\n\n protected:\n  // Win32Window:\n  bool OnCreate() override;\n  void OnDestroy() override;\n  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,\n                         LPARAM const lparam) noexcept override;\n\n private:\n  // The project to run.\n  flutter::DartProject project_;\n\n  // The Flutter instance hosted by this window.\n  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;\n};\n\n#endif  // RUNNER_FLUTTER_WINDOW_H_\n"
  },
  {
    "path": "windows/runner/main.cpp",
    "content": "#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n#include <windows.h>\n\n#include \"flutter_window.h\"\n#include \"utils.h\"\n\n#include <iostream>\n#include \"window_manager_plus/window_manager_plus_plugin.h\"\n\nint APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,\n                      _In_ wchar_t *command_line, _In_ int show_command) {\n  // Attach to console when present (e.g., 'flutter run') or create a\n  // new console when running with a debugger.\n  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {\n    CreateAndAttachConsole();\n  }\n\n  // Initialize COM, so that it is available for use in the library and/or\n  // plugins.\n  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);\n\n  flutter::DartProject project(L\"data\");\n\n  std::vector<std::string> command_line_arguments =\n      GetCommandLineArguments();\n\n  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));\n\n  FlutterWindow window(project);\n  Win32Window::Point origin(10, 10);\n  Win32Window::Size size(1280, 720);\n  if (!window.Create(L\"flutter_browser_app\", origin, size)) {\n    return EXIT_FAILURE;\n  }\n  window.SetQuitOnClose(false);\n\n  WindowManagerPlusPluginSetWindowCreatedCallback(\n    [](std::vector<std::string> command_line_arguments) {\n      flutter::DartProject project(L\"data\");\n\n      project.set_dart_entrypoint_arguments(\n          std::move(command_line_arguments));\n\n      auto window = std::make_shared<FlutterWindow>(project);\n      Win32Window::Point origin(10, 10);\n      Win32Window::Size size(1280, 720);\n      if (!window->Create(L\"flutter_browser_app\", origin, size)) {\n        std::cerr << \"Failed to create a new window\" << std::endl;\n      }\n      window->SetQuitOnClose(false);\n      return std::move(window);\n    });\n\n  ::MSG msg;\n  while (::GetMessage(&msg, nullptr, 0, 0)) {\n    ::TranslateMessage(&msg);\n    ::DispatchMessage(&msg);\n  }\n\n  ::CoUninitialize();\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "windows/runner/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Runner.rc\n//\n#define IDI_APP_ICON                    101\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "windows/runner/runner.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n    </windowsSettings>\n  </application>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 10 and Windows 11 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n    </application>\n  </compatibility>\n</assembly>\n"
  },
  {
    "path": "windows/runner/utils.cpp",
    "content": "#include \"utils.h\"\n\n#include <flutter_windows.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include <iostream>\n\nvoid CreateAndAttachConsole() {\n  if (::AllocConsole()) {\n    FILE *unused;\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stdout)) {\n      _dup2(_fileno(stdout), 1);\n    }\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stderr)) {\n      _dup2(_fileno(stdout), 2);\n    }\n    std::ios::sync_with_stdio();\n    FlutterDesktopResyncOutputStreams();\n  }\n}\n\nstd::vector<std::string> GetCommandLineArguments() {\n  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.\n  int argc;\n  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);\n  if (argv == nullptr) {\n    return std::vector<std::string>();\n  }\n\n  std::vector<std::string> command_line_arguments;\n\n  // Skip the first argument as it's the binary name.\n  for (int i = 1; i < argc; i++) {\n    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));\n  }\n\n  ::LocalFree(argv);\n\n  return command_line_arguments;\n}\n\nstd::string Utf8FromUtf16(const wchar_t* utf16_string) {\n  if (utf16_string == nullptr) {\n    return std::string();\n  }\n  unsigned int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr)\n    -1; // remove the trailing null character\n  int input_length = (int)wcslen(utf16_string);\n  std::string utf8_string;\n  if (target_length == 0 || target_length > utf8_string.max_size()) {\n    return utf8_string;\n  }\n  utf8_string.resize(target_length);\n  int converted_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      input_length, utf8_string.data(), target_length, nullptr, nullptr);\n  if (converted_length == 0) {\n    return std::string();\n  }\n  return utf8_string;\n}\n"
  },
  {
    "path": "windows/runner/utils.h",
    "content": "#ifndef RUNNER_UTILS_H_\n#define RUNNER_UTILS_H_\n\n#include <string>\n#include <vector>\n\n// Creates a console for the process, and redirects stdout and stderr to\n// it for both the runner and the Flutter library.\nvoid CreateAndAttachConsole();\n\n// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string\n// encoded in UTF-8. Returns an empty std::string on failure.\nstd::string Utf8FromUtf16(const wchar_t* utf16_string);\n\n// Gets the command line arguments passed in as a std::vector<std::string>,\n// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.\nstd::vector<std::string> GetCommandLineArguments();\n\n#endif  // RUNNER_UTILS_H_\n"
  },
  {
    "path": "windows/runner/win32_window.cpp",
    "content": "#include \"win32_window.h\"\n\n#include <dwmapi.h>\n#include <flutter_windows.h>\n\n#include \"resource.h\"\n\nnamespace {\n\n/// Window attribute that enables dark mode window decorations.\n///\n/// Redefined in case the developer's machine has a Windows SDK older than\n/// version 10.0.22000.0.\n/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute\n#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE\n#define DWMWA_USE_IMMERSIVE_DARK_MODE 20\n#endif\n\nconstexpr const wchar_t kWindowClassName[] = L\"FLUTTER_RUNNER_WIN32_WINDOW\";\n\n/// Registry key for app theme preference.\n///\n/// A value of 0 indicates apps should use dark mode. A non-zero or missing\n/// value indicates apps should use light mode.\nconstexpr const wchar_t kGetPreferredBrightnessRegKey[] =\n  L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Themes\\\\Personalize\";\nconstexpr const wchar_t kGetPreferredBrightnessRegValue[] = L\"AppsUseLightTheme\";\n\n// The number of Win32Window objects that currently exist.\nstatic int g_active_window_count = 0;\n\nusing EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);\n\n// Scale helper to convert logical scaler values to physical using passed in\n// scale factor\nint Scale(int source, double scale_factor) {\n  return static_cast<int>(source * scale_factor);\n}\n\n// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.\n// This API is only needed for PerMonitor V1 awareness mode.\nvoid EnableFullDpiSupportIfAvailable(HWND hwnd) {\n  HMODULE user32_module = LoadLibraryA(\"User32.dll\");\n  if (!user32_module) {\n    return;\n  }\n  auto enable_non_client_dpi_scaling =\n      reinterpret_cast<EnableNonClientDpiScaling*>(\n          GetProcAddress(user32_module, \"EnableNonClientDpiScaling\"));\n  if (enable_non_client_dpi_scaling != nullptr) {\n    enable_non_client_dpi_scaling(hwnd);\n  }\n  FreeLibrary(user32_module);\n}\n\n}  // namespace\n\n// Manages the Win32Window's window class registration.\nclass WindowClassRegistrar {\n public:\n  ~WindowClassRegistrar() = default;\n\n  // Returns the singleton registrar instance.\n  static WindowClassRegistrar* GetInstance() {\n    if (!instance_) {\n      instance_ = new WindowClassRegistrar();\n    }\n    return instance_;\n  }\n\n  // Returns the name of the window class, registering the class if it hasn't\n  // previously been registered.\n  const wchar_t* GetWindowClass();\n\n  // Unregisters the window class. Should only be called if there are no\n  // instances of the window.\n  void UnregisterWindowClass();\n\n private:\n  WindowClassRegistrar() = default;\n\n  static WindowClassRegistrar* instance_;\n\n  bool class_registered_ = false;\n};\n\nWindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;\n\nconst wchar_t* WindowClassRegistrar::GetWindowClass() {\n  if (!class_registered_) {\n    WNDCLASS window_class{};\n    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);\n    window_class.lpszClassName = kWindowClassName;\n    window_class.style = CS_HREDRAW | CS_VREDRAW;\n    window_class.cbClsExtra = 0;\n    window_class.cbWndExtra = 0;\n    window_class.hInstance = GetModuleHandle(nullptr);\n    window_class.hIcon =\n        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));\n    window_class.hbrBackground = 0;\n    window_class.lpszMenuName = nullptr;\n    window_class.lpfnWndProc = Win32Window::WndProc;\n    RegisterClass(&window_class);\n    class_registered_ = true;\n  }\n  return kWindowClassName;\n}\n\nvoid WindowClassRegistrar::UnregisterWindowClass() {\n  UnregisterClass(kWindowClassName, nullptr);\n  class_registered_ = false;\n}\n\nWin32Window::Win32Window() {\n  ++g_active_window_count;\n}\n\nWin32Window::~Win32Window() {\n  --g_active_window_count;\n  Destroy();\n}\n\nbool Win32Window::Create(const std::wstring& title,\n                         const Point& origin,\n                         const Size& size) {\n  Destroy();\n\n  const wchar_t* window_class =\n      WindowClassRegistrar::GetInstance()->GetWindowClass();\n\n  const POINT target_point = {static_cast<LONG>(origin.x),\n                              static_cast<LONG>(origin.y)};\n  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);\n  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);\n  double scale_factor = dpi / 96.0;\n\n  HWND window = CreateWindow(\n      window_class, title.c_str(), WS_OVERLAPPEDWINDOW,\n      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),\n      Scale(size.width, scale_factor), Scale(size.height, scale_factor),\n      nullptr, nullptr, GetModuleHandle(nullptr), this);\n\n  if (!window) {\n    return false;\n  }\n\n  UpdateTheme(window);\n\n  return OnCreate();\n}\n\nbool Win32Window::Show() {\n  return ShowWindow(window_handle_, SW_SHOWNORMAL);\n}\n\n// static\nLRESULT CALLBACK Win32Window::WndProc(HWND const window,\n                                      UINT const message,\n                                      WPARAM const wparam,\n                                      LPARAM const lparam) noexcept {\n  if (message == WM_NCCREATE) {\n    auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);\n    SetWindowLongPtr(window, GWLP_USERDATA,\n                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));\n\n    auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);\n    EnableFullDpiSupportIfAvailable(window);\n    that->window_handle_ = window;\n  } else if (Win32Window* that = GetThisFromHandle(window)) {\n    return that->MessageHandler(window, message, wparam, lparam);\n  }\n\n  return DefWindowProc(window, message, wparam, lparam);\n}\n\nLRESULT\nWin32Window::MessageHandler(HWND hwnd,\n                            UINT const message,\n                            WPARAM const wparam,\n                            LPARAM const lparam) noexcept {\n  switch (message) {\n    case WM_DESTROY:\n      window_handle_ = nullptr;\n      Destroy();\n      if (quit_on_close_) {\n        PostQuitMessage(0);\n      }\n      return 0;\n\n    case WM_DPICHANGED: {\n      auto newRectSize = reinterpret_cast<RECT*>(lparam);\n      LONG newWidth = newRectSize->right - newRectSize->left;\n      LONG newHeight = newRectSize->bottom - newRectSize->top;\n\n      SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,\n                   newHeight, SWP_NOZORDER | SWP_NOACTIVATE);\n\n      return 0;\n    }\n    case WM_SIZE: {\n      RECT rect = GetClientArea();\n      if (child_content_ != nullptr) {\n        // Size and position the child window.\n        MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,\n                   rect.bottom - rect.top, TRUE);\n      }\n      return 0;\n    }\n\n    case WM_ACTIVATE:\n      if (child_content_ != nullptr) {\n        SetFocus(child_content_);\n      }\n      return 0;\n\n    case WM_DWMCOLORIZATIONCOLORCHANGED:\n      UpdateTheme(hwnd);\n      return 0;\n  }\n\n  return DefWindowProc(window_handle_, message, wparam, lparam);\n}\n\nvoid Win32Window::Destroy() {\n  OnDestroy();\n\n  if (window_handle_) {\n    DestroyWindow(window_handle_);\n    window_handle_ = nullptr;\n  }\n  if (g_active_window_count == 0) {\n    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();\n  }\n}\n\nWin32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {\n  return reinterpret_cast<Win32Window*>(\n      GetWindowLongPtr(window, GWLP_USERDATA));\n}\n\nvoid Win32Window::SetChildContent(HWND content) {\n  child_content_ = content;\n  SetParent(content, window_handle_);\n  RECT frame = GetClientArea();\n\n  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,\n             frame.bottom - frame.top, true);\n\n  SetFocus(child_content_);\n}\n\nRECT Win32Window::GetClientArea() {\n  RECT frame;\n  GetClientRect(window_handle_, &frame);\n  return frame;\n}\n\nHWND Win32Window::GetHandle() {\n  return window_handle_;\n}\n\nvoid Win32Window::SetQuitOnClose(bool quit_on_close) {\n  quit_on_close_ = quit_on_close;\n}\n\nbool Win32Window::OnCreate() {\n  // No-op; provided for subclasses.\n  return true;\n}\n\nvoid Win32Window::OnDestroy() {\n  // No-op; provided for subclasses.\n}\n\nvoid Win32Window::UpdateTheme(HWND const window) {\n  DWORD light_mode;\n  DWORD light_mode_size = sizeof(light_mode);\n  LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,\n                               kGetPreferredBrightnessRegValue,\n                               RRF_RT_REG_DWORD, nullptr, &light_mode,\n                               &light_mode_size);\n\n  if (result == ERROR_SUCCESS) {\n    BOOL enable_dark_mode = light_mode == 0;\n    DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,\n                          &enable_dark_mode, sizeof(enable_dark_mode));\n  }\n}\n"
  },
  {
    "path": "windows/runner/win32_window.h",
    "content": "#ifndef RUNNER_WIN32_WINDOW_H_\n#define RUNNER_WIN32_WINDOW_H_\n\n#include <windows.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n\n// A class abstraction for a high DPI-aware Win32 Window. Intended to be\n// inherited from by classes that wish to specialize with custom\n// rendering and input handling\nclass Win32Window {\n public:\n  struct Point {\n    unsigned int x;\n    unsigned int y;\n    Point(unsigned int x, unsigned int y) : x(x), y(y) {}\n  };\n\n  struct Size {\n    unsigned int width;\n    unsigned int height;\n    Size(unsigned int width, unsigned int height)\n        : width(width), height(height) {}\n  };\n\n  Win32Window();\n  virtual ~Win32Window();\n\n  // Creates a win32 window with |title| that is positioned and sized using\n  // |origin| and |size|. New windows are created on the default monitor. Window\n  // sizes are specified to the OS in physical pixels, hence to ensure a\n  // consistent size this function will scale the inputted width and height as\n  // as appropriate for the default monitor. The window is invisible until\n  // |Show| is called. Returns true if the window was created successfully.\n  bool Create(const std::wstring& title, const Point& origin, const Size& size);\n\n  // Show the current window. Returns true if the window was successfully shown.\n  bool Show();\n\n  // Release OS resources associated with window.\n  void Destroy();\n\n  // Inserts |content| into the window tree.\n  void SetChildContent(HWND content);\n\n  // Returns the backing Window handle to enable clients to set icon and other\n  // window properties. Returns nullptr if the window has been destroyed.\n  HWND GetHandle();\n\n  // If true, closing this window will quit the application.\n  void SetQuitOnClose(bool quit_on_close);\n\n  // Return a RECT representing the bounds of the current client area.\n  RECT GetClientArea();\n\n protected:\n  // Processes and route salient window messages for mouse handling,\n  // size change and DPI. Delegates handling of these to member overloads that\n  // inheriting classes can handle.\n  virtual LRESULT MessageHandler(HWND window,\n                                 UINT const message,\n                                 WPARAM const wparam,\n                                 LPARAM const lparam) noexcept;\n\n  // Called when CreateAndShow is called, allowing subclass window-related\n  // setup. Subclasses should return false if setup fails.\n  virtual bool OnCreate();\n\n  // Called when Destroy is called.\n  virtual void OnDestroy();\n\n private:\n  friend class WindowClassRegistrar;\n\n  // OS callback called by message pump. Handles the WM_NCCREATE message which\n  // is passed when the non-client area is being created and enables automatic\n  // non-client DPI scaling so that the non-client area automatically\n  // responds to changes in DPI. All other messages are handled by\n  // MessageHandler.\n  static LRESULT CALLBACK WndProc(HWND const window,\n                                  UINT const message,\n                                  WPARAM const wparam,\n                                  LPARAM const lparam) noexcept;\n\n  // Retrieves a class instance pointer for |window|\n  static Win32Window* GetThisFromHandle(HWND const window) noexcept;\n\n  // Update the window frame's theme to match the system theme.\n  static void UpdateTheme(HWND const window);\n\n  bool quit_on_close_ = false;\n\n  // window handle for top level window.\n  HWND window_handle_ = nullptr;\n\n  // window handle for hosted content.\n  HWND child_content_ = nullptr;\n};\n\n#endif  // RUNNER_WIN32_WINDOW_H_\n"
  }
]