Lawrence  Lesch

Lawrence Lesch

1676317440

Webview_deno: Deno Bindings for Webview

Webview_deno

deno bindings for webview

Webview is a tiny cross-platform library to make web-based GUIs for desktop applications.


⚠️ This project is still in development. Expect breaking changes.


Example Image

Example

import { Webview } from "https://deno.land/x/webview/mod.ts";

const html = `
  <html>
  <body>
    <h1>Hello from deno v${Deno.version.deno}</h1>
  </body>
  </html>
`;

const webview = new Webview();

webview.navigate(`data:text/html,${encodeURIComponent(html)}`);
webview.run();

You can run this example directly from the web:

deno run -Ar --unstable https://deno.land/x/webview/examples/local.ts

or in your development environment:

deno run -Ar --unstable examples/local.ts

you can find other examples in the examples/ directory.

Documentation

You can find the official documentation here.

Development

Prerequisites

Linux

  • webkit2gtk (to install using apt: sudo apt-get install libwebkit2gtk-4.0-dev)

Building

Make sure to init the webview submodule with:

$ git submodule update --init --recursive

Building on Windows requires admin privileges.

$ deno task build

Running

To run webview_deno without automatically downloading the binaries from releases you will need to use the environment variable PLUGIN_URL and set it to the path where the built binaries are located. This is usually file://./target/release.

$ deno task build
$ PLUGIN_URL=./build/
$ deno run --unstable -A examples/local.ts

or

$ deno task run examples/local.ts

or if you have the webview library already built and didn't make any changes to it, you can skip the building step with:

$ deno task run:fast examples/local.ts

Environment variables

  • PLUGIN_URL - Set a custom library URL. Defaults to the latest release assets on Github. Setting this also disables cache for plug.

Dependencies

Other

Contribution

Pull request, issues and feedback are very welcome. Code style is formatted with deno task fmt, linted with deno task lint and commit messages are done following Conventional Commits spec.

Download Details:

Author: Webview
Source Code: https://github.com/webview/webview_deno 
License: MIT license

#typescript #gui #webview #hacktoberfest #deno 

Webview_deno: Deno Bindings for Webview
Rupert  Beatty

Rupert Beatty

1672421100

Multi: Create A Custom, Lightweight MacOS App From A Group Of Websites

Multi

Create a custom, lightweight macOS app from a group of websites, complete with:

  • Native notifications, file uploads, and dialogs
  • Ad-blocking, provided by better.fyi
  • Customization options with JSON, CSS, and JavaScript

Watch me create a Slack clone from scratch in 30 seconds (high res video):

Demo GIF

I've also written a few blog posts that discuss some of the decisions behind Multi:

Installation

The easiest method is to use Homebrew:

brew install --cask multi

Alternatively, you can manually download and run the latest .dmg from Releases. If you are on macOS 10.13 High Sierra (the minimum supported version), you'll also need to install the Swift runtime from Apple.

JSON configuration

Multi apps store their configuration in a single JSON file. If your app is named Test, then you'll find that file at /Applications/Multi/Test.app/Contents/Resources/config.json. The JSON configuration uses the following top-level fields:

Field NameTypeDescription
tabsArray (Required)Titles and URLs of tabs for this app
windowedBoolean (Optional, default false)Start the app with each tab in its own window
alwaysNotifyBoolean (Optional, default false)Show macOS notifications even if your app is currently focused
openNewWindowsWithString (Optional, macOS 10.15+)Override system default browser for external links — value is a bundle identifier like com.apple.Safari, com.google.Chrome, or com.mozilla.firefox
openNewWindowsInBackgroundBoolean (Optional, default false, macOS 10.15+)Determines if browser app becomes active when opening external links

The tabs field is an array of objects with the following fields:

Field NameTypeDescription
titleString (Required)Whatever you want to call this tab
urlString (Required)Starting page for this tab
customJsArray of Strings (Optional)Custom JS URLs (see Custom JS/CSS)
customCssArray of Strings (Optional)Custom CSS URLs (see Custom JS/CSS)
basicAuthUserString (Optional)User name credential for requests that use basic access authentication
basicAuthPasswordString (Optional)Password credential for requests that use basic access authentication
userAgentString (Optional)Override the default WebKit user agent header

Here's the bare minimum example used in the Slack demo video above:

{ "tabs": [{ "title": "Slack Lite", "url": "https://app.slack.com/client" }] }

Here's a fancier example that uses the optional fields referenced above:

{
  "tabs": [
    {
      "title": "Dancing",
      "url": "https://rc.kofi.sexy/bathroom-floss",
      "basicAuthUser": "user",
      "basicAuthPassword": "password",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"
    },
    {
      "title": "Walking",
      "url": "https://kofi.sexy/cel-shading",
      "customJs": [ "https://raw.githubusercontent.com/kofigumbs/multi/2.x/Assets/test.js" ],
      "customCss": [ "https://raw.githubusercontent.com/kofigumbs/multi/2.x/Assets/test.css" ]
    }
  ],
  "windowed": true,
  "alwaysNotify": true,
  "openNewWindowsWith": "com.apple.Safari",
  "openNewWindowsInBackground": true
}

If your configuration file fails to decode for any reason, your Multi app will open to the preferences window, where you can fix any issues.

User-agent tip

Before v2.2.0, Multi included a hard-coded user-agent that made it appear like Safari. This behavior caused subtle issues and confusion when the hard-coded user-agent didn't reflect the system WebKit version. Recent Multi versions remove the hard-coded user-agent, but now sites like Slack and WhatsApp complain that your browser is out of date. (Ideally these sites would use feature detection instead of user-agent sniffing to gracefully degrade behavior; alas, the world does not work ideally.)

If your site doesn't work because it thinks you're using an outdated browser, try setting the userAgent config to match your Safari version.

Using the CLI: create-mac-app

You can create and update Multi apps entirely from the command-line with the included script. In fact, the Multi configuration UI just runs this script under-the-hood! The create-mac-app script takes its options as environment variables. For instance, here's how you'd create a bare-minimum app named Test:

MULTI_APP_NAME='Test' /Applications/Multi.app/Contents/Resources/create-mac-app

When you open Test, you'll be greeted with the preferences window, where you can finish configuring your app. If you'd like to configure your app entirely from the command-line, you can set any of the following variables:

  
MULTI_ICON_PATHPNG or ICNS path to icon image
MULTI_JSON_CONFIGSee JSON configuration
MULTI_OVERWRITESet to 1 to replace an existing Multi app with the same name

Custom JS/CSS

Multi lets you customize any site by injecting JavaScript and CSS on every page in your app. Each custom JS/CSS file is specified with a URL, which gives you a few options for how you want to manage your customizations:

  1. Host your file online, and use its URL: ex. https://raw.githubusercontent.com/kofigumbs/dotfiles/master/example.js
  2. Reference a local file on your computer: ex. file:///Users/kofi/workspace/dotfiles/example.js
  3. Encode your script directly in the JSON using Data URIs: ex. data:,console.log%28%27Hello%2C%20from%20Multi%21%27%29%3B%0A

Custom JS/CSS is one of the most important parts of Multi. It lets the main project stay small and focused, while letting you extend it with new features that fit your use case. If you have a neat JS/CSS snippet, you'd like to share, please open an Issue or Pull Request! Here are a few that have come up before:

Fix links in GMail and Google Calendar

Google seems to be doing some trickery here. Instead of allowing the browser to handle the links, they use JS to open a blank popup window, then dynamically set the URL to google.com/url?q=REAL_URL_HERE. Presumably all of this is so that they can track you for a few moments on your way out of their app. Custom JS solution:

(() => {
  const listener = e => e.stopPropagation();
  const query = () => document.querySelectorAll('a[target=_blank]').forEach(a => {
    a.removeEventListener('click', listener);
    a.addEventListener('click', listener, true);
  });
  query();
  setInterval(query, 400); // wait time between DOM queries, in milliseconds
})();

Reload Slack when it disconnects

Sometimes Slack's WebSocket disconnects and stops loading new messages. It seems like this is either an issue with WebKit or Slack.com. Custom JS solution:

setInterval(() => {
  if (document.body.innerText.includes('Load new messages.'))
    window.location.reload();
}, 90000);

Find in page

Multi doesn't include any search functionality (Cmd-F). Custom JS solution:

