web3_webview 1.0.10 copy "web3_webview: ^1.0.10" to clipboard
web3_webview: ^1.0.10 copied to clipboard

web3_webview is a powerful bridge between a DApp running inside a WebView and your Flutter application, enabling secure and seamless two-way communication between them

web3_webview #

web3_webview is a Flutter bridge between a DApp running inside an InAppWebView and your host app. It injects an EIP-1193 / EIP-6963 compliant window.ethereum provider (via ethers.min.js + generated script) and routes all wallet operations – connect, sign, sendTransaction, chain management – to native Dart dialogs and web3dart.

Security-first fork: private-key validation, XSS-safe JS injection, EIP-1193 error codes, PermissionRequest default-deny, non-blocking eth_sendTransaction, and correct EIP-712 padding. See Security.


Architecture #

flowchart LR
  DApp -- window.ethereum.request --> JSBridge[JS Provider\nprovider_script.dart]
  JSBridge -- flutter_inappwebview callHandler\nethereumRequest --> WebView[Web3WebView\nweb3_webview_eip1193.dart]
  WebView --> Provider[EthereumProvider\nsingleton + Web3Client]
  Provider --> Signing[SigningHandler\nEIP-191 / EIP-712]
  Provider --> Tx[TransactionHandler\nsend + estimate + gas]
  Provider --> Dialog[WalletDialogService\nbottom sheet]
  Provider -- jsonEncode + evaluateJavascript --> JSBridge

Core modules:

Module File Responsibility
Web3WebView lib/web3_webview_eip1193.dart Wraps flutter_inappwebview, injects ethers.min.js + provider script at AT_DOCUMENT_START (Android re-inject on onLoadStart), validates privateKey, shows initError state, forwards onPermissionRequest as DENY by default
EthereumProvider lib/ethereum/ethereum_provider.dart Singleton, Web3Client + Client lifecycle, handleRequest dispatcher, 12s eth_blockNumber cache, chain registry, event emit via jsonEncode
ProviderScriptGenerator lib/provider/provider_script.dart Generates JS EthereumProvider extends EventTarget, jsonEncode-escapes chainId/accounts/isConnected/info, handles request/send/sendAsync, EIP-6963 announce
SigningHandler lib/signing/signing_handler.dart personal_sign/eth_sign/eth_signTypedData* with full EIP-712 v3/v4 validation, struct hashing, 32-byte padding for bool/address/bytesN
TransactionHandler lib/transaction/transaction_handler.dart Validate to/value/data, estimateGas + 20% BigInt buffer, signTransaction(chainId), sendRawTransaction returns hash immediately (EIP-1193), optional waitForConfirmation
WalletDialogService lib/ethereum/wallet_dialog_service.dart Bottom sheets for connect/sign/tx/switch/add, requestFrom = controller.getUrl().host, WalletDialogTheme

Key Features #

  • Wallet Connection – user-confirmed eth_requestAccounts; returns [] on eth_accounts until connected
  • DApp Integrationwindow.ethereum + window.web3.currentProvider, isMetaMask=true, EIP-6963 discovery
  • Transaction Lifecycle – confirmation dialog, eth_estimateGas buffered, non-blocking hash return, fire-and-forget receipt polling (opt-in blocking)
  • Message Signingpersonal_sign (EIP-191 prefix), eth_sign (raw keccak), eth_signTypedData/v1/v3/v4 (EIP-712)
  • Chain Managementwallet_switchEthereumChain/wallet_addEthereumChain with idempotency checks
  • Two-way Bridgeflutter_inappwebview callHandlerevaluateJavascript, XSS-safe via jsonEncode
  • Multi-chain – any EVM via NetworkConfig

Requirements #

  • Flutter >=3.24.0, Dart ^3.5.0
  • flutter_inappwebview ^6.1.5, web3dart ^2.7.3

Installation #

dependencies:
  web3_webview: ^1.0.9
import 'package:web3_webview/web3_webview.dart';

Quick Start #

privateKey is required and must be a 64-hex (32 bytes) string, with or without 0x. The widget validates it on initState and renders an inline error instead of crashing.

Do not hardcode keys. Load from flutter_secure_storage / Keychain / Keystore and clear on lock. See Security.

import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:web3_webview/web3_webview.dart';

