magic_flutter 1.1.5 magic_flutter: ^1.1.5 copied to clipboard
A Flutter plugin to integrate Magic SDK with native android and ios support
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:magic_flutter/magic_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Magic SDK Sample",
theme: ThemeData(primarySwatch: Colors.deepPurple),
home: InitializeApp(),
);
}
}
class InitializeApp extends StatelessWidget {
final publisherKey = "xxxxxxx";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Magic plugin sample"),
),
body: FutureBuilder(
future: Magic.initializeMagic(
publisherKey: publisherKey,
//providing testnet rpcURL and chain id
rpcURL: "your rpc url",
chainID: "xxxxx"),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return LoginPage();
} else {
print(snapshot.error);
return Container(
alignment: Alignment.center,
child: Text("Something went wrong. Failed to initialize Magic"),
);
}
} else
return Center(child: CircularProgressIndicator());
},
),
);
}
}
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final formKey = GlobalKey<FormState>();
bool isBusy = false;
final emailController = TextEditingController();
@override
void dispose() {
emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: isBusy
? Center(
child: Container(
child: Text('Please verify your mail',
style: TextStyle(
color: Colors.black, fontWeight: FontWeight.bold)),
))
: Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: emailController,
decoration: InputDecoration(
hintText: "Enter your email id",
),
validator: (value) {
if (!value!.isValidEmail) return "Enter a valid email";
return null;
},
),
SizedBox(height: 24),
ElevatedButton(
onPressed: () => login(context),
child: Text("LogIn"),
),
],
),
),
),
);
}
Future<void> login(BuildContext context) async {
if (formKey.currentState!.validate()) {
try {
setState(() {
isBusy = true;
});
await Magic.loginWithMagicLink(
email: emailController.text, showLoadingUI: false);
setState(() {
isBusy = false;
});
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => HomePage(),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text((e as PlatformException).message!),
),
);
}
}
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isUserLoggedIn = false;
bool isBusy = false;
late String userAccount;
String userBalance = '0 BNB';
final formKey = GlobalKey<FormState>();
final addressController = TextEditingController();
final amountController = TextEditingController();
@override
void dispose() {
addressController.dispose();
amountController.dispose();
super.dispose();
}
@override
void initState() {
isLoggedIn(context);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text("Home Page"),
actions: [
isUserLoggedIn
? ElevatedButton.icon(
icon: Icon(Icons.logout_outlined),
label: Text('Logout',
style: TextStyle(fontWeight: FontWeight.w500)),
onPressed: () => logout(context),
)
: SizedBox()
],
),
body: ListView(children: [
FutureBuilder<GetMetaDataResponse>(
future: Magic.getMetaData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Container(
alignment: Alignment.center,
child: Text('Something went wrong while fetching data'),
);
}
userAccount = snapshot.data!.publicAddress;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
ListTile(
title: Text('Email address',
style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text(snapshot.data!.email),
),
ListTile(
title: Text('Public address',
style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: SelectableText(userAccount),
trailing: TextButton.icon(
onPressed: () async {
await Clipboard.setData(
ClipboardData(text: userAccount));
showBar(context, 'Address copied');
},
icon: Icon(
Icons.copy_outlined,
color: Colors.green,
),
label: Text('Copy')),
),
ListTile(
title: Text('User Balance',
style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text(userBalance),
trailing: TextButton.icon(
onPressed: () => getUserBalance(context),
icon: Icon(
Icons.refresh_outlined,
color: Colors.green,
),
label: Text('Refresh balance')),
),
SizedBox(height: 20),
Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 20),
child: Column(children: [
TextFormField(
controller: addressController,
decoration: InputDecoration(
hintText: "Enter receiver address",
),
validator: (value) {
if (value == '' || value == null)
return "Enter a valid address";
return null;
},
),
SizedBox(height: 20),
TextFormField(
controller: amountController,
decoration: InputDecoration(
hintText: "Enter amount in BNB",
),
validator: (value) {
if (value == '' || value == null)
return "Enter a valid amount";
return null;
},
),
SizedBox(height: 30),
ElevatedButton(
style: ElevatedButton.styleFrom(
alignment: Alignment.center,
padding: EdgeInsets.all(12)),
onPressed: () => sendTransaction(
context,
addressController.text,
amountController.text),
child: Text(
'Send transaction',
style: TextStyle(fontWeight: FontWeight.bold),
)),
]),
),
),
Divider(),
SignMessageForm(fromAddress: userAccount),
],
),
);
}
return Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
);
},
),
]),
);
}
Future<void> isLoggedIn(BuildContext context) async {
try {
setState(() {
isBusy = true;
});
bool value = await Magic.isLoggedIn();
setState(() {
isBusy = false;
isUserLoggedIn = value;
});
} catch (e) {
showBar(context, (e as PlatformException).message!);
}
}
Future<void> sendTransaction(
BuildContext context, String receiverAddress, String sendAmount) async {
try {
setState(() {
isBusy = true;
});
String value = await Magic.sendTransaction(
from: userAccount,
to: receiverAddress,
amount: sendAmount,
gasLimit: "21000",
gasPrice: "10000000000");
setState(() {
isBusy = false;
});
showBar(context, 'Transaction successful $value');
} catch (e) {
showBar(context, (e as PlatformException).message!);
}
}
Future<void> getUserBalance(BuildContext context) async {
try {
setState(() {
isBusy = true;
});
String value = await Magic.getDynamicContractUserBalance(
contractABI: '',
byteCode: "",
accountAddress: userAccount,
contractAddress: "");
setState(() {
isBusy = false;
userBalance = value + ' GFX';
});
showBar(context, 'Balance updated\nYour current balance is $userBalance');
} catch (e) {
showBar(context, (e as PlatformException).message!);
}
}
Future<void> logout(BuildContext context) async {
try {
bool value = await Magic.logout();
if (value) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => LoginPage(),
),
);
} else {
showBar(context, 'Logout failed');
}
} catch (e) {
showBar(context, (e as PlatformException).message!);
}
}
showBar(BuildContext context, String title) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(title),
),
);
}
}
extension EmailValidator on String {
bool get isValidEmail => RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(this);
}
class SignMessageForm extends StatefulWidget {
final String fromAddress;
const SignMessageForm({Key? key, required this.fromAddress})
: super(key: key);
@override
State<SignMessageForm> createState() => _SignMessageFormState();
}
class _SignMessageFormState extends State<SignMessageForm> {
final _formKey = GlobalKey<FormState>();
String? message;
void onSaveMessage(value) {
message = value;
}
bool isBusy = false;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Sign Message',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
SizedBox(
height: 10,
),
Text("Eth Sign"),
SizedBox(
height: 10,
),
TextFormField(
decoration: InputDecoration(labelText: "Message"),
validator: (value) {
if (value == '' || value == null)
return "This field is required";
return null;
},
onChanged: onSaveMessage,
),
SizedBox(height: 20),
Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
alignment: Alignment.center, padding: EdgeInsets.all(12)),
onPressed: () async {
_formKey.currentState?.save();
bool? valid = _formKey.currentState?.validate();
if (valid == null || !valid) {
return;
}
try {
setState(() {
isBusy = true;
});
String value = await Magic.sign(
fromAccount: widget.fromAddress, message: message!);
setState(() {
isBusy = false;
});
showBar(context, 'Signed message! hash : $value');
} catch (e) {
showBar(context, (e as PlatformException).message!);
}
},
child: Text(
'Sign message',
style: TextStyle(fontWeight: FontWeight.bold),
)),
),
],
),
),
);
}
showBar(BuildContext context, String title) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(title),
),
);
}
}