(() => {
  const highlightResults = (text, color) => {
    document.designMode = "on"; // https://stackoverflow.com/a/5887719
    var selection = window.getSelection();
    selection.collapse(document.body, 0);
    while (window.find(text)) {
      document.execCommand("HiliteColor", false, color);
      selection.collapseToEnd();
    }
    document.designMode = "off";
  };

  let mostRecentSearchText = "";
  const search = text => {
    highlightResults(mostRecentSearchText, "transparent");
    highlightResults(text, "rgb(255 255 1 / 50%)");
    mostRecentSearchText = text;
  };

  const input = document.createElement("input");
  input.placeholder = "Search...";
  input.style.padding = "10px 15px";
  input.style.fontSize = "15px";
  input.style.borderRadius = "3px";
  input.style.border = "solid 1px lightgray";

  const form = document.createElement("form");
  form.style.display = "none";
  form.style.position = "fixed";
  form.style.top = "15px";
  form.style.right = "15px";
  form.style.zIndex = "2147483647"; // https://stackoverflow.com/a/856569
  form.addEventListener("submit", e => {
    e.preventDefault();
    search(input.value);
  });

  const close = document.createElement("a");
  close.innerText = "⨯";
  close.href = "javascript:void(0)";
  close.style.fontSize = "30px";
  close.style.padding = "15px";
  close.style.textDecoration = "none";
  close.addEventListener("click", e => {
    e.preventDefault();
    search("");
    form.style.display = "none";
  });

  form.appendChild(input);
  form.appendChild(close);
  document.body.appendChild(form);

  document.addEventListener("keydown", event => {
    if (event.metaKey && event.key === "f") {
      event.preventDefault();
      form.style.display = "block";
      input.focus();
    }
  });
})();

Drag-and-drop to open URLs

Sometimes you have a URL outside of Multi (maybe in an email), and you want to open it in Multi. Custom JS solution:

document.addEventListener("dragover", e => e.preventDefault());

Preview link targets

Multi doesn't include any hover-to-preview-link-target functionality. Custom CSS solution:

a:hover::after {
  content: attr(href);
  position: fixed;
  left: 4px;
  bottom: 4px;
  padding: 4px;
  font-size: 12px;
  font-family: -apple-system, BlinkMacSystemFont;
  font-weight: normal;
  color: black;
  background: ghostwhite;
  border: solid 1px black;
  border-radius: 1px;
}

Keyboard shortcuts

Multi's shortcuts should basically match those of other macOS apps:

     
⌘XCut ⌘[Back
⌘CCopy ⌘]Forward
⌘VPaste ⌘RReload This Page
⌘↑VPaste and Match Style ⌘+/⌘-/⌘0Zoom in/out/default
⌘ASelect All ^TabSelect Next Tab
⌘^FToggle Full Screen ^↑TabSelect Previous Tab
⌘MMinimize ⌘1 - ⌘9Select Tab
⌘HHide ⌘LCopy current URL
⌘WClose Tab ⌘↑TToggle Tab Bar
⌘QQuit ⌘↑\Toggle Tab Overview

Download Details:

Author: Kofigumbs
Source Code: https://github.com/kofigumbs/multi 
License: GPL-3.0 license

#swift #macos #cli #webview 

Multi: Create A Custom, Lightweight MacOS App From A Group Of Websites
Rupert  Beatty

Rupert Beatty

1667310793

An Easy Way to Use Pull To Refresh and infinite Scrolling in Swift

ESPullToRefresh is an easy-to-use component that give pull-to-refresh and infinite-scrolling implemention for developers. By extension to UIScrollView, you can easily add pull-to-refresh and infinite-scrolling for any subclass of UIScrollView. If you want to customize its UI style, you just need conform the specified protocol.

中文介绍

Requirements

  • Xcode 8 or later
  • iOS 8.0 or later
  • ARC
  • Swift 5.0 or later

Features

  • Support UIScrollView and its subclasses UICollectionView UITableView UITextView
  • Pull-Down to refresh and Pull-Up to load more
  • Support customize your own style(s)

Demo

Download and run the ESPullToRefreshExample project in Xcode to see ESPullToRefresh in action.

Installation

CocoaPods

pod "ESPullToRefresh"

Carthage

github "eggswift/pull-to-refresh"

Manually

git clone https://github.com/eggswift/pull-to-refresh.git
open ESPullToRefresh

Usage

Default style:

example_default.gif

Add ESPullToRefresh to your project

import ESPullToRefresh

Add default pull-to-refresh

self.tableView.es.addPullToRefresh {
    [unowned self] in
    /// Do anything you want...
    /// ...
    /// Stop refresh when your job finished, it will reset refresh footer if completion is true
    self.tableView.es.stopPullToRefresh(completion: true)
    /// Set ignore footer or not
    self.tableView.es.stopPullToRefresh(completion: true, ignoreFooter: false)
}

Add default infinite-scrolling

self.tableView.es.addInfiniteScrolling {
    [unowned self] in
    /// Do anything you want...
    /// ...
    /// If common end
    self.tableView.es.stopLoadingMore()
    /// If no more data
    self.tableView.es.noticeNoMoreData()
}

Customize Style

As effect:

example_meituan.gif

PS: Load effect is from MeiTuan iOS app.

example_wechat.gif

Customize refresh need conform the ESRefreshProtocol and ESRefreshAnimatorProtocol protocol.

Add customize pull-to-refresh

func es.addPullToRefresh(animator animator: protocol<ESRefreshProtocol, ESRefreshAnimatorProtocol>, handler: ESRefreshHandler)

Add customize infinite-scrolling

func es.addInfiniteScrolling(animator animator: protocol<ESRefreshProtocol, ESRefreshAnimatorProtocol>, handler: ESRefreshHandler)

Espried and auto refresh

ESPullToRefresh support for the latest expiration time and the cache refresh time, You need set an refreshIdentifier to your UIScrollView.

scrollView.refreshIdentifier = "Your Identifier" // Set refresh identifier
scrollView.expriedTimeInterval = 20.0 // Set the expiration interval

You can use es.autoPullToRefresh() method, when the time over the last refresh interval expires automatically refreshed.

scrollView.es.autoPullToRefresh()

let expried = scrollView.espried // expired or not

Remove

func es.removeRefreshHeader()
func es.removeRefreshFooter()

Sponsor

You can support the project by checking out our sponsor page. It takes only one click:

git-adThis advert was placed by GitAds

Download Details:

Author: Eggswift
Source Code: https://github.com/eggswift/pull-to-refresh 
License: MIT license

#swift #webview #infinite #collectionview 

An Easy Way to Use Pull To Refresh and infinite Scrolling in Swift

A Webview Implementation of Dojah Financial

Features [KYC AND FINANCIAL WIDGET]

Installation

First, add flutter_dojah_financial as a dependency in your pubspec.yaml file.

iOS

Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor.

NSMicrophoneUsageDescription - describe why your app needs access to the microphone, if you intend to record videos. This is called Privacy - Microphone Usage Description in the visual editor.

NSLocationWhenInUseUsageDescription - describe why your app needs access to the location, if you intend to verify address/location. This is called Privacy - Location Usage Description in the visual editor.

Podfile

Kindly include this in Podfile set up.

dart: PermissionGroup.camera PERMISSION_CAMERA=1,

dart: PermissionGroup.microphone PERMISSION_MICROPHONE=1,

dart: PermissionGroup.location PERMISSION_LOCATION=1,

Android

// Add the camera permission: 
<uses-permission android:name="android.permission.CAMERA" />
// Add the modify audio settings permission:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
// Add the Internet settings permission:
<uses-permission android:name="android.permission.INTERNET"/>
// Add the Location settings permission :
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

Usage

TODO: Include short and useful examples for package users. Add longer examples to /example folder.

final Map<String,dynamic> config = {
  debug: true,
  otp: true, //for verification type
  selfie: true //for verification type
};
 final DojahFinancial _dojahFinancial = DojahFinancial(
    appId: 'xxxxxxxxxxxxxxx',
    publicKey: 'prod_pk_xxxxxxxxxxxxxx',
    type: 'liveness'  //link, identification, verification, payment
    config: config
    referenceId: referenceId,
  );

  _dojahFinancial.open(context, onSuccess: (result) {
    print('$result');
  }, 
  onClose: (close) => print('Widget Closed'),
  onError: (err) {
    print('error: $err');
  });

Deployment

REMEMBER TO CHANGE THE APP ID and PUBLIC KEY WHEN DEPLOYING TO A LIVE (PRODUCTION) ENVIRONMENT

Contributing

  • Fork it!
  • Create your feature branch: git checkout -b feature/feature-name
  • Commit your changes: git commit -am 'Some commit message'
  • Push to the branch: git push origin feature/feature-name
  • Submit a pull request 😉😉

Additional information

Contact Dojah for more options for the config object.

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add flutter_dojah_financial

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  flutter_dojah_financial: ^0.1.5

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:flutter_dojah_financial/flutter_dojah_financial.dart'; 

Download Details:

Author: dojah-inc

Source Code: https://github.com/dojah-inc/flutter-financial

#flutter #webview 