final ethMainnet = NetworkConfig(
  chainId: '0x1',
  chainName: 'Ethereum Mainnet',
  nativeCurrency: NativeCurrency(name: 'Ether', symbol: 'ETH', decimals: 18),
  rpcUrls: ['https://mainnet.infura.io/v3/YOUR_KEY'],
  blockExplorerUrls: ['https://etherscan.io'],
);
final bscMainnet = NetworkConfig(
  chainId: '0x38',
  chainName: 'BNB Smart Chain',
  nativeCurrency: NativeCurrency(name: 'BNB', symbol: 'BNB', decimals: 18),
  rpcUrls: ['https://bsc-dataseed.binance.org'],
  blockExplorerUrls: ['https://bscscan.com'],
);

class DappScreen extends StatefulWidget {
  const DappScreen({super.key});
  @override State<DappScreen> createState() => _DappScreenState();
}

class _DappScreenState extends State<DappScreen> {
  String? _pk;
  @override void initState() {
    super.initState();
    const storage = FlutterSecureStorage();
    storage.read(key: 'evm_pk').then((v) => setState(() => _pk = v));
  }

  @override Widget build(BuildContext context) {
    if (_pk == null) return const Center(child: CircularProgressIndicator());
    return Web3WebView(
      web3WalletConfig: Web3WalletConfig(
        privateKey: _pk!, // required, 0x + 64 hex
        currentNetwork: ethMainnet,
        supportNetworks: [ethMainnet, bscMainnet],
        name: 'MyDApp Wallet',
        id: 'com.example.mydapp',
        // optional theming
        dialogTheme: WalletDialogTheme(primaryColor: Color(0xFF3B82F6)),
        onError: (method, params, message) => debugPrint('[$method] $message'),
      ),
      initialUrlRequest: URLRequest(url: WebUri('https://metamask.github.io/test-dapp/')),
      // SECURITY: override only to grant selectively
      onPermissionRequest: (ctrl, req) async => PermissionResponse(
        resources: req.resources,
        action: PermissionResponseAction.DENY, // default in lib
      ),
    );
  }
}

Handling Web3WalletConfig #

Field Type Required Notes
privateKey String yes 0x + 64 hex or 64 hex; validated with -32602 on invalid, Web3WebView shows inline error
currentNetwork NetworkConfig? no defaults to 0x1
supportNetworks List<NetworkConfig>? no defaults to [eth, bsc]
name/icon/id String? no EIP-6963 info (icon is data-URI), idrdns
dialogTheme WalletDialogTheme? no colors, text styles, paddings
onError void Function(JsonRpcMethod, List?, String)? no invoked before JS error is thrown, receives EIP-1193 code in message

NetworkConfig { chainId (0x hex), chainName, nativeCurrency?, rpcUrls, blockExplorerUrls? }


Supported JSON-RPC #

EthereumProvider.handleRequest (lib/ethereum/ethereum_provider.dart:110) dispatches:

Method Params Behaviour
eth_requestAccounts [] Bottom sheet → connect + accountsChanged events, returns [address] or 4001
eth_accounts [] [] until connected
eth_chainId / net_version [] state.chainId
eth_blockNumber [] 12s cached HexUtils.numberToHex(blockNumber)
eth_call [tx, block] TransactionHandler.handleTransaction (no value)
eth_sendTransaction [tx] Dialog → sign+sendhash immediately, polling fire-and-forget
eth_getBalance [addr, tag] getBalance → hex
eth_getBlockByNumber/Hash [id, withTx] makeRPCCallMap?
eth_getTransactionByHash/Receipt [hash] makeRPCCallMap?
eth_getCode/StorageAt/TransactionCount [...] getCode/getStorage/getTransactionCount or RPC fallback
eth_gasPrice / eth_estimateGas []/[tx] hex; estimate buffered +20% via BigInt
personal_sign [hexMsg, addr, pw?] dialog shows decoded UTF-8, signs with signPersonalMessageToUint8List
eth_sign [addr, hexMsg] raw keccak256
eth_signTypedData[*_v1/v3/v4] [addr, typed] EIP-712 with domain/type validation
wallet_switchEthereumChain [{chainId}] idempotent, dialog if needed → chainChanged
wallet_addEthereumChain [NetworkConfig] dialog → add + auto-switch
wallet_getPermissions [] ['eth_accounts','eth_chainId','personal_sign']
wallet_revokePermissions [] true
other throws UnsupportedMethodException(4200)

Unknown method surfaces to JS as {code:4200, message} and to onError.

Error codes (lib/exceptions.dart:2) #

