web3_webview 1.0.14
web3_webview: ^1.0.14 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
import 'package:flutter/material.dart';
import 'package:web3_webview/web3_webview.dart';
/// Example app for `web3_webview` – 3 modes: read-only, privateKey, external signer.
///
/// Loads `privateKey` from secure storage in production – here placeholders.
/// Do NOT hardcode keys in real apps.
void main() => runApp(const ExampleApp());
/// Root widget with tab demo for 3 modes + dark preview.
class ExampleApp extends StatefulWidget {
const ExampleApp({super.key});
@override
State<ExampleApp> createState() => _ExampleAppState();
}
class _ExampleAppState extends State<ExampleApp> {
ThemeMode _mode = ThemeMode.light;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'web3_webview example',
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorSchemeSeed: const Color(0xFF6366F1)),
darkTheme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorSchemeSeed: const Color(0xFF818CF8)),
themeMode: _mode,
home: ExampleHome(
isDark: _mode == ThemeMode.dark,
onToggleDark: () => setState(() =>
_mode = _mode == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark),
),
);
}
}
class ExampleHome extends StatefulWidget {
final bool isDark;
final VoidCallback onToggleDark;
const ExampleHome(
{super.key, required this.isDark, required this.onToggleDark});
@override
State<ExampleHome> createState() => _ExampleHomeState();
}
class _ExampleHomeState extends State<ExampleHome> {
int _index = 0;
static final NetworkConfig _eth = 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'],
);
// Premium themes – auto dark via system, plus custom dark override
WalletDialogTheme get _lightTheme =>
WalletDialogTheme(primaryColor: const Color(0xFF6366F1));
WalletDialogTheme get _darkTheme =>
WalletDialogTheme.dark(primaryColor: const Color(0xFF818CF8));
// Fully custom UI – replace any dialog with your own widget (branding, layout, no code fork)
WalletDialogBuilders get _customBuilders => WalletDialogBuilders(
connect: (ctx,
{required address,
required host,
required appName,
required controller,
required theme}) async {
// Example: branded confirm with AlertDialog – you can use any widget
return showDialog<bool>(
context: ctx,
barrierDismissible: false,
builder: (c) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
title: Text('$appName muốn kết nối', style: theme.headerStyle),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Host: $host', style: theme.captionStyle),
const SizedBox(height: 8),
Text(
'Wallet: ${address.substring(0, 8)}...${address.substring(address.length - 6)}',
style: theme.valueStyle
.copyWith(fontFamily: 'monospace', fontSize: 12)),
const SizedBox(height: 12),
Text('Bạn có tin tưởng site này không?',
style: theme.captionStyle),
]),
actions: [
TextButton(
onPressed: () => Navigator.pop(c, false),
child: const Text('Từ chối')),
FilledButton(
onPressed: () => Navigator.pop(c, true),
style: FilledButton.styleFrom(
backgroundColor: theme.primaryColor),
child: const Text('Kết nối')),
],
),
);
},
sign: (ctx,
{required message,
required address,
required host,
required controller,
required theme}) async {
return showDialog<bool>(
context: ctx,
builder: (c) => AlertDialog(
title: const Text('Yêu cầu ký'),
content: SingleChildScrollView(
child: Text(message,
style: const TextStyle(
fontFamily: 'monospace', fontSize: 12))),
actions: [
TextButton(
onPressed: () => Navigator.pop(c, false),
child: const Text('Hủy')),
FilledButton(
onPressed: () => Navigator.pop(c, true),
child: const Text('Ký')),
],
),
);
},
transaction: (ctx,
{required txParams,
required host,
required controller,
required theme}) async {
final to = txParams['to']?.toString() ?? '—';
final value = txParams['value']?.toString() ?? '0';
return showDialog<bool>(
context: ctx,
builder: (c) => AlertDialog(
title: const Text('Xác nhận giao dịch'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Host: $host', style: theme.captionStyle),
const SizedBox(height: 8),
Text('To: $to',
style: theme.valueStyle
.copyWith(fontSize: 12, fontFamily: 'monospace')),
const SizedBox(height: 4),
Text('Value: $value', style: theme.valueStyle),
]),
actions: [
TextButton(
onPressed: () => Navigator.pop(c, false),
child: const Text('Từ chối')),
FilledButton(
onPressed: () => Navigator.pop(c, true),
child: const Text('Gửi')),
],
),
);
},
);
// Demo private key – NEVER hardcode in production, load from secure storage
static const _demoPk =
'0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('web3_webview 1.0.11 • premium + dark'),
actions: [
IconButton(
tooltip: widget.isDark ? 'Light mode' : 'Dark mode',
icon: Icon(widget.isDark
? Icons.light_mode_rounded
: Icons.dark_mode_rounded),
onPressed: widget.onToggleDark,
),
],
),
body: IndexedStack(
index: _index,
children: [
// 1. Read-only – no privateKey/signer, DApp still loads (auto dark)
Web3WebView(
web3WalletConfig: Web3WalletConfig(
currentNetwork: _eth,
supportNetworks: [_eth],
name: 'Read-only Demo',
id: 'com.example.readonly',
dialogTheme: _lightTheme,
darkDialogTheme: _darkTheme,
onError: (m, p, msg) => debugPrint('read-only onError $m: $msg'),
),
initialUrlRequest: URLRequest(
url: WebUri('https://metamask.github.io/test-dapp/')),
onPermissionRequest: (c, r) async => PermissionResponse(
resources: r.resources,
action: PermissionResponseAction.DENY,
),
),
// 2. Local private key – same themes, premium cards adapt to dark
Web3WebView(
web3WalletConfig: Web3WalletConfig(
privateKey: _demoPk,
currentNetwork: _eth,
supportNetworks: [_eth],
name: 'PrivateKey Demo',
id: 'com.example.privatekey',
dialogTheme: _lightTheme,
darkDialogTheme: _darkTheme,
onError: (m, p, msg) => debugPrint('pk onError $m: $msg'),
),
initialUrlRequest: URLRequest(
url: WebUri('https://metamask.github.io/test-dapp/')),
onPermissionRequest: (c, r) async => PermissionResponse(
resources: r.resources,
action: PermissionResponseAction.DENY,
),
),
// 3. External signer (WalletConnect etc.) – dark auto
Web3WebView(
web3WalletConfig: Web3WalletConfig(
signer: DemoSigner('0x1111111111111111111111111111111111111111'),
currentNetwork: _eth,
supportNetworks: [_eth],
name: 'External Signer Demo',
id: 'com.example.signer',
dialogTheme: _lightTheme,
darkDialogTheme: _darkTheme,
),
initialUrlRequest: URLRequest(
url: WebUri('https://metamask.github.io/test-dapp/')),
onPermissionRequest: (c, r) async => PermissionResponse(
resources: r.resources,
action: PermissionResponseAction.DENY,
),
),
// 4. Custom UI – override dialog completely to match your brand
Web3WebView(
web3WalletConfig: Web3WalletConfig(
privateKey: _demoPk,
currentNetwork: _eth,
supportNetworks: [_eth],
name: 'Custom UI Demo',
id: 'com.example.custom',
dialogTheme: _lightTheme,
darkDialogTheme: _darkTheme,
dialogBuilders: _customBuilders, // <-- your builders
onError: (m, p, msg) => debugPrint('custom onError $m: $msg'),
),
initialUrlRequest: URLRequest(
url: WebUri('https://metamask.github.io/test-dapp/')),
onPermissionRequest: (c, r) async => PermissionResponse(
resources: r.resources,
action: PermissionResponseAction.DENY,
),
),
],
),
bottomNavigationBar: NavigationBar(
selectedIndex: _index,
onDestinationSelected: (i) => setState(() => _index = i),
destinations: const [
NavigationDestination(
icon: Icon(Icons.visibility), label: 'Read-only'),
NavigationDestination(icon: Icon(Icons.key), label: 'PrivateKey'),
NavigationDestination(icon: Icon(Icons.wallet), label: 'Signer'),
NavigationDestination(
icon: Icon(Icons.brush_rounded), label: 'Custom'),
],
),
);
}
}
/// Minimal external signer demo – replace with WalletConnect / enclave logic.
class DemoSigner extends Web3Signer {
@override
final String address;
DemoSigner(this.address);
@override
Future<String> signMessage(
String method, String from, dynamic message, String password) async {
// TODO: delegate to WalletConnect, show your own UI, then return 0x signature
throw WalletException('DemoSigner: implement signMessage', code: 4200);
}
@override
Future<String> sendTransaction(Map<String, dynamic> txParams) async {
// TODO: delegate to WalletConnect
throw WalletException('DemoSigner: implement sendTransaction', code: 4200);
}
}