A Webview Implementation of Dojah Financial

An Extension of Webview_flutter to Load HTML,CSS and Javascript

webview_flutter_plus

Contents

About

webview_flutter_plus is a powerful extension of webview_flutter. This package helps to load Local HTML, CSS and Javascript content from Assets or Strings. This inherits all features of webview_flutter with minor API changes.

Do check flutter_tex a powerful implementation of this package.

What's unique in webview_flutter_plus

  • Load HTML, CSS and Javascript content from Assets, see example.
  • Load HTML, CSS and Javascript content from Strings, see example.
  • Get height of Web content which will allow you to use WebviewPlus widget even in list view, see example.
  • It includes all features of its parent plugin webview_flutter.

How to use?

1: Add this to your package's pubspec.yaml file:

dependencies:
  webview_flutter_plus: ^0.3.0+2

2: You can install packages from the command line:

$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3: Now you need to put the following implementations in Android and iOS respectively.

Android

Make sure to add this line android:usesCleartextTraffic="true" in your <project-directory>/android/app/src/main/AndroidManifest.xml under application like this.

<application
       android:usesCleartextTraffic="true">
</application>

Required Permissions are:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

iOS

Add following code in your <project-directory>/ios/Runner/Info.plist

<key>NSAppTransportSecurity</key>
  <dict>
    <key>NSAllowsArbitraryLoads</key> <true/>
  </dict>
<key>io.flutter.embedded_views_preview</key> <true/> 

4: Now in your Dart code, you can use:

import 'package:webview_flutter_plus/webview_flutter_plus.dart'; 

5: Now you can use WebViewPlus as a widget:

Examples

Loading From String

WebViewPlus(
    javascriptMode: JavascriptMode.unrestricted,
    onWebViewCreated: (controller) {
      controller.loadString(r"""
           <html lang="en">
            <body>hello world</body>
           </html>
      """);
    },
  )

Loading from Assets

It is mandatory to mention all associated HTML, CSS and Javascript files in pubspecs.yaml under assets:

WebViewPlus(
    javascriptMode: JavascriptMode.unrestricted,
    onWebViewCreated: (controller) {
      controller.loadUrl('assets/index.html');
    },
  )

Use in ListView

WebViewPlusController also allows you to get WebViewPlus height like controller.getHeight()

WebViewPlusController _controller;
double _height = 1;

@override
Widget build(BuildContext context) {
return Scaffold(
  appBar: AppBar(
    title: Text('ListView Example'),
  ),
  body: ListView(
    children: [
      SizedBox(
        height: _height,
        child: WebViewPlus(
          onWebViewCreated: (controller) {
            this._controller = controller;
            controller.loadUrl('assets/index.html');
          },
          onPageFinished: (url) {
            _controller.getHeight().then((double height) {
              print("Height:  " + height.toString());
              setState(() {
                _height = height;
              });
            });
          },
          javascriptMode: JavascriptMode.unrestricted,
        ),
      )
    ],
  ),
);
}

Plus APIs

WebViewPlusController controller;

  • controller.loadUrl('path/to/index.html') load HTML content from Assets.
  • controller.loadString(r"<html>HTML, CSS and Javascript code in raw string</html>"); load HTML, CSS and Javascript Code from a String.
  • controller.getHeight() returns height of WebViewPlus.

API differences from webview_flutter

There are very minor API differences as following.

webview_flutterwebview_flutter_plus
WebViewWebViewPlus
WebViewControllerWebViewPlusController contains WebViewController inside.
WebViewCreatedCallbackWebViewPlusCreatedCallback

Rest everything is same as webview_flutter.

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add webview_flutter_plus

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  webview_flutter_plus: ^0.3.0+2

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:webview_flutter_plus/webview_flutter_plus.dart'; 

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: WebViewPlusExampleMainPage(),
    );
  }
}

class WebViewPlusExampleMainPage extends StatefulWidget {
  const WebViewPlusExampleMainPage({Key? key}) : super(key: key);

  @override
  _WebViewPlusExampleMainPageState createState() =>
      _WebViewPlusExampleMainPageState();
}

class _WebViewPlusExampleMainPageState
    extends State<WebViewPlusExampleMainPage> {
  WebViewPlusController? _controller;
  double _height = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('webview_flutter_plus Example'),
      ),
      body: ListView(
        children: [
          Text("Height of WebviewPlus: $_height",
              style: const TextStyle(fontWeight: FontWeight.bold)),
          SizedBox(
            height: _height,
            child: WebViewPlus(
              serverPort: 5353,
              javascriptChannels: null,
              initialUrl: 'assets/index.html',
              onWebViewCreated: (controller) {
                _controller = controller;
              },
              onPageFinished: (url) {
                _controller?.getHeight().then((double height) {
                  debugPrint("Height: " + height.toString());
                  setState(() {
                    _height = height;
                  });
                });
              },
              javascriptMode: JavascriptMode.unrestricted,
            ),
          )
        ],
      ),
    );
  }
} 

Download Details:

Author: shah-xad

Source Code: https://github.com/shah-xad/webview_flutter_plus

#flutter #javascript #webview 

An Extension of Webview_flutter to Load HTML,CSS and Javascript

Show A Webview Window on Your Flutter Deksktop Application

desktop_webview_window

Show a webview window on your flutter deksktop application.

   
WindowsWebview2 1.0.992.28
LinuxWebKitGTK
macOSWKWebview

Getting Started

modify your main method.

import 'package:desktop_webview_window/desktop_webview_window.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Add this your main method.
  // used to show a webview title bar.
  if (runWebViewTitleBarWidget(args)) {
    return;
  }

  runApp(MyApp());
}

launch WebViewWindow

  final webview = await WebviewWindow.create();
  webview.launch("https://example.com");

linux requirement

sudo apt install webkit2gtk-4.0

Windows requirement

The backend of desktop_webview_window on Windows is WebView2, which requires WebView2 Runtime installed.

WebView2 Runtime is ship in box with Windows11, but it may not installed on Windows10 devices. So you need consider how to distribute the runtime to your users.

See more: https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution

For convenience, you can use WebviewWindow.isWebviewAvailable() check whether the WebView2 is available.

License

see LICENSE

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add desktop_webview_window

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  desktop_webview_window: ^0.1.6

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:desktop_webview_window/desktop_webview_window.dart'; 

example/lib/main.dart

import 'dart:io';

import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

