BNB Smart Chain

BNB Smart Chain

Dart / Flutter

Web3Dart is the library of record for reaching BNB Smart Chain from the Dart world. Flutter compiles one codebase to four runtime families: iOS, Android, the web, plus desktop. A single integration can therefore power a chain-56 dApp across every platform Flutter targets. Add it to a Flutter app with flutter pub add web3dart, or dart pub add web3dart in a pure-Dart project. One non-negotiable for shipping: store private keys in flutter_secure_storage, backed by the platform Keychain or Keystore — never bake a key into source or shared preferences.

The package works equally well in Flutter apps and headless Dart programs, so a CLI tool and a mobile wallet can share the same BSC logic. Pull it in via flutter pub add web3dart, and treat key management as a security boundary from day one: production builds must keep private keys in flutter_secure_storage and never hardcode them anywhere in the bundle.

Web3Dart Library

Build a client by pairing the BSC endpoint with an HTTP transport: Web3Client("https://bsc.therpc.io/YOUR_API_KEY", http.Client()). That Web3Client is your connection to chain 56 for balance reads, contract calls, plus transaction submission. When you move from prototype to production, set an explicit maxGas on outgoing transactions rather than relying on auto-estimation — estimation can fail or under-shoot on BSC, and a hardcoded ceiling keeps a transfer from silently stalling.

import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart';
class EthereumService {
final Web3Client client;
EthereumService() : client = Web3Client('https://bsc.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;
}
}

Flutter Integration Examples

A StatefulWidget is the simplest way to surface a live BNB balance in Flutter: hold the value in state and call setState once the chain-56 query resolves so the widget rebuilds with fresh data. The pitfall to avoid is firing an RPC call inside build, which Flutter may invoke many times per second — cache the last result and debounce refreshes so you query the BSC endpoint on intent, not on every repaint.

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)} BNB'),
ElevatedButton(
onPressed: updateBalance,
child: Text('Refresh Balance'),
),
],
),
),
);
}
}

Smart Contract Integration

Contract work starts by constructing a DeployedContract from its ABI JSON string and on-chain address, for example a BEP-20 token deployed on chain 56. With that handle, the two operations split by intent: read-only functions go through client.call(), which returns decoded values without costing gas, while state-changing functions are dispatched with client.sendTransaction() using a signed Transaction so BNB Smart Chain records the change.

class SmartContractService {
final Web3Client client;
final DeployedContract contract;
SmartContractService(String contractAddress, String abi)
: client = Web3Client('https://bsc.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

Raw Web3Dart exceptions are low-level, so wrap them in a domain-specific EthereumException at your service boundary — that gives the UI a single, predictable type to catch when a chain-56 call fails and keeps RPC plumbing from leaking into widgets. While you're shaping the presentation layer, an extension on EtherAmount that formats wei into a trimmed, human-friendly BNB string saves repeating the same conversion logic across every screen.

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.