Wallet Access
userWallets
Get the current list of user wallets (synchronous).external List<BaseWallet> get userWallets;
Example
final wallets = sdk.wallets.userWallets;
for (final wallet in wallets) {
print('${wallet.address} (${wallet.chain})');
}
userWalletsChanges
Observe wallet list changes (reactive stream).external Stream<List<BaseWallet>> get userWalletsChanges;
Example
sdk.wallets.userWalletsChanges.listen((wallets) {
// Handle wallet changes
setState(() {
_wallets = wallets;
});
});
StreamBuilder Example
StreamBuilder<List<BaseWallet>>(
stream: sdk.wallets.userWalletsChanges,
initialData: sdk.wallets.userWallets,
builder: (context, snapshot) {
final wallets = snapshot.data ?? [];
if (wallets.isEmpty) {
return Text('No wallets found');
}
return ListView.builder(
itemCount: wallets.length,
itemBuilder: (context, index) {
final wallet = wallets[index];
return ListTile(
title: Text(wallet.address),
subtitle: Text(wallet.chain),
trailing: Text(wallet.chain),
);
},
);
},
);
Balance Operations
getBalance
Get the balance for a specific wallet.external Future<String> getBalance({required BaseWallet wallet});
Parameters
- wallet (BaseWallet): The wallet to check balance for
Returns
- String: Balance as a string (in native token units)
Example
try {
final balance = await sdk.wallets.getBalance(wallet: wallet);
print('Balance: $balance');
} catch (e) {
print('Failed to get balance: $e');
}
FutureBuilder Example
FutureBuilder<String>(
future: sdk.wallets.getBalance(wallet: wallet),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Balance: ${snapshot.data}');
},
);
Network Operations
getNetwork
Get the current network for a wallet.external Future<Network> getNetwork({required BaseWallet wallet});
Parameters
- wallet (BaseWallet): The wallet to get network for
Returns
- Network: The current network
Example
try {
final network = await sdk.wallets.getNetwork(wallet: wallet);
print('Network: ${network.value}');
} catch (e) {
print('Failed to get network: $e');
}
switchNetwork
Switch a wallet to a different network.external Future<void> switchNetwork({
required BaseWallet wallet,
required Network network,
});
Parameters
- wallet (BaseWallet): The wallet to switch networks for
- network (Network): The target network
Example
try {
final polygon = sdk.networks.evm.firstWhere((n) => n.chainId == 137);
await sdk.wallets.switchNetwork(
wallet: wallet,
network: Network(polygon.chainId),
);
print('Switched to ${polygon.name}');
} catch (e) {
print('Failed to switch network: $e');
}
Complete Example with Network Selector
class NetworkSwitcher extends StatefulWidget {
final BaseWallet wallet;
NetworkSwitcher({required this.wallet});
@override
_NetworkSwitcherState createState() => _NetworkSwitcherState();
}
class _NetworkSwitcherState extends State<NetworkSwitcher> {
final sdk = DynamicSDK.instance;
Network? currentNetwork;
bool isLoading = false;
@override
void initState() {
super.initState();
loadCurrentNetwork();
}
Future<void> loadCurrentNetwork() async {
try {
final network = await sdk.wallets.getNetwork(wallet: widget.wallet);
setState(() {
currentNetwork = network;
});
} catch (e) {
print('Failed to load network: $e');
}
}
Future<void> switchNetwork(GenericNetwork network) async {
setState(() => isLoading = true);
try {
await sdk.wallets.switchNetwork(
wallet: widget.wallet,
network: Network(network.chainId),
);
setState(() {
currentNetwork = Network(network.chainId);
isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Switched to ${network.name}')),
);
} catch (e) {
setState(() => isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to switch: $e')),
);
}
}
@override
Widget build(BuildContext context) {
final networks = widget.wallet.chain == 'EVM'
? sdk.networks.evm
: sdk.networks.solana;
return Column(
children: [
if (currentNetwork != null)
Text('Current: ${currentNetwork!.value}'),
SizedBox(height: 16),
if (isLoading)
CircularProgressIndicator()
else
DropdownButton<GenericNetwork>(
items: networks.map((network) {
return DropdownMenuItem(
value: network,
child: Text(network.name),
);
}).toList(),
onChanged: (network) {
if (network != null) {
switchNetwork(network);
}
},
),
],
);
}
}
Message Signing
signMessage
Sign a message with a wallet.external Future<String> signMessage({
required BaseWallet wallet,
required String message,
});
Parameters
- wallet (BaseWallet): The wallet to sign with
- message (String): The message to sign
Returns
- String: The signature
Example
try {
final signature = await sdk.wallets.signMessage(
wallet: wallet,
message: 'Hello!',
);
print('Signature: $signature');
} catch (e) {
print('Failed to sign: $e');
}
Complete Example
class MessageSigner extends StatefulWidget {
final BaseWallet wallet;
MessageSigner({required this.wallet});
@override
_MessageSignerState createState() => _MessageSignerState();
}
class _MessageSignerState extends State<MessageSigner> {
final sdk = DynamicSDK.instance;
final messageController = TextEditingController();
String? signature;
bool isLoading = false;
Future<void> signMessage() async {
setState(() => isLoading = true);
try {
final sig = await sdk.wallets.signMessage(
wallet: widget.wallet,
message: messageController.text,
);
setState(() {
signature = sig;
isLoading = false;
});
} catch (e) {
setState(() => isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to sign: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: messageController,
decoration: InputDecoration(
labelText: 'Message to sign',
hintText: 'Enter your message',
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: isLoading ? null : signMessage,
child: isLoading
? CircularProgressIndicator()
: Text('Sign Message'),
),
if (signature != null) ...[
SizedBox(height: 16),
Text('Signature:'),
SelectableText(
signature!,
style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
),
],
],
);
}
@override
void dispose() {
messageController.dispose();
super.dispose();
}
}
Typed Data Signing (EIP-712)
signTypedData
Sign typed data (EIP-712) with a wallet.external Future<String> signTypedData({
required String walletId,
required TypedData typedData,
});
Parameters
- walletId (String): The ID of the embedded wallet to sign with
- typedData (TypedData): The typed data (from
dynamic_sdk_web3dart)
Returns
- String: The signature
Example
import 'dart:convert';
import 'package:dynamic_sdk_web3dart/dynamic_sdk_web3dart.dart';
const typedDataJson = '''
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "chainId", "type": "uint256"}
],
"Person": [
{"name": "name", "type": "string"}
]
},
"primaryType": "Person",
"domain": {
"name": "My App",
"chainId": 1
},
"message": {
"name": "Alice"
}
}
''';
try {
final typedData = TypedData.fromJson(jsonDecode(typedDataJson));
final signature = await sdk.wallets.signTypedData(
walletId: wallet.id,
typedData: typedData,
);
print('Signature: $signature');
} catch (e) {
print('Failed to sign: $e');
}
Complete Example
import 'dart:convert';
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:dynamic_sdk_web3dart/dynamic_sdk_web3dart.dart';
import 'package:flutter/material.dart';
class TypedDataSigner extends StatefulWidget {
final BaseWallet wallet;
TypedDataSigner({required this.wallet});
@override
_TypedDataSignerState createState() => _TypedDataSignerState();
}
class _TypedDataSignerState extends State<TypedDataSigner> {
final sdk = DynamicSDK.instance;
String? signature;
bool isLoading = false;
Future<void> signTypedData() async {
setState(() => isLoading = true);
final typedDataJson = '''
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"}
],
"Person": [
{"name": "name", "type": "string"},
{"name": "wallet", "type": "address"}
]
},
"primaryType": "Person",
"domain": {
"name": "My DApp",
"version": "1",
"chainId": 1
},
"message": {
"name": "Alice",
"wallet": "${widget.wallet.address}"
}
}
''';
try {
final typedData = TypedData.fromJson(jsonDecode(typedDataJson));
final sig = await sdk.wallets.signTypedData(
walletId: widget.wallet.id,
typedData: typedData,
);
setState(() {
signature = sig;
isLoading = false;
});
} catch (e) {
setState(() => isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to sign: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Sign Typed Data (EIP-712)'),
SizedBox(height: 16),
ElevatedButton(
onPressed: isLoading ? null : signTypedData,
child: isLoading
? CircularProgressIndicator()
: Text('Sign Typed Data'),
),
if (signature != null) ...[
SizedBox(height: 16),
Text('Signature:'),
SelectableText(
signature!,
style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
),
],
],
);
}
}
Primary Wallet Management
setPrimary
Set a wallet as the primary wallet for the user.external Future<void> setPrimary({required String walletId});
Parameters
- walletId (String): The ID of the wallet to set as primary
Example
try {
final walletId = wallet.id;
if (walletId != null) {
await sdk.wallets.setPrimary(walletId: walletId);
print('Set wallet as primary');
} else {
print('Wallet ID is null');
}
} catch (e) {
print('Failed to set primary: $e');
}
Complete Example with Wallet List
class WalletListWithPrimary extends StatefulWidget {
@override
_WalletListWithPrimaryState createState() => _WalletListWithPrimaryState();
}
class _WalletListWithPrimaryState extends State<WalletListWithPrimary> {
final sdk = DynamicSDK.instance;
Future<void> setPrimaryWallet(BaseWallet wallet) async {
final walletId = wallet.id;
if (walletId == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Wallet ID is null')),
);
return;
}
try {
await sdk.wallets.setPrimary(walletId: walletId);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Set ${wallet.address} as primary')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to set primary: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return StreamBuilder<List<BaseWallet>>(
stream: sdk.wallets.userWalletsChanges,
initialData: sdk.wallets.userWallets,
builder: (context, snapshot) {
final wallets = snapshot.data ?? [];
if (wallets.isEmpty) {
return Center(child: Text('No wallets found'));
}
return ListView.builder(
itemCount: wallets.length,
itemBuilder: (context, index) {
final wallet = wallets[index];
return Card(
child: ListTile(
title: Text(wallet.address),
subtitle: Text(
wallet.chain,
),
trailing: IconButton(
icon: Icon(Icons.star_border),
onPressed: () => setPrimaryWallet(wallet),
tooltip: 'Set as primary',
),
),
);
},
);
},
);
}
}
Complete Wallet Management Screen
class WalletManagementScreen extends StatelessWidget {
final sdk = DynamicSDK.instance;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Wallet Management'),
),
body: StreamBuilder<List<BaseWallet>>(
stream: sdk.wallets.userWalletsChanges,
initialData: sdk.wallets.userWallets,
builder: (context, snapshot) {
final wallets = snapshot.data ?? [];
if (wallets.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.wallet, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('No wallets found'),
],
),
);
}
return ListView.builder(
itemCount: wallets.length,
itemBuilder: (context, index) {
final wallet = wallets[index];
return WalletCard(wallet: wallet);
},
);
},
),
);
}
}
class WalletCard extends StatelessWidget {
final BaseWallet wallet;
final sdk = DynamicSDK.instance;
WalletCard({required this.wallet});
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(8),
child: ExpansionTile(
title: Text(
wallet.address.length > 20
? '${wallet.address.substring(0, 10)}...${wallet.address.substring(wallet.address.length - 8)}'
: wallet.address,
),
subtitle: Text(wallet.chain),
children: [
Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FutureBuilder<String>(
future: sdk.wallets.getBalance(wallet: wallet),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Balance: Loading...');
}
if (snapshot.hasError) {
return Text('Balance: Error');
}
return Text('Balance: ${snapshot.data}');
},
),
SizedBox(height: 8),
FutureBuilder<Network>(
future: sdk.wallets.getNetwork(wallet: wallet),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Network: Loading...');
}
if (snapshot.hasError) {
return Text('Network: Error');
}
return Text('Network: ${snapshot.data?.value}');
},
),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () async {
final walletId = wallet.id;
if (walletId != null) {
try {
await sdk.wallets.setPrimary(walletId: walletId);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Set as primary')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed: $e')),
);
}
}
},
child: Text('Set Primary'),
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MessageSigner(wallet: wallet),
),
);
},
child: Text('Sign Message'),
),
],
),
],
),
),
],
),
);
}
}