void main(List<String> args) {
  debugPrint('args: $args');
  if (runWebViewTitleBarWidget(args)) {
    return;
  }
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final TextEditingController _controller = TextEditingController(
    text: 'https://example.com',
  );

  bool? _webviewAvailable;

  @override
  void initState() {
    super.initState();
    WebviewWindow.isWebviewAvailable().then((value) {
      setState(() {
        _webviewAvailable = value;
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
          actions: [
            IconButton(
              onPressed: () async {
                final webview = await WebviewWindow.create(
                  configuration: CreateConfiguration(
                    windowHeight: 1280,
                    windowWidth: 720,
                    title: "ExampleTestWindow",
                    titleBarTopPadding: Platform.isMacOS ? 20 : 0,
                    userDataFolderWindows: await _getWebViewPath(),
                  ),
                );
                webview
                  ..registerJavaScriptMessageHandler("test", (name, body) {
                    debugPrint('on javaScipt message: $name $body');
                  })
                  ..setApplicationNameForUserAgent(" WebviewExample/1.0.0")
                  ..setPromptHandler((prompt, defaultText) {
                    if (prompt == "test") {
                      return "Hello World!";
                    } else if (prompt == "init") {
                      return "initial prompt";
                    }
                    return "";
                  })
                  ..addScriptToExecuteOnDocumentCreated("""
  const mixinContext = {
    platform: 'Desktop',
    conversation_id: 'conversationId',
    immersive: false,
    app_version: '1.0.0',
    appearance: 'dark',
  }
  window.MixinContext = {
    getContext: function() {
      return JSON.stringify(mixinContext)
    }
  }
""")
                  ..launch("http://localhost:3000/test.html");
              },
              icon: const Icon(Icons.bug_report),
            )
          ],
        ),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                TextField(controller: _controller),
                const SizedBox(height: 16),
                TextButton(
                  onPressed: _webviewAvailable != true ? null : _onTap,
                  child: const Text('Open'),
                ),
                const SizedBox(height: 20),
                TextButton(
                  onPressed: () async {
                    await WebviewWindow.clearAll(
                      userDataFolderWindows: await _getWebViewPath(),
                    );
                    debugPrint('clear complete');
                  },
                  child: const Text('Clear all'),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }

  void _onTap() async {
    final webview = await WebviewWindow.create(
      configuration: CreateConfiguration(
        userDataFolderWindows: await _getWebViewPath(),
        titleBarTopPadding: Platform.isMacOS ? 20 : 0,
      ),
    );
    webview
      ..setBrightness(Brightness.dark)
      ..setApplicationNameForUserAgent("WebviewExample/1.0.0")
      ..launch(_controller.text)
      ..addOnUrlRequestCallback((url) {
        debugPrint('url: $url');
        final uri = Uri.parse(url);
        if (uri.path == '/login_success') {
          debugPrint('login success. token: ${uri.queryParameters['token']}');
          webview.close();
        }
      })
      ..onClose.whenComplete(() {
        debugPrint("on close");
      });
    await Future.delayed(const Duration(seconds: 2));
    for (final javaScript in _javaScriptToEval) {
      try {
        final ret = await webview.evaluateJavaScript(javaScript);
        debugPrint('evaluateJavaScript: $ret');
      } catch (e) {
        debugPrint('evaluateJavaScript error: $e \n $javaScript');
      }
    }
  }
}

const _javaScriptToEval = [
  """
  function test() {
    return;
  }
  test();
  """,
  'eval({"name": "test", "user_agent": navigator.userAgent})',
  '1 + 1',
  'undefined',
  '1.0 + 1.0',
  '"test"',
];

Future<String> _getWebViewPath() async {
  final document = await getApplicationDocumentsDirectory();
  return p.join(
    document.path,
    'desktop_webview_window',
  );
} 

Download Details:

Author: MixinNetwork

Source Code: https://github.com/MixinNetwork/flutter-plugins/tree/main/packages/desktop_webview_window

#flutter #webview #window 

Show A Webview Window on Your Flutter Deksktop Application

A Flutter Plugin Which Allows The Usage of The Android Webkit API

Introduction

A flutter plugin which allows the usage of the android webkit API without needing to display anything.

Setup

Android

  • The following permission has to be added to your AndroidManifest.xml.
<uses-permission android:name="android.permission.INTERNET" />

IOS

  • Not yet implemented, because I currently do not have access to a machine running MacOS.

Usage

  1. The plugin has to be initialized before being able to use it. It is advised to do this in the main function, before the runApp function is called.
import 'package:flutter/material.dart';
import 'package:webview_without_view/webview_without_view.dart' as webview;

import 'home_page.dart';


Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await webview.initialize();

  runApp(HomePage());
} 

After successfully initializing the webview_without_view plugin, you can load a page and retrieve the HTML source, if you wish to. (See the example app for more information)

When you are done with using the functions from webview_without_view, call dispose.

  @override
  void dispose() {
    webview.dispose();
    super.dispose();
  }

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add webview_without_view

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  webview_without_view: ^1.0.6

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:webview_without_view/webview_without_view.dart'; 

example/lib/main.dart

import 'package:flutter/material.dart';

import 'application.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Application.run();
} 

Download Details:

Author: flutterplugins

Source Code: https://gitlab.com/flutterplugins/webview_without_view

#webview #flutter #android 

A Flutter Plugin Which Allows The Usage of The Android Webkit API

A Bootpay Plugin That Provides A WebView Widget on Android and IOS

bootpay_webview_flutter 플러터 라이브러리

webview_flutter를 부트페이가 Fork 떠서 만든 웹뷰입니다. 이미 결제모듈이 동작하는 웹사이트에 webview로 링크만 연결하여 사용하실 웹앱 flutter 개발자분께서는 해당 모듈의 웹뷰를 사용하시면 쉽게 결제 진행이 가능하십니다.

설치하기

pubspec.yaml 파일에 아래 모듈을 추가해주세요

...
dependencies:
 ...
 bootpay_webview_flutter: last_version
...

설정하기

Android

따로 설정하실 것이 없습니다.

iOS

** {your project root}/ios/Runner/Info.plist ** CFBundleURLNameCFBundleURLSchemes의 값은 개발사에서 고유값으로 지정해주셔야 합니다. 외부앱(카드사앱)에서 다시 기존 앱으로 앱투앱 호출시 필요한 스키마 값입니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>CFBundleURLName</key>
            <string>kr.co.bootpaySample</string> 
            <key>CFBundleURLSchemes</key>
            <array>
                <string>bootpaySample</string> 
            </array>
        </dict>
    </array>

    ...
</dict>
</plist>

사용예제

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

import 'dart:io';
import 'package:bootpay_webview_flutter/webview_flutter.dart';

void main() => runApp(MaterialApp(home: WebViewExample()));

class WebViewExample extends StatefulWidget {
  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView example'),
        // This drop down menu demonstrates that Flutter widgets can be shown over the web view.
      ),
      // We're using a Builder here so we have a context that is below the Scaffold
      // to allow calling Scaffold.of(context) so we can show a snackbar.
      body: Builder(builder: (BuildContext context) {
        return WebView(
          initialUrl: 'https://your.payweb.domain',
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
          },
          onProgress: (int progress) {
            print("WebView is loading (progress : $progress%)");
          },
          navigationDelegate: (NavigationRequest request) {
            if (request.url.startsWith('https://your.service.domain/')) {
              print('allowing navigation to $request');
              return NavigationDecision.navigate;
            } else if(Platform.isAndroid) { //bootpay의 정상 수행을 위해 필요합니다
              return NavigationDecision.prevent;
            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },
          onPageStarted: (String url) {
            print('Page started loading: $url');
          },
          onPageFinished: (String url) {
            print('Page finished loading: $url');
          },
        );
      }),
    );
  }
}

Documentation

부트페이 개발매뉴얼을 참조해주세요

기술문의

채팅으로 문의

License

MIT License.

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add bootpay_webview_flutter

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  bootpay_webview_flutter: ^3.2.2

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:bootpay_webview_flutter/bootpay_webview_flutter.dart'; 

example/lib/main.dart

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

import 'dart:io';
import 'package:bootpay_webview_flutter/webview_flutter.dart';

void main() => runApp(MaterialApp(home: WebViewExample()));

class WebViewExample extends StatefulWidget {
  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  final Completer<WebViewController> _controller =
  Completer<WebViewController>();

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView example'),
        // This drop down menu demonstrates that Flutter widgets can be shown over the web view.
      ),
      // We're using a Builder here so we have a context that is below the Scaffold
      // to allow calling Scaffold.of(context) so we can show a snackbar.
      body: Builder(builder: (BuildContext context) {
        return WebView(
          // initialUrl: 'https://your.payweb.domain',
          initialUrl: 'https://d-cdn.bootapi.com/test/payment/',
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
          },
          onProgress: (int progress) {
            print("WebView is loading (progress : $progress%)");
          },
          navigationDelegate: (NavigationRequest request) {
            if (request.url.startsWith('https://www.youtube.com/')) {
              print('blocking navigation to $request}');
              return NavigationDecision.prevent;
            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },
          onPageStarted: (String url) {
            print('Page started loading: $url');
          },
          onPageFinished: (String url) {
            print('Page finished loading: $url');
          },
        );
      }),
    );
  }
} 

Download Details:

Author: bootpay

Source Code: https://github.com/bootpay/bootpay_webview_flutter

#flutter #widget #webview 

A Bootpay Plugin That Provides A WebView Widget on Android and IOS

A Flutter Plugin That Provides A jsBridge on WebView Widget

Add JsBridge Plugin to the WebView

A Flutter plugin that provides a JsBridge on WebView widget.

This plugin must introduce webview_flutter

Usage

Add flutter_jsbridge_plugin as a dependency in your pubspec.yaml file.

dependencies:
  flutter_jsbridge_plugin: ^0.0.4

Init JsBridge and register handler

...
final JsBridge _jsBridge = JsBridge();
...
WebView(
    initialUrl: "https://www.baidu.com?timeStamp=${new DateTime.now().millisecondsSinceEpoch}",
    javascriptMode: JavascriptMode.unrestricted,
    onWebViewCreated: (WebViewController webViewController) async {
        _jsBridge.loadJs(webViewController);
        _controller.complete(webViewController);
        _jsBridge.registerHandler("getToken", onCallBack: (data, func) {
            // return token to js
            func({"token": "token"});
        });
        _jsBridge.registerHandler("IAPpayment", onCallBack: (data, func) {
            print("js call flutter iap");
        });
        _jsBridge.registerHandler("back", onCallBack: (data, func) {
            print("js call flutter back");
        });
    },
    navigationDelegate: (NavigationRequest request) {
        if (_jsBridge.handlerUrl(request.url)) {
            return NavigationDecision.navigate;
        }
        return NavigationDecision.prevent;
    },
    onPageStarted: (url) {
        _jsBridge.init();
    },
))

JS Usage