WalletException.code follows EIP-1193 + JSON-RPC:

  • 4001 – user rejected
  • 4100 – unauthorized
  • 4200 – unsupported method
  • 4900 – disconnected
  • -32601 – method not found
  • -32602 – invalid params
  • -32603 – internal

Web3WebView (lib/web3_webview_eip1193.dart:887) maps WalletException.code into the thrown {code,message} consumed by provider_script.dart:127 _processError.


Permissions & WebView #

  • onPermissionRequest defaults to DENY (lib/web3_webview_eip1193.dart:1019). Override Web3WebView.onPermissionRequest to grant selectively, never GRANT all.
  • shouldInterceptRequest/onReceivedServerTrustAuthRequest are forwarded; implement cert pinning in host if needed.
  • Provider injected as two UserScript at AT_DOCUMENT_START; on Android re-evaluated in onLoadStart.

Signing details (lib/signing/signing_handler.dart:1) #

  • personal_sign accepts 0x hex or UTF-8, decodes for UI, signs prefixed.
  • eth_sign signs keccak256(hexOrUtf8) directly (dangerous – dialog still shown).
  • Typed data: types must contain EIP712Domain; checks primaryType, domain whitelist (name,version,chainId,verifyingContract,salt), circular deps, array lengths, and ABI 32-byte padding for bool/address/bytesN/uint/int.

Transaction details (lib/transaction/transaction_handler.dart:1) #

  • Requires to (0x 42 chars), validates value/data as 0x hex.
  • getTransactionCount(from) as nonce, estimateGas +20% with BigInt (*120/100), getGasPrice() or gasPrice override.
  • handleTransaction({waitForConfirmation=false}) – default returns hash; set true to block up to 30×10s polling and throw on receipt.status==false or timeout.

Theming #

WalletDialogTheme(
  primaryColor: Color(0xFF3B82F6),
  textColor: Color(0xFF1F2937),
  borderRadius: 16,
  dialogPadding: EdgeInsets.all(24),
  buttonConfirmStyle: ButtonConfig(backgroundColor: ...),
)

Passed via Web3WalletConfig.dialogTheme, consumed by WalletDialogService (lib/ethereum/wallet_dialog_service.dart:1).


Security #

  • Private key: never log, never commit, store in flutter_secure_storage with biometrics, wipe on logout, consider external signer (WalletConnect) instead of embedding keys. EthereumProvider.initialize (lib/ethereum/ethereum_provider.dart:57) validates format; Web3WebView shows error, not WebView, on failure. Clipboard now uses flutter/services without logging (lib/utils/app_utils.dart:5).
  • XSS: all JS interpolation uses jsonEncode (lib/provider/provider_script.dart:13, lib/web3_js_bridge_callback.dart:5, lib/ethereum/ethereum_provider.dart:817).
  • Origin: dialogs display controller.getUrl().host; consider allowlist / phishing check before showConnectWallet.
  • RPC: rpcUrls.first is used; use HTTPS + API key, pin certs via onReceivedServerTrustAuthRequest.
  • Singleton: EthereumProvider is a singleton; multiple Web3WebView share state – avoid mounting two simultaneously, call dispose() (lib/ethereum/ethereum_provider.dart:245) on screen dispose (already done in Web3WebView).

Limitations #

  • Singleton EthereumProvider – not multi-account, not multi-chain concurrent.
  • ethers.min.js (464 KB) bundled at packages/web3_webview/assets/ethers.min.js, injected twice.
  • No eth_subscribe/eth_getLogs polling; unsupported methods throw 4200.
  • LoadingHelper is global ref-counted overlay (lib/utils/loading.dart:3).

Changelog #

See CHANGELOG.md. 1.0.9 fixes validations, XSS, permission default, non-blocking tx, EIP-712 padding, clipboard/log leaks, and removes clipboard/hex unused deps.


License & Thanks #

  • License: see LICENSE (BSD).
  • Thanks: PositionExchange/flutter-web3-provider, ethers.js v6 docs.
7
likes
0
points
62
downloads

Publisher

verified publisherdatit309.is-a.dev

Weekly Downloads

web3_webview is a powerful bridge between a DApp running inside a WebView and your Flutter application, enabling secure and seamless two-way communication between them

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

crypto, flutter, flutter_inappwebview, hex, http, intl, modal_bottom_sheet, uuid, web3dart

More

Packages that depend on web3_webview