This example demonstrates how to integrate MetaMask Embedded Wallets (formerly Web3Auth Plug and Play) into a Flutter application. It provides a simple yet comprehensive example of implementing social authentication and blockchain functionality in a cross-platform Flutter app.
- Social Authentication: Google, Facebook, Twitter, Discord, Apple, and more
- EVM Wallet: Automatic Ethereum wallet creation and management
- Blockchain Interactions: Basic blockchain operations using web3dart
- Secure Key Management: Non-custodial key management using Shamir Secret Sharing
- Cross-Platform: Single codebase for iOS and Android
- Material Design UI: Clean, modern interface components
This example uses web3auth_flutter ^8.0.0. See the root README.
- iOS 15, native Web3Auth from Swift Package Manager
sessionTimecomes from the dashboard (not set in code)- Post-login actions include
getAccessToken(),refreshSession(), andrequest("personal_sign", ...) - Google and email passwordless use
AuthConnection
- Flutter: 3.0.0 or higher
- Dart: 2.18.0 or higher
- iOS (for iOS development):
- iOS 15+
- Xcode 12+
- CocoaPods
- Swift 5.x
- Android (for Android development):
- API level 26+
- Android Studio
- JDK 11+
- compileSdkVersion 34
- MetaMask Embedded Wallets: Dashboard account
-
Clone the repository:
git clone https://github.com/Web3Auth/web3auth-flutter-examples.git cd web3auth-flutter-examples/flutter-quick-start -
Install dependencies:
flutter pub get
-
iOS Setup (for iOS development):
cd ios && pod install && cd ..
-
Create a project on the Embedded Wallets Dashboard
-
Choose your network:
- Sapphire Devnet: For development and testing (allows localhost)
- Sapphire Mainnet: For production (does NOT allow localhost)
-
Configure platform settings:
- iOS: Allowlist
{bundleId}://auth(e.g.,com.example.w3aflutter://auth) - Android: Allowlist your package name and configure deep link scheme
- iOS: Allowlist
-
Get your Client ID from the dashboard
Update the configuration in lib/main.dart:
import 'package:web3auth_flutter/web3auth_flutter.dart';
import 'package:web3auth_flutter/enums.dart';
import 'dart:io';
Future<void> initWeb3Auth() async {
String redirectUrl;
if (Platform.isAndroid) {
redirectUrl = 'w3a://com.example.w3aflutter';
} else if (Platform.isIOS) {
redirectUrl = 'com.example.w3aflutter://auth';
} else {
throw UnKnownException('Unknown platform');
}
await Web3AuthFlutter.init(
Web3AuthOptions(
clientId: "YOUR_WEB3AUTH_CLIENT_ID", // Get from dashboard
web3AuthNetwork: Web3AuthNetwork.sapphire_mainnet, // or sapphire_devnet
redirectUrl: redirectUrl,
),
);
await Web3AuthFlutter.initialize();
}-
Update compileSdkVersion in
android/app/build.gradle:android { compileSdkVersion 34 // ... } -
Add JitPack repository in
android/settings.gradle:dependencyResolutionManagement { repositories { google() mavenCentral() maven { url "https://jitpack.io" } } } -
Configure deep link in
android/app/src/main/AndroidManifest.xml:<manifest> <uses-permission android:name="android.permission.INTERNET" /> <application> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="w3a" android:host="com.example.w3aflutter" /> </intent-filter> </activity> </application> </manifest>
-
Handle custom tabs lifecycle - Add to your login screen widget:
class _LoginScreenState extends State<LoginScreen> with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { Web3AuthFlutter.setCustomTabsClosed(); } } }
-
Set minimum iOS version in
ios/Podfile:platform :ios, '14.0'
-
Configure URL scheme in
ios/Runner/Info.plist:<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLSchemes</key> <array> <string>com.example.w3aflutter</string> </array> </dict> </array>
-
Allowlist the redirect URL in your dashboard:
com.example.w3aflutter://auth
# Run in debug mode
flutter run
# Build for release
flutter build ios # For iOS
flutter build apk # For Androidlib/
βββ main.dart # Entry point & Web3Auth initialization
βββ services/
β βββ blockchain.dart # Blockchain operations
βββ screens/
βββ home.dart # Home screen with wallet info
βββ login.dart # Login screen
import 'package:web3auth_flutter/web3auth_flutter.dart';
Future<void> initWeb3Auth() async {
String redirectUrl;
if (Platform.isAndroid) {
redirectUrl = 'w3a://com.example.w3aflutter/auth';
} else if (Platform.isIOS) {
redirectUrl = 'com.example.w3aflutter://auth';
}
await Web3AuthFlutter.init(
Web3AuthOptions(
clientId: "YOUR_CLIENT_ID",
web3AuthNetwork: Web3AuthNetwork.sapphire_mainnet,
redirectUrl: redirectUrl,
)
);
// Initialize and check for existing session
await Web3AuthFlutter.initialize();
}// Login with Google
Future<void> login() async {
try {
final Web3AuthResponse response = await Web3AuthFlutter.connectTo(
LoginParams(
authConnection: AuthConnection.google,
mfaLevel: MFALevel.NONE,
),
);
final UserInfo? userInfo = response.userInfo;
print('User email: ${userInfo?.email}');
} catch (e) {
print('Login error: $e');
}
}
// Logout
Future<void> logout() async {
await Web3AuthFlutter.logout();
}
// Check existing session
Future<void> checkSession() async {
try {
final privateKey = await Web3AuthFlutter.getPrivateKey();
if (privateKey.isNotEmpty) {
// User has an active session
}
} catch (e) {
// No active session
}
}import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart';
// Get private key
final privateKey = await Web3AuthFlutter.getPrivateKey();
// Create credentials
final credentials = EthPrivateKey.fromHex(privateKey);
// Get wallet address
final address = credentials.address;
// Create Web3Client
final client = Web3Client(
'https://rpc.ankr.com/eth', // Your RPC URL
Client(),
);
// Get balance
final balance = await client.getBalance(address);
final ethBalance = balance.getValueInUnit(EtherUnit.ether);
// Send transaction
final txHash = await client.sendTransaction(
credentials,
Transaction(
to: EthereumAddress.fromHex('0x...'),
value: EtherAmount.fromUnitAndValue(EtherUnit.ether, 0.01),
),
chainId: 1, // Ethereum mainnet
);- Non-Custodial: Private keys are managed using Shamir Secret Sharing - no single party (including MetaMask/Web3Auth) holds the full key
- Deterministic Wallets: Same user + same config = same wallet address
- Network Consistency: Never change Client ID or Sapphire network in production
- Secure Storage: Session data is securely stored on device
- OAuth Best Practices: Social login credentials handled through standard OAuth flows
Problem: Compilation fails or dependencies conflict
Solution:
flutter clean
flutter pub get
cd ios && pod install && cd .. # For iOS
flutter runProblem: Login flow doesn't start or fails
Solutions:
- Verify Client ID is correct in code
- Check that redirect URLs are allowlisted in dashboard
- Ensure network setting matches your dashboard project (devnet vs mainnet)
- For Android: Verify deep link intent filter in
AndroidManifest.xml - For iOS: Check URL scheme in
Info.plist
Android:
- Verify scheme and host in
AndroidManifest.xmlmatch your code - Check package name is allowlisted in dashboard
- Ensure
INTERNETpermission is added
iOS:
- Verify URL scheme in
Info.plistmatches bundle ID - Check that
{bundleId}://authis allowlisted in dashboard - Ensure app handles URL scheme correctly
Problem: User session not persisting or being restored
Solution:
// Always call initialize() after init()
await Web3AuthFlutter.init(...);
await Web3AuthFlutter.initialize(); // This checks for existing session
// Check for active session
try {
final privateKey = await Web3AuthFlutter.getPrivateKey();
if (privateKey.isNotEmpty) {
// Session exists
}
} catch (e) {
// No active session
}Need help? Reach out through:
This example is available under the MIT License. See the LICENSE file for more info.