js file

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add flutter_jsbridge_x

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  flutter_jsbridge_x: ^1.1.0

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:flutter_jsbridge_x/flutterjsbridgeplugin.dart';
import 'package:flutter_jsbridge_x/init_script.dart';
import 'package:flutter_jsbridge_x/js_bridge.dart';
import 'package:flutter_jsbridge_x/js_obj.dart'; 

example/lib/main.dart

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

import 'package:flutter/services.dart';
import 'package:flutter_jsbridge_x/flutterjsbridgeplugin.dart';
import 'package:flutter_jsbridge_x/js_bridge.dart';
import 'package:webview_flutter/webview_flutter.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';

  final Completer<WebViewController> _controller =
      Completer<WebViewController>();
  final JsBridge _jsBridge = JsBridge();

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      platformVersion = await Flutterjsbridgeplugin.platformVersion;
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('Plugin example app'),
          ),
          body: WebView(
            initialUrl: "https://www.baidu.com?timeStamp=${new DateTime.now().millisecondsSinceEpoch}",
            javascriptMode: JavascriptMode.unrestricted,
            onWebViewCreated: (WebViewController webViewController) async {
              _jsBridge.loadJs(webViewController.evaluateJavascript);
              _controller.complete(webViewController);
              _jsBridge.registerHandler("getToken", onCallBack: (data, func) {
                // return token to js
                func({"token": "token"});
              });
              _jsBridge.registerHandler("IAPpayment", onCallBack: (data, func) {
                print("js call flutter iap");
              });
              _jsBridge.registerHandler("back", onCallBack: (data, func) {
                print("js call flutter back");
              });

            },
            navigationDelegate: (NavigationRequest request) {
              if (_jsBridge.handlerUrl(request.url)) {
                return NavigationDecision.navigate;
              }
              return NavigationDecision.prevent;
            },
            onPageStarted: (url) {
              _jsBridge.init();
            },
            debuggingEnabled: true,
          )),
    );
  }
} 

Download Details:

Author: ibeilly

Source Code: https://github.com/ibeilly/flutter_jsbridge_plugin

#flutter #widget #webview 

A Flutter Plugin That Provides A jsBridge on WebView Widget

A Flutter Plugin That Provides a WebView Widget on Web

webview_flutter_web

This is an implementation of the webview_flutter plugin for web.

It is currently severely limited and doesn't implement most of the available functionality. The following functionality is currently available:

  • loadUrl (Without headers)
  • requestUrl
  • loadHTMLString (Without baseUrl)
  • Setting the initialUrl through CreationParams.

Nothing else is currently supported.

Usage

This package is not an endorsed implementation of the webview_flutter plugin yet, so it currently requires extra setup to use:

  • Add this package as an explicit dependency of your project, in addition to depending on webview_flutter.
  • Register WebWebViewPlatform as the WebView.platform before creating a WebView. See below for examples.

Once those steps below are complete, the APIs from webview_flutter listed above can be used as normal on web.

Registering the implementation

Before creating a WebView (for instance, at the start of main), you will need to register the web implementation.

Web-only project example

...
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_web/webview_flutter_web.dart';

main() {
  WebView.platform = WebWebViewPlatform();
  ...

Multi-platform project example

If your project supports platforms other than web, you will need to use a conditional import to avoid directly including webview_flutter_web.dart on non-web platforms. For example:

register_web_webview.dart:

import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_web/webview_flutter_web.dart';

void registerWebViewWebImplementation() {
  WebView.platform = WebWebViewPlatform();
}

register_web_webview_stub.dart:

void registerWebViewWebImplementation() {
  // No-op.
}

main.dart:

...
import 'register_web_webview_stub.dart'
    if (dart.library.html) 'register_web.dart';

main() {
  registerWebViewWebImplementation();
  ...

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add webview_flutter_web

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  webview_flutter_web: ^0.1.0+4

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:webview_flutter_web/webview_flutter_web.dart'; 

example/lib/main.dart

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';

import 'web_view.dart';

void main() {
  runApp(const MaterialApp(home: _WebViewExample()));
}

class _WebViewExample extends StatefulWidget {
  const _WebViewExample({Key? key}) : super(key: key);

  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<_WebViewExample> {
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView example'),
        actions: <Widget>[
          _SampleMenu(_controller.future),
        ],
      ),
      body: WebView(
        initialUrl: 'https://flutter.dev',
        onWebViewCreated: (WebViewController controller) {
          _controller.complete(controller);
        },
      ),
    );
  }
}

enum _MenuOptions {
  doPostRequest,
}

class _SampleMenu extends StatelessWidget {
  const _SampleMenu(this.controller);

  final Future<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> controller) {
        return PopupMenuButton<_MenuOptions>(
          onSelected: (_MenuOptions value) {
            switch (value) {
              case _MenuOptions.doPostRequest:
                _onDoPostRequest(controller.data!, context);
                break;
            }
          },
          itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.doPostRequest,
              child: Text('Post Request'),
            ),
          ],
        );
      },
    );
  }

  Future<void> _onDoPostRequest(
      WebViewController controller, BuildContext context) async {
    final WebViewRequest request = WebViewRequest(
      uri: Uri.parse('https://httpbin.org/post'),
      method: WebViewRequestMethod.post,
      headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'},
      body: Uint8List.fromList('Test Body'.codeUnits),
    );
    await controller.loadRequest(request);
  }
} 

Download Details:

Author: 

Source Code: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web

#flutter #webview 

A Flutter Plugin That Provides a WebView Widget on Web

WebView OAuth Flows for Desktop Flutter Apps

Desktop webview auth

This package enables Firebase OAuth on desktop via webview

Supported providers:

  • Google
  • Facebook
  • Twitter

Installation

macOS setup

The recaptcha verification flow is done on the local server, and it requires that the app has the following in the Release.entitlements:

<key>com.apple.security.network.server</key>
<true/>

Linux setup

To display webview on Linux, libwebkit2gtk-4.0-dev is used, if you don't have it already installed:

apt install libwebkit2gtk-4.0-dev

Additionally, if Flutter is installed using snap, you might face issues compiling the app, to fix you would need to uninstall the snap version and install Flutter manually on Linux.

Windows setup

Make sure you are on latest stable channle of Flutter, and have installed the requirements as mentioned here.

Nothing extra is needed to get started on Windows.

Add dependency

flutter pub add desktop_webview_auth

Imports

import 'package:desktop_webview_auth/desktop_webview_auth.dart';
import 'package:desktop_webview_auth/google.dart';
import 'package:desktop_webview_auth/facebook.dart';
import 'package:desktop_webview_auth/twitter.dart';

Usage

  • Configure OAuth providers in firebase console
  • Create an instance of ProviderArgs
final googleSignInArgs = GoogleSignInArgs(
  clientId:
    '448618578101-sg12d2qin42cpr00f8b0gehs5s7inm0v.apps.googleusercontent.com',
  redirectUri:
    'https://react-native-firebase-testing.firebaseapp.com/__/auth/handler',
  scope: 'email',
)
  • call DesktopWebviewAuth.signIn
try {
    final result = await DesktopWebviewAuth.signIn(args);

    print(result?.accessToken);
    print(result?.tokenSecret);
} catch (err) {
    // something went wrong
}
  • create an instance of OAuthCredential and sign in
import 'package:firebase_auth/firebase_auth.dart';

final credential = GoogleAuthProvider.credential(accessToken: result.accessToken)

FirebaseAuth.instance.signInWithCredential(credential);

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add desktop_webview_auth

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  desktop_webview_auth: ^0.0.6

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:desktop_webview_auth/desktop_webview_auth.dart'; 

example/lib/main.dart

// ignore_for_file: constant_identifier_names

import 'package:flutter/material.dart';
import 'package:googleapis/identitytoolkit/v3.dart';
import 'package:googleapis_auth/googleapis_auth.dart';

import 'package:desktop_webview_auth/desktop_webview_auth.dart';
import 'package:desktop_webview_auth/google.dart';
import 'package:desktop_webview_auth/facebook.dart';
import 'package:desktop_webview_auth/twitter.dart';

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

typedef SignInCallback = Future<void> Function();
const String apiKey = 'AIzaSyAgUhHU8wSJgO5MVNy95tMT07NEjzMOfz0';

const GOOGLE_CLIENT_ID =
    '448618578101-sg12d2qin42cpr00f8b0gehs5s7inm0v.apps.googleusercontent.com';
const REDIRECT_URI =
    'https://react-native-firebase-testing.firebaseapp.com/__/auth/handler';
const TWITTER_API_KEY = 'YEXSiWv5UeCHyy0c61O2LBC3B';
const TWITTER_API_SECRET_KEY =
    'DOd9dCCRFgtnqMDQT7A68YuGZtvcO4WP1mEFS4mEJAUooM4yaE';
const FACEBOOK_CLIENT_ID = '128693022464535';

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

  SignInCallback signInWithArgs(BuildContext context, ProviderArgs args) =>
      () async {
        final result = await DesktopWebviewAuth.signIn(args);
        notify(context, result?.toString());
      };

  void notify(BuildContext context, String? result) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Result: $result'),
      ),
    );
  }

  Future<void> getRecaptchaVerification(BuildContext context) async {
    final client = clientViaApiKey(apiKey);
    final identityToolkit = IdentityToolkitApi(client);
    final res = identityToolkit.relyingparty;

    final recaptchaResponse = await res.getRecaptchaParam();

    final args = RecaptchaArgs(
      siteKey: recaptchaResponse.recaptchaSiteKey!,
      siteToken: recaptchaResponse.recaptchaStoken!,
    );

    final result = await DesktopWebviewAuth.recaptchaVerification(
      args,
      height: 600,
      width: 600,
    );

    notify(context, result?.verificationId);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ButtonStyle(
            padding: MaterialStateProperty.all(const EdgeInsets.all(20)),
          ),
        ),
      ),
      home: Scaffold(
        body: Builder(
          builder: (context) {
            final buttons = [
              ElevatedButton(
                child: const Text('Sign in with Google'),
                onPressed: signInWithArgs(
                  context,
                  GoogleSignInArgs(
                    clientId: GOOGLE_CLIENT_ID,
                    redirectUri: REDIRECT_URI,
                  ),
                ),
              ),
              ElevatedButton(
                child: const Text('Sign in with Twitter'),
                onPressed: signInWithArgs(
                  context,
                  TwitterSignInArgs(
                    apiKey: TWITTER_API_KEY,
                    apiSecretKey: TWITTER_API_SECRET_KEY,
                    redirectUri: REDIRECT_URI,
                  ),
                ),
              ),
              ElevatedButton(
                child: const Text('Sign in with Facebook'),
                onPressed: signInWithArgs(
                  context,
                  FacebookSignInArgs(
                    clientId: FACEBOOK_CLIENT_ID,
                    redirectUri: REDIRECT_URI,
                  ),
                ),
              ),
              ElevatedButton(
                child: const Text('Recaptcha Verification'),
                onPressed: () => getRecaptchaVerification(context),
              ),
            ];

            return Center(
              child: ConstrainedBox(
                constraints: const BoxConstraints(maxWidth: 300),
                child: ListView.separated(
                  itemCount: buttons.length,
                  shrinkWrap: true,
                  separatorBuilder: (_, __) => const Divider(),
                  itemBuilder: (context, index) {
                    return buttons[index];
                  },
                ),
              ),
            );
          },
        ),
      ),
    );
  }
} 

Download Details:

Author: invertase

Source Code: https://github.com/invertase/flutter_desktop_webview_auth

#flutter #webview #auth 

WebView OAuth Flows for Desktop Flutter Apps

A Flutter Plugin That Provides A WebView Widget on Android and IOS

WebView for Flutter

[En]

A Flutter plugin that provides a WebView widget. Base on webview_flutter and enhance it. For the moment, webview_flutter and webview_flutter_max can not coexist in one project,otherwise it will cause duplicate class exception in ios.

[Zh]

Flutter webview插件 基于webview_flutter插件改造,并增强一些特性。 目前为止,不能在一个工程内同时引用webview_flutter和webview_flutter_max, 因为ios下会报重复类异常。

Addition features:

  • file chooser on android
  • enable geolocation
  • igorne ssl error

how to publish

set http proxy flutter pub publish

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add webview_flutter_max

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  webview_flutter_max: ^1.0.7

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:webview_flutter_max/platform_interface.dart';
import 'package:webview_flutter_max/webview_flutter.dart'; 

example/lib/main.dart

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() => runApp(MaterialApp(home: WebViewExample()));

const String kNavigationExamplePage = '''
<!DOCTYPE html><html>
<head><title>Navigation Delegate Example</title></head>
<body>
<p>
The navigation delegate is set to block navigation to the youtube website.
</p>
<ul>
<ul><a href="https://www.youtube.com/">https://www.youtube.com/</a></ul>
<ul><a href="https://www.google.com/">https://www.google.com/</a></ul>
</ul>
</body>
</html>
''';

class WebViewExample extends StatefulWidget {
  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView example'),
        // This drop down menu demonstrates that Flutter widgets can be shown over the web view.
        actions: <Widget>[
          NavigationControls(_controller.future),
          SampleMenu(_controller.future),
        ],
      ),
      // We're using a Builder here so we have a context that is below the Scaffold
      // to allow calling Scaffold.of(context) so we can show a snackbar.
      body: Builder(builder: (BuildContext context) {
        return WebView(
          initialUrl: 'https://flutter.dev',
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
          },
          onProgress: (int progress) {
            print("WebView is loading (progress : $progress%)");
          },
          javascriptChannels: <JavascriptChannel>{
            _toasterJavascriptChannel(context),
          },
          navigationDelegate: (NavigationRequest request) {
            if (request.url.startsWith('https://www.youtube.com/')) {
              print('blocking navigation to $request}');
              return NavigationDecision.prevent;
            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },
          onPageStarted: (String url) {
            print('Page started loading: $url');
          },
          onPageFinished: (String url) {
            print('Page finished loading: $url');
          },
          gestureNavigationEnabled: true,
        );
      }),
      floatingActionButton: favoriteButton(),
    );
  }

  JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
        name: 'Toaster',
        onMessageReceived: (JavascriptMessage message) {
          // ignore: deprecated_member_use
          Scaffold.of(context).showSnackBar(
            SnackBar(content: Text(message.message)),
          );
        });
  }

  Widget favoriteButton() {
    return FutureBuilder<WebViewController>(
        future: _controller.future,
        builder: (BuildContext context,
            AsyncSnapshot<WebViewController> controller) {
          if (controller.hasData) {
            return FloatingActionButton(
              onPressed: () async {
                final String url = (await controller.data!.currentUrl())!;
                // ignore: deprecated_member_use
                Scaffold.of(context).showSnackBar(
                  SnackBar(content: Text('Favorited $url')),
                );
              },
              child: const Icon(Icons.favorite),
            );
          }
          return Container();
        });
  }
}

enum MenuOptions {
  showUserAgent,
  listCookies,
  clearCookies,
  addToCache,
  listCache,
  clearCache,
  navigationDelegate,
}

class SampleMenu extends StatelessWidget {
  SampleMenu(this.controller);

  final Future<WebViewController> controller;
  final CookieManager cookieManager = CookieManager();

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> controller) {
        return PopupMenuButton<MenuOptions>(
          onSelected: (MenuOptions value) {
            switch (value) {
              case MenuOptions.showUserAgent:
                _onShowUserAgent(controller.data!, context);
                break;
              case MenuOptions.listCookies:
                _onListCookies(controller.data!, context);
                break;
              case MenuOptions.clearCookies:
                _onClearCookies(context);
                break;
              case MenuOptions.addToCache:
                _onAddToCache(controller.data!, context);
                break;
              case MenuOptions.listCache:
                _onListCache(controller.data!, context);
                break;
              case MenuOptions.clearCache:
                _onClearCache(controller.data!, context);
                break;
              case MenuOptions.navigationDelegate:
                _onNavigationDelegateExample(controller.data!, context);
                break;
            }
          },
          itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
            PopupMenuItem<MenuOptions>(
              value: MenuOptions.showUserAgent,
              child: const Text('Show user agent'),
              enabled: controller.hasData,
            ),
            const PopupMenuItem<MenuOptions>(
              value: MenuOptions.listCookies,
              child: Text('List cookies'),
            ),
            const PopupMenuItem<MenuOptions>(
              value: MenuOptions.clearCookies,
              child: Text('Clear cookies'),
            ),
            const PopupMenuItem<MenuOptions>(
              value: MenuOptions.addToCache,
              child: Text('Add to cache'),
            ),
            const PopupMenuItem<MenuOptions>(
              value: MenuOptions.listCache,
              child: Text('List cache'),
            ),
            const PopupMenuItem<MenuOptions>(
              value: MenuOptions.clearCache,
              child: Text('Clear cache'),
            ),
            const PopupMenuItem<MenuOptions>(
              value: MenuOptions.navigationDelegate,
              child: Text('Navigation Delegate example'),
            ),
          ],
        );
      },
    );
  }

  void _onShowUserAgent(
      WebViewController controller, BuildContext context) async {
    // Send a message with the user agent string to the Toaster JavaScript channel we registered
    // with the WebView.
    await controller.evaluateJavascript(
        'Toaster.postMessage("User Agent: " + navigator.userAgent);');
  }

  void _onListCookies(
      WebViewController controller, BuildContext context) async {
    final String cookies =
        await controller.evaluateJavascript('document.cookie');
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(SnackBar(
      content: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const Text('Cookies:'),
          _getCookieList(cookies),
        ],
      ),
    ));
  }

  void _onAddToCache(WebViewController controller, BuildContext context) async {
    await controller.evaluateJavascript(
        'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(const SnackBar(
      content: Text('Added a test entry to cache.'),
    ));
  }

  void _onListCache(WebViewController controller, BuildContext context) async {
    await controller.evaluateJavascript('caches.keys()'
        '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
        '.then((caches) => Toaster.postMessage(caches))');
  }

  void _onClearCache(WebViewController controller, BuildContext context) async {
    await controller.clearCache();
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(const SnackBar(
      content: Text("Cache cleared."),
    ));
  }

  void _onClearCookies(BuildContext context) async {
    final bool hadCookies = await cookieManager.clearCookies();
    String message = 'There were cookies. Now, they are gone!';
    if (!hadCookies) {
      message = 'There are no cookies.';
    }
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(SnackBar(
      content: Text(message),
    ));
  }

  void _onNavigationDelegateExample(
      WebViewController controller, BuildContext context) async {
    final String contentBase64 =
        base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
    await controller.loadUrl('data:text/html;base64,$contentBase64');
  }

  Widget _getCookieList(String cookies) {
    if (cookies == null || cookies == '""') {
      return Container();
    }
    final List<String> cookieList = cookies.split(';');
    final Iterable<Text> cookieWidgets =
        cookieList.map((String cookie) => Text(cookie));
    return Column(
      mainAxisAlignment: MainAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: cookieWidgets.toList(),
    );
  }
}

