Ethereum

Ethereum

Dart / Flutter

Web3Dart is the library Dart and Flutter developers reach for when they need to talk to Ethereum. One codebase ships an Ethereum wallet or dApp to iOS, Android, web, and desktop. Add it with flutter pub add web3dart, or dart pub add web3dart for a server-side or CLI Dart project with no Flutter dependency. One rule overrides everything else: a private key controls real ETH on chain 1, so keep it in flutter_secure_storage (Keychain on iOS, Keystore on Android) and never commit it or bake it into the binary.

Web3Dart runs in both Flutter apps and plain Dart projects, so flutter pub add web3dart covers the mobile case and the same package serves a headless Dart backend. For anything that signs Ethereum transactions, store the private key in flutter_secure_storage — a hardcoded key in source is a hardcoded key in everyone's APK.

Web3Dart Library

Web3Client takes two things: the Ethereum HTTP endpoint and a Dart http.Client() it uses for transport. getBalance returns an EtherAmount, which carries wei internally and converts to ether on demand, so you never juggle 18 decimal places by hand. For transactions that move ETH or hit a busy contract, set an explicit maxGas rather than leaning on auto-estimation — gas estimation runs an eth_estimateGas simulation that can fail or undershoot on Ethereum when a contract path is conditional, and a too-low limit reverts the transaction while still burning gas.

import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart';
class EthereumService {
final Web3Client client;
EthereumService() : client = Web3Client('https://ethereum.therpc.io/YOUR_API_KEY', Client());
Future<EtherAmount> getBalance(String address) async {
final addr = EthereumAddress.fromHex(address);
return await client.getBalance(addr);
}
Future<String> sendTransaction({
required Credentials credentials,
required String to,
required BigInt amount,
}) async {
final transaction = await client.sendTransaction(
credentials,
Transaction(
to: EthereumAddress.fromHex(to),
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
),
);
return transaction;
}
}
  • GitHub: https://github.com/simolus3/web3dart
  • Pub.dev: https://pub.dev/packages/web3dart
  • Cross-platform Flutter dApps from a single Dart codebase
  • Contract interaction and transaction signing for Ethereum
  • HD wallet derivation (BIP-39/44) for seed-phrase wallets
  • ENS resolution for .eth names
  • ERC-20 and ERC-721 token support, plus eth_estimateGas gas estimation

Flutter Integration Examples

A StatefulWidget holds the latest EtherAmount in its state and calls setState once the balance comes back, so the UI repaints with the new ETH value. The trap is Flutter's rebuild model: build can run many times a second during animation or scrolling, so never fire an RPC call from inside build. Trigger reads from a button, a pull-to-refresh, or a timer, and cache the result so a dozen rebuilds don't turn into a dozen requests against your CU budget.

import 'package:flutter/material.dart';
import 'package:web3dart/web3dart.dart';
class WalletWidget extends StatefulWidget {
@override
_WalletWidgetState createState() => _WalletWidgetState();
}
class _WalletWidgetState extends State<WalletWidget> {
final ethereum = EthereumService();
EtherAmount? balance;
Future<void> updateBalance() async {
final newBalance = await ethereum.getBalance('YOUR_ADDRESS');
setState(() {
balance = newBalance;
});
}
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Text('Balance: ${balance?.getValueInUnit(EtherUnit.ether)} ETH'),
ElevatedButton(
onPressed: updateBalance,
child: Text('Refresh Balance'),
),
],
),
),
);
}
}

Smart Contract Integration

Build a DeployedContract from the ABI JSON and the address it lives at on Ethereum, then look up individual functions by name. Reads go through client.call() — an eth_call that returns the decoded result without spending gas, ideal for an ERC-20 balanceOf or a totalSupply. Anything that changes state goes through client.sendTransaction() with credentials, which signs and broadcasts, then hands you the transaction hash to watch for a receipt.

class SmartContractService {
final Web3Client client;
final DeployedContract contract;
SmartContractService(String contractAddress, String abi)
: client = Web3Client('https://ethereum.therpc.io/YOUR_API_KEY', Client()),
contract = DeployedContract(
ContractAbi.fromJson(abi, 'YourContract'),
EthereumAddress.fromHex(contractAddress),
);
Future<List<dynamic>> callFunction(
String functionName,
List<dynamic> params,
) async {
final function = contract.function(functionName);
final result = await client.call(
contract: contract,
function: function,
params: params,
);
return result;
}
}

Error Handling and Utilities

Web3Dart throws RPCError and transport exceptions straight from the network layer, which is noisy to handle at the UI. Catch them and rethrow a domain-specific EthereumException so the rest of your app pattern-matches one type instead of several library internals. A small extension on EtherAmount is the other half: it gives you a formatEther helper that rounds to a fixed number of decimals, so a balance reads as 1.2345 ETH in the UI rather than a raw wei integer.

class EthereumException implements Exception {
final String message;
final dynamic originalError;
EthereumException(this.message, [this.originalError]);
@override
String toString() => 'EthereumException: $message';
}
extension EtherAmountExtension on EtherAmount {
String formatEther([int decimals = 4]) {
return getValueInUnit(EtherUnit.ether).toStringAsFixed(decimals);
}
}

Ready to call this in production?

Free tier covers personal projects. Pay-as-you-go scales without a card.