【Flutter】アプリをMetamaskウォレットと接続させてみた
はじめに
今回は、Flutter を用いてモバイルアプリと Metamask の接続を行う方法について記載をしていきます。
今回の実装にあたって以下の記事を参考にしてみました。
こちらも併せてご覧下さい。
完成イメージ
動作自体はシンプルでアプリ側にてボタンを押すと Metamask へ遷移し、接続をユーザーに依頼する。
その後アプリ側では
- パブリックアドレスの取得
- パブリックアドレスを用いた処理
- Metamask とコネクション確立時・切断時に処理を走らせる
といったことが出来る流れになります。
実装
Flutter のコマンドに関しては本記事で言及したい内容ではないので特に触れません。
$ flutter create connect_to_metamask
プロジェクトを作成した後は以下のコマンドを実行します。
$ flutter pub add url_launcher walletconnect_dart
それぞれのパッケージのリポジトリは以下になります。
-
url_launcher
- url から Metamask のアプリを起動する際に使用
-
walletconnect_dart
- walletConnect とのセッションを作成し、Metamask と接続をする際に使用
問題なくパッケージの追加まで出来たらコードを書いていきます。
lib/
に main.dart
ファイルがあるので、以下の様に修正します。
import 'package:flutter/material.dart';
import 'package:connect_to_metamask/pages/login_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: "/login",
routes: {
"/login": (context) => const LoginPage(),
},
);
}
}
今回はログインページを用意してそこで Metamask と接続を行うケースを想定しています。そして他のページは設けないため、main.dart
ではまず /login
を表示させるようにしましょう。
この時点では存在していない /pages/login_page.dart
を読み込んでいますが、今から作成しますので一旦無視しておいてください。
次は lib/
配下に pages
というディレクトリを作って、その配下に login_page.dart
を作成します。
中身は以下の様な形です。
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:walletconnect_dart/walletconnect_dart.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
late SessionStatus _session;
late Uri _uri;
// connector を作成する関数
WalletConnect createConnector() {
return WalletConnect(
bridge: 'https://bridge.walletconnect.org',
clientMeta: const PeerMeta(
name: 'create_to_Metamask_App',
description: 'アプリの説明文です',
url: 'https://yamawo.info',
icons: [
'https://files.gitbook.com/v0/b/gitbook-legacy-files/o/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media'
]));
}
// ボタンが押された際に発火させる、walletConnect との session を確立する関数
Future<void> connectToMetamask() async {
final connector = createConnector();
// session が確立されていない時
if (!connector.connected) {
try {
final session =
await connector.createSession(onDisplayUri: (uri) async {
_uri = Uri.parse(uri);
// Metamask アプリを立ち上げる
await launchUrl(Uri.parse(uri));
});
setState(() {
_session = session;
});
} catch (e) {
print(e);
}
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login Page'),
),
body: Center(
child: SingleChildScrollView(
child: ElevatedButton(
onPressed: () => connectToMetamask(),
child: const Text("Connect with Metamask"),
style: ElevatedButton.styleFrom(
onPrimary: Colors.black,
),
),
),
),
);
}
}
少し説明をすると、
WalletConnect createConnector() {
return WalletConnect(
bridge: 'https://bridge.walletconnect.org',
clientMeta: const PeerMeta(
name: 'create_to_Metamask_App',
description: 'アプリの説明文です',
url: 'https://yamawo.info',
icons: [
'https://files.gitbook.com/v0/b/gitbook-legacy-files/o/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media'
]));
}
上記の関数は walletConnect とのコネクタを作成しています。
walletConnect の機能を用いて Metamask との接続を行なっていきます。
// ボタンが押された際に発火させる、walletConnect との session を確立する関数
Future<void> connectToMetamask() async {
final connector = createConnector();
// session が確立されていない時
if (!connector.connected) {
try {
final session =
await connector.createSession(onDisplayUri: (uri) async {
_uri = Uri.parse(uri);
// Metamask アプリを立ち上げる
await launchUrl(Uri.parse(uri));
});
setState(() {
_session = session;
});
} catch (e) {
print(e);
}
}
}
connectToMetamask()
は実際にボタンが押された際に走る処理になります。
流れとしては、まず初めに先ほどのcreateConnector()
でコネクタを作成して、connector.conncted
で接続が既に確立されているかの確認を行ないます。
確立されていなければcreateSession()
を実行し、引数で URI を生成時に実行される関数を渡します。
その後、生成された URI を launchUrl()
に渡して立ち上げ、ユーザーが Metamask 側でアプリとの接続をすると session が返され、session.accounts[0]
やsession.chainId
でユーザーの情報が取得出来る様になるという流れです。
これで Metamask とアプリでの接続は終わりになります。
他にも出来ること
walletconnect_dartパッケージの README を見ても分かりますが、「session を確立した時」や「session が更新された時」、「接続が切れた時」に特定の処理を走らせるということも可能そうです。
// session を確立した時
connector.on('connect', (session) => print(session));
// session が更新された時
connector.on('session_update', (payload) => print(payload));
// 接続が切れた時
connector.on('disconnect', (session) => print(session));
今回実装はしていませんが、例ではこのように各コネクタのイベントを subscribe してイベントが起きたときに走る様にしているみたいでした。
この他にもトランザクションの実装なども出来そうで活用すればモバイルアプリでもより多くの機能が実現できそうですね。
Discussion