class NavigationControls extends StatelessWidget {
  const NavigationControls(this._webViewControllerFuture)
      : assert(_webViewControllerFuture != null);

  final Future<WebViewController> _webViewControllerFuture;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: _webViewControllerFuture,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
        final bool webViewReady =
            snapshot.connectionState == ConnectionState.done;
        final WebViewController controller = snapshot.data!;
        return Row(
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.arrow_back_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller.canGoBack()) {
                        await controller.goBack();
                      } else {
                        // ignore: deprecated_member_use
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(content: Text("No back history item")),
                        );
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.arrow_forward_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller.canGoForward()) {
                        await controller.goForward();
                      } else {
                        // ignore: deprecated_member_use
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(
                              content: Text("No forward history item")),
                        );
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.replay),
              onPressed: !webViewReady
                  ? null
                  : () {
                      controller.reload();
                    },
            ),
          ],
        );
      },
    );
  }
} 

Download Details:

Author: 

Source Code: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter

#flutter #webview 

A Flutter Plugin That Provides A WebView Widget on Android and IOS

A Flutter Plugin That Provides A WebView Widget

WebView for Flutter used x5 core

A Flutter plugin that provides a WebView widget.

On iOS the WebView widget is backed by a WKWebView; On Android the WebView widget is backed by a WebView.

Usage

Add webview_flutter as a dependency in your pubspec.yaml file.

You can now include a WebView widget in your widget tree. See the WebView widget's Dartdoc for more details on how to use the widget.

Android Platform Views

The WebView is relying on Platform Views to embed the Android’s webview within the Flutter app. By default a Virtual Display based platform view backend is used, this implementation has multiple keyboard. When keyboard input is required we recommend using the Hybrid Composition based platform views implementation. Note that on Android versions prior to Android 10 Hybrid Composition has some performance drawbacks.

Using Hybrid Composition

  1. Set the minSdkVersion in android/app/build.gradle:
android {
    defaultConfig {
        minSdkVersion 19
    }
}

This means that app will only be available for users that run Android SDK 19 or higher.

  1. To enable hybrid composition, set WebView.platform = SurfaceAndroidWebView(); in initState(). For example:
import 'dart:io';

import 'package:webview_flutter/webview_flutter.dart';

class WebViewExample extends StatefulWidget {
  @override
  WebViewExampleState createState() => WebViewExampleState();
}

class WebViewExampleState extends State<WebViewExample> {
  @override
  void initState() {
    super.initState();
    // Enable hybrid composition.
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: 'https://flutter.dev',
    );
  }
}

Enable Material Components for Android

To use Material Components when the user interacts with input elements in the WebView, follow the steps described in the Enabling Material Components instructions.

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add webview_flutter_x5

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  webview_flutter_x5: ^2.0.8+9

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:webview_flutter_x5/platform_interface.dart';
import 'package:webview_flutter_x5/webview_flutter.dart'; 

example/lib/main.dart

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

import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:webview_flutter_x5/webview_flutter.dart';
import 'package:webview_flutter/webview_flutter.dart' as OriginWebview;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Webview demp',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({this.title = ''});

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late FocusNode _focusNode;
  ChooseFileMode chooseFileMode = ChooseFileMode.auto;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // return Container();
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: ListView(
          children: <Widget>[
            GestureDetector(
              onTap: () {
                WebviewFlutterX5.initX5(needPermissionCallback: (List<String> permissions) async {
                  List<Permission> permissionList = [];
                  for (final item in permissions) {
                    if (item == 'camera') {
                      permissionList.add(Permission.camera);
                    } else if (item == 'storage') {
                      permissionList.add(Permission.storage);
                    }
                  }
                  if (permissionList.isNotEmpty) {
                    Map<Permission, PermissionStatus> statuses = await permissionList.request();
                    print('dart层权限: $permissions $statuses');
                  }
                });
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  'X5初始化',
                ),
              ),
            ),
            GestureDetector(
              onTap: () {
                Navigator.push(context, MaterialPageRoute(builder: (_) {
                  return Scaffold(
                    appBar: AppBar(
                      title: Text('X5内核加载状态'),
                    ),
                    body: SafeArea(child: WebviewPage('http://soft.imtt.qq.com/browser/tes/feedback.html')),
                  );
                }));
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  'X5内核加载状态',
                ),
              ),
            ),
            GestureDetector(
              onTap: () {
                Navigator.push(context, MaterialPageRoute(builder: (_) {
                  return Scaffold(
                    appBar: AppBar(
                      title: Text('x5内核加载测试'),
                    ),
                    body: SafeArea(child: WebviewPage('http://debugtbs.qq.com/')),
                  );
                }));
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  'x5内核加载测试',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                if (_focusNode.hasFocus) {
                  _focusNode.unfocus();
                }
                String url2 = 'https://www.baidu.com/';
                await Navigator.push(context, MaterialPageRoute(builder: (_) {
                  return Scaffold(
                    appBar: AppBar(
                      title: Text('测试'),
                    ),
                    body: SafeArea(child: WebviewPage(url2)),
                  );
                }));
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '测试',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                if (_focusNode.hasFocus) {
                  _focusNode.unfocus();
                }
                String url2 = 'https://www.baidu.com/';
                await Navigator.push(context, MaterialPageRoute(builder: (_) {
                  return Scaffold(
                    appBar: AppBar(
                      title: Text('官方webview插件'),
                    ),
                    body: SafeArea(
                        child: OriginWebview.WebView(
                      initialUrl: url2,
                    )),
                  );
                }));
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '官方webview插件',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                await WebviewFlutterX5.canGetDeviceId(false);
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '禁止获取imei',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                await WebviewFlutterX5.canGetSubscriberId(false);
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '禁止获取IMSI',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                await WebviewFlutterX5.canGetAndroidId(false);
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '禁止获取AndroidID',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                await WebviewFlutterX5.manualPhoneModel();
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '设置手机型号',
                ),
              ),
            ),
            GestureDetector(
              onTap: () async {
                await WebviewFlutterX5.manualPhoneSerial();
              },
              child: Container(
                width: 100.0,
                height: 45.0,
                margin: EdgeInsets.only(top: 20),
                color: Colors.blue[200],
                alignment: Alignment.center,
                child: Text(
                  '设置SN',
                ),
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Container(
                  width: 200.0,
                  height: 45.0,
                  margin: EdgeInsets.only(top: 20),
                  color: Colors.blue[200],
                  alignment: Alignment.center,
                  child: Text(
                    '选择文件模式 $chooseFileMode',
                  ),
                ),
                PopupMenuButton<ChooseFileMode>(
                    icon: Icon(
                      Icons.add,
                      size: 24,
                      color: Colors.grey,
                    ),
                    padding: EdgeInsets.zero,
                    onSelected: (value) async{
                      setState(() {
                        chooseFileMode = value;
                      });
                      await WebviewFlutterX5.setChooseFileMode(value);
                    },
                    itemBuilder: (BuildContext context) => getWidgets()),
              ],
            ),
          ],
        ),
      ),
    );
  }

  List<PopupMenuEntry<ChooseFileMode>> getWidgets() {
    List<ChooseFileMode> strategyList = ChooseFileMode.values;
    List<PopupMenuEntry<ChooseFileMode>> widgets = [];
    for (int i = 0; i < strategyList.length; i++) {
      var ele = strategyList[i];
      widgets.add(PopupMenuItem<ChooseFileMode>(
          value: ele,
          child: Container(
            // color: Colors.green,
            width: 180,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text(
                  '$ele',
                  style: TextStyle(fontSize: 14.0),
                ),
                if (ele == chooseFileMode)
                  Icon(
                    Icons.favorite,
                    size: 16.0,
                    color: Colors.blue,
                  )
                else
                  Container(),
              ],
            ),
          )));
      widgets.add(const PopupMenuDivider());
    }
    return widgets;
  }
}

