Web3Dart is the main library for reaching OP Mainnet from Dart, and it lets you ship one codebase to iOS, Android, and the web from a single Flutter project. Because OP Mainnet is EVM-equivalent (chain ID 10, ETH for gas), Web3Dart's standard JSON-RPC calls work against it without any chain-specific tweaks. Add it with flutter pub add web3dart in a Flutter app, or dart pub add web3dart for pure Dart. In production, store private keys in flutter_secure_storage (backed by the iOS Keychain and Android Keystore) — never hardcode a key in source.
Web3Dart runs in both Flutter and plain Dart projects, so the same OP Mainnet logic powers a mobile app or a server-side script. Add it with flutter pub add web3dart. For any production build, manage private keys through flutter_secure_storage rather than committing them — a leaked key on a chain where ETH is real value is unrecoverable.
Web3Dart Library
Create a Web3Client by passing the OP Mainnet HTTP endpoint and an http.Client() instance — that pair handles all the request plumbing. For production transactions, set an explicit gas limit instead of relying on auto-estimation: estimation can fail or under-shoot on contract-heavy calls, and OP Mainnet's transaction fee combines an L2 execution cost with an L1 data cost, so you want predictable, sufficient limits before signing.
Key features: cross-platform Flutter mobile dApps on OP Mainnet, contract interaction, transaction signing, HD wallet support (BIP-39/44), ENS resolution, ERC-20 and ERC-721 helpers, and gas estimation
Flutter Integration Examples
A StatefulWidget is the natural fit for showing a live OP Mainnet balance: fetch the value, then call setState to redraw the widget with the new number. Just don't fire an RPC call from inside build() — that method runs on every rebuild and would hammer the endpoint. Trigger fetches from explicit actions (a button, initState, or a timer) and cache or debounce the result so a fast scroll or repeated rebuild reuses the last value.
To call a contract deployed on OP Mainnet, build a DeployedContract from its ABI JSON string and its on-chain address. Read-only views go through client.call() — no gas, no signature, instant result. State-changing methods go through client.sendTransaction() with signed credentials, which broadcasts an ETH-paid transaction and returns its hash.
Raw Web3Dart exceptions can be noisy, so wrap them in a domain-specific EthereumException that carries a clean message and the original error — your UI layer then catches one predictable type instead of guessing at network internals. An EtherAmount extension is also worth adding: it converts wei into a fixed-decimal display string so the OP Mainnet balance reads as a tidy ETH figure rather than a raw integer.