class WebviewPage extends StatefulWidget {
  WebviewPage(this.url);

  final String url;

  @override
  _WebviewPageState createState() => _WebviewPageState();
}

class _WebviewPageState extends State<WebviewPage> {
  final Completer<WebViewController> _completer = Completer<WebViewController>();
  bool loading = true;

  @override
  void initState() {
    super.initState();
    // Enable hybrid composition.
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: widget.url,
          onWebViewCreated: _completer.complete,
          javascriptMode: JavascriptMode.unrestricted,
          onPageStarted: (url) {
            print('web view page test: start url:$url');
          },
          onPageFinished: (url) {
            print('web view page test: finish url:$url');
            setState(() {
              loading = false;
            });
          },
          navigationDelegate: (request) {
            print('web view page test: url:${widget.url}');
            // if (request.url.startsWith('http://')) {
            //   request.url.replaceFirst('http://', 'https://');
            // }
            print('web view page test: url:${widget.url}');
            return NavigationDecision.navigate;
          },
        ),
        loading ? Center(child: CircularProgressIndicator()) : Container(),
      ],
    );
  }
} 

Download Details:

Author: buaashuai

Source Code: https://github.com/buaashuai/plugins

#flutter #webview #widget 

A Flutter Plugin That Provides A WebView Widget

Tauri: Build Smaller, Faster and More Secure Desktop App with Web Frontend

Introduction

Tauri is a framework for building tiny, blazing fast binaries for all major desktop platforms. Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface. The backend of the application is a rust-sourced binary with an API that the front-end can interact with.

The user interface in Tauri apps currently leverages tao as a window handling library on macOS and Windows, and gtk on Linux via the Tauri-team incubated and maintained WRY, which creates a unified interface to the system webview (and other goodies like Menu and Taskbar), leveraging WebKit on macOS, WebView2 on Windows and WebKitGTK on Linux.

To learn more about the details of how all of these pieces fit together, please consult this ARCHITECTURE.md document.

Get Started

If you are interested in making a tauri-app, please visit the documentation website. This README is directed towards those who are interested in contributing to the core library. But if you just want a quick overview about where tauri is at in its development, here's a quick burndown:

Platforms

  • Windows 7,8,10
  • Linux
  • macOS
  • iOS (in progress)
  • android (soon)

App Bundles

  • App Icons
  • Build on MacOS (.app, .dmg)
  • Build on Linux (.deb, AppImage)
  • Build on Windows (.exe, .msi)
  • Copy Buffer
  • Device Notifications (toast)
  • Self Updater
  • App Signing
  • Frameless Mode
  • Transparent Mode
  • Multiwindow Mode
  • Tray
  • deeplink RPC (in progress)
  • One-Time commands (coming soon)

Security Features

  • localhost-free (:fire:)
  • custom protocol for secure mode
  • Dynamic ahead of Time Compilation (dAoT) with functional tree-shaking
  • functional Address Space Layout Randomization
  • OTP salting of function names and messages at runtime
  • CSP Injection

Utilities

  • GH Action for creating binaries for all platforms
  • VS Code Extension
  • Tauri Core Plugins
  • Update core dependencies automatically from the command line
  • Rust-based CLI

Comparison between Tauri and Electron

DetailTauriElectron
Installer Size Linux3.1 MB52.1 MB
Memory Consumption Linux180 MB462 MB
Launch Time Linux0.39s0.80s
Interface Service ProviderWRYChromium
Backend BindingRustNode.js (ECMAScript)
Underlying EngineRustV8 (C/C++)
FLOSSYesNo
MultithreadingYesYes
Bytecode DeliveryYesNo
Multiple WindowsYesYes
Auto UpdaterYesYes1
Custom App IconYesYes
Windows BinaryYesYes
MacOS BinaryYesYes
Linux BinaryYesYes
iOS BinarySoonNo
Android BinarySoonNo
Desktop TrayYesYes
Sidecar BinariesYesNo

Notes

  1. Electron has no native auto updater on Linux, but is offered by electron-packager

Development

Tauri is a system composed of a number of moving pieces:

Infrastructure

  • Git for code management
  • GitHub for project management
  • GitHub actions for CI and CD
  • Discord for discussions
  • Netlify-hosted documentation website
  • DigitalOcean meilisearch instance

Major Runtimes

  • Node.js for running the CLI (deno and pure rust are on the roadmap)
  • Cargo for testing, running the dev service, building binaries and as the runtime harness for the webview

Major Languages

  • Rust for the CLI
  • EcmaScript bindings to the Rust API, written in typescript
  • Rust for bindings, rust side of the API, harnesses
  • Rust plugins to Tauri backend

Operating systems

Tauri core can be developed on Mac, Linux and Windows, but you are encouraged to use the latest possible operating systems and build tools for your OS.

Contributing

Before you start working on something, it's best to check if there is an existing issue first. It's also is a good idea to stop by the Discord server and confirm with the team if it makes sense or if someone is already working on it.

Please make sure to read the Contributing Guide before making a pull request.

Thank you to everyone contributing to Tauri!

Documentation

Documentation in a polyglot system is a tricky proposition. To this end, we prefer to use inline documentation of Rust code and at JSDoc in typescript / javascript code. We autocollect these and publish them using Docusaurus v2 and netlify. Here is the hosting repository for the documentation site: https://github.com/tauri-apps/tauri-docs

Testing & Linting

Test all the things! We have a number of test suites, but are always looking to improve our coverage:

  • Rust (cargo test) => sourced via inline #[cfg(test)] declarations
  • TS (jest) => via spec files
  • Smoke Tests (run on merges to latest)
  • eslint, clippy

CI/CD

We recommend you read this article to understand better how we run our pipelines: https://www.jacobbolda.com/setting-up-ci-and-cd-for-tauri/

Organization

Tauri aims to be a sustainable collective based on principles that guide sustainable free and open software communities. To this end it has become a Programme within the Commons Conservancy, and you can contribute financially via Open Collective.

Download Details: 
Author: tauri-apps
Source Code: https://github.com/tauri-apps/tauri 
 

#rust #webview #webdev #html #css #javascript 

Tauri: Build Smaller, Faster and More Secure Desktop App with Web Frontend

Flutter Plugin That Lets You Display Native WebView on MacOS

flutter_macos_webview

Flutter plugin that lets you display native WebView on macOS

For docs see flutter_macos_webview.dart

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add flutter_macos_webview

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  flutter_macos_webview: ^0.0.6

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:flutter_macos_webview/flutter_macos_webview.dart'; 

example/lib/main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter_macos_webview/flutter_macos_webview.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  Future<void> _onOpenPressed(PresentationStyle presentationStyle) async {
    final webview = FlutterMacOSWebView(
      onOpen: () => print('Opened'),
      onClose: () => print('Closed'),
      onPageStarted: (url) => print('Page started: $url'),
      onPageFinished: (url) => print('Page finished: $url'),
      onWebResourceError: (err) {
        print(
          'Error: ${err.errorCode}, ${err.errorType}, ${err.domain}, ${err.description}',
        );
      },
    );

    await webview.open(
      url: 'https://google.com',
      presentationStyle: presentationStyle,
      size: Size(400.0, 400.0),
      userAgent:
          'Mozilla/5.0 (iPhone; CPU iPhone OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
    );

    // await Future.delayed(Duration(seconds: 5));
    // await webview.close();
  }

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      debugShowCheckedModeBanner: false,
      home: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CupertinoButton(
            child: Text('Open as modal'),
            onPressed: () => _onOpenPressed(PresentationStyle.modal),
          ),
          SizedBox(height: 16.0),
          CupertinoButton(
            child: Text('Open as sheet'),
            onPressed: () => _onOpenPressed(PresentationStyle.sheet),
          ),
        ],
      ),
    );
  }
} 

Download Details:

Author: vanelizarov

Source Code: https://github.com/vanelizarov/flutter_macos_webview

#flutter #webview #macos 

Flutter Plugin That Lets You Display Native WebView on MacOS