From 56fb6485ce34202324822f97df3fb5da7c4b10fa Mon Sep 17 00:00:00 2001 From: 0xmove <0xmovebuilder@gmail.com> Date: Mon, 30 Oct 2023 19:44:24 +0800 Subject: [PATCH] web: update example --- example/lib/components/button.dart | 25 ++++ example/lib/helper/helper.dart | 32 +++++ example/lib/main.dart | 217 ++++++++++++++++++----------- example/lib/pages/faucet.dart | 103 ++++++++++++++ example/lib/pages/merge.dart | 19 +++ example/lib/pages/split.dart | 61 ++++++++ example/lib/pages/token_menu.dart | 1 - example/lib/pages/transfer.dart | 76 ++++++++++ example/pubspec.lock | 12 +- example/pubspec.yaml | 3 +- 10 files changed, 466 insertions(+), 83 deletions(-) create mode 100644 example/lib/components/button.dart create mode 100644 example/lib/pages/faucet.dart create mode 100644 example/lib/pages/merge.dart create mode 100644 example/lib/pages/split.dart create mode 100644 example/lib/pages/transfer.dart diff --git a/example/lib/components/button.dart b/example/lib/components/button.dart new file mode 100644 index 0000000..3859633 --- /dev/null +++ b/example/lib/components/button.dart @@ -0,0 +1,25 @@ + +import 'package:flutter/material.dart'; + +class Button extends StatelessWidget { + Button(this.title, this.onClick, { Key? key }) : super(key: key); + + String title; + Function onClick; + + @override + Widget build(BuildContext context) { + return Container( + child: SizedBox( + width: 150, + height: 50, + child: ElevatedButton( + child: Text(title, textAlign: TextAlign.center), + onPressed: () { + onClick(); + }, + ), + ), + ); + } +} \ No newline at end of file diff --git a/example/lib/helper/helper.dart b/example/lib/helper/helper.dart index 060f7f1..4732685 100644 --- a/example/lib/helper/helper.dart +++ b/example/lib/helper/helper.dart @@ -2,9 +2,12 @@ import 'package:another_flushbar/flushbar.dart'; import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sui/sui.dart'; +final suiClient = SuiClient(Constants.devnetAPI); + void showSnackBar(BuildContext context, String title, {int seconds = 3}) { Flushbar( icon: const Icon(Icons.info_outline), @@ -13,6 +16,35 @@ void showSnackBar(BuildContext context, String title, {int seconds = 3}) { ).show(context); } +void showToast(BuildContext context, String title, {int seconds = 3, Color? color}) { + Widget toast = Container( + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25.0), + color: color ?? Colors.greenAccent, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.check), + SizedBox( + width: 12.0, + ), + Text(title), + ], + ), + ); + FToast().init(context).showToast( + gravity: ToastGravity.BOTTOM, + toastDuration: Duration(seconds: 3), + child: toast + ); +} + +void showErrorToast(BuildContext context, String title, {int seconds = 3}) { + showToast(context, title, seconds: seconds, color: Colors.redAccent); +} + Future getLocalSuiAccount() async { const suiAccountKey = "sui_dart_account_key"; final SharedPreferences prefs = await SharedPreferences.getInstance(); diff --git a/example/lib/main.dart b/example/lib/main.dart index 40d3ec4..930a7b7 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,32 +1,42 @@ -import 'package:example/pages/token_menu.dart'; +import 'package:example/pages/faucet.dart'; import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:focus_detector/focus_detector.dart'; import 'package:sui/sui.dart'; import 'package:sui/types/faucet.dart'; import 'helper/helper.dart'; +import 'pages/split.dart'; +import 'pages/token_menu.dart'; +import 'pages/transfer.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}): super(key: key); + const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( - title: 'Sui Example', + title: 'Sui Dart Demo', theme: ThemeData( primarySwatch: Colors.blue, + textSelectionTheme: const TextSelectionThemeData( + cursorColor: Color.fromRGBO(84, 150, 229, 1), + selectionColor: Colors.yellow, + selectionHandleColor: Colors.black, + ), ), - home: const MyHomePage(title: 'Sui Example'), + home: const MyHomePage(title: 'Sui Dart Demo'), + builder: FToastBuilder(), ); } } class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}): super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @@ -35,10 +45,8 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - - BigInt _balance = BigInt.zero; late SuiClient suiClient; - SuiAccount? account; + late SuiAccount account; @override void initState() { @@ -52,89 +60,140 @@ class _MyHomePageState extends State { }); } - bool requestingFaucet = false; - - void _requestFaucet() async { - if (requestingFaucet || account == null) return; + void _navigateToTokenManage() { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => TokenMenu(client: suiClient))); + } - var resp = await suiClient.getBalance(account!.getAddress()); - _balance = resp.totalBalance; - if (_balance <= BigInt.zero) { - setState(() { - requestingFaucet = true; - }); + int menuIndex = 0; - try { - final faucet = FaucetClient(Constants.faucetDevAPI); - final faucetResp = await faucet.requestSuiFromFaucetV1(account!.getAddress()); - if (faucetResp.task != null) { - while(true) { - final statusResp = await faucet.getFaucetRequestStatus(faucetResp.task!); - if (statusResp.status.status == BatchSendStatus.SUCCEEDED) { - break; - } else { - await Future.delayed(const Duration(seconds: 3)); - } - } - } - } catch(e) { - showSnackBar(context, e.toString()); - } finally { - setState(() { - requestingFaucet = false; - }); - } + Widget contentPage() { + switch(menuIndex) { + case 0: + return const Center( + child: Column( + children: [ + Text("Sui Dart SDK", style: TextStyle(fontSize: 35)), + SizedBox(height: 20), + Text("A cross-platform Sui SDK for Mobile, Web and Desktop", style: TextStyle(fontSize: 28), textAlign: TextAlign.center) + ], + ) + ); + case 1: return Faucet(account); + case 2: return Split(account); + case 4: return Transfer(account); } - resp = await suiClient.getBalance(account!.getAddress()); - _balance = resp.totalBalance; + return Container(); + } + void menuClick(int menu) { setState(() { - _balance = _balance; + menuIndex = menu; }); } - void _navigateToTokenManage() { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => TokenMenu(client: suiClient))); - } - @override Widget build(BuildContext context) { return FocusDetector( - onFocusGained: () { - _requestFaucet(); - }, - child:Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Padding( - padding: const EdgeInsets.only(top: 40.0), - child: Column( - children: [ - Text( - '${_balance / BigInt.from(10).pow(9)} SUI', - style: const TextStyle(fontSize: 20), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: _requestFaucet, - child: Text(requestingFaucet ? "Inprogress" : "Faucet")), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.only(left: 16, right: 16), - child: SelectableText(account?.getAddress() ?? ''), - ) - ], + onFocusGained: () { + // _requestFaucet(); + }, + child: Scaffold( + appBar: AppBar( + title: Text(widget.title), ), - ), - ), - floatingActionButton: _balance != BigInt.zero ? FloatingActionButton( - onPressed: _navigateToTokenManage, - tooltip: 'TokenManager', - child: const Icon(Icons.menu), - ) : null, - )); + drawer: Drawer( + child: ListView( + children: [ + DrawerHeader( + decoration: const BoxDecoration( + color: Color.fromRGBO(84, 150, 229, 1) + ), + child: Center( + child: Column( + children: [ + const Text("Sui Dart", style: TextStyle(fontSize: 20, color: Colors.white)), + const SizedBox(height: 30), + SelectableText( + account.getAddress(), + showCursor: true, + style: const TextStyle(fontSize: 14, color: Colors.white), + onTap: () { + print(account.getAddress()); + }, + ), + ], + ), + ) + ), + ListTile( + title: const Text("Faucet"), + onTap: () { + Navigator.pop(context); + menuClick(1); + }, + ), + ListTile( + title: const Text("Split SUI"), + onTap: () { + Navigator.pop(context); + menuClick(2); + }, + ), + // ListTile( + // title: const Text(("Merge Sui")), + // onTap: () { + // Navigator.pop(context); + // menuClick(3); + // }, + // ), + ListTile( + title: const Text(("Transfer Sui")), + onTap: () { + menuClick(4); + }, + ), + // ListTile( + // title: const Text(("Publish Package")), + // onTap: () { + // print("tap Publish..."); + // }, + // ) + ], + ), + ), + body: Center( + child: Padding( + padding: const EdgeInsets.only(top: 300.0), + child: contentPage() + // child: Column( + // children: [ + // Text( + // '${_balance / BigInt.from(10).pow(9)} SUI', + // style: const TextStyle(fontSize: 20), + // ), + // const SizedBox(height: 20), + // ElevatedButton( + // onPressed: _requestFaucet, + // child: Text(requestingFaucet ? "Inprogress" : "Faucet")), + // const SizedBox(height: 20), + // Padding( + // padding: const EdgeInsets.only(left: 16, right: 16), + // child: SelectableText(account?.getAddress() ?? ''), + // ) + // ], + // ), + ), + ), + // floatingActionButton: balance != BigInt.zero + // ? FloatingActionButton( + // onPressed: _navigateToTokenManage, + // tooltip: 'TokenManager', + // child: const Icon(Icons.menu), + // ) + // : null, + ) + ); } } diff --git a/example/lib/pages/faucet.dart b/example/lib/pages/faucet.dart new file mode 100644 index 0000000..f59e7ea --- /dev/null +++ b/example/lib/pages/faucet.dart @@ -0,0 +1,103 @@ + +import 'package:example/components/button.dart'; +import 'package:example/helper/helper.dart'; +import 'package:flutter/material.dart'; +import 'package:sui/sui.dart'; +import 'package:sui/types/faucet.dart'; + +class Faucet extends StatefulWidget { + const Faucet(this.account, {super.key}); + + final SuiAccount account; + + @override + State createState() => _FaucetState(); + +} + +class _FaucetState extends State { + + BigInt balance = BigInt.zero; + + @override + void initState() { + super.initState(); + + suiClient.getBalance(widget.account.getAddress()).then((res) { + setState(() { + balance = res.totalBalance; + }); + }); + } + + bool requestingFaucet = false; + + void _requestFaucet() async { + if (requestingFaucet) return; + + final address = widget.account.getAddress(); + + var resp = await suiClient.getBalance(address); + balance = resp.totalBalance; + if (balance <= BigInt.zero) { + setState(() { + requestingFaucet = true; + }); + + try { + final faucet = FaucetClient(Constants.faucetDevAPI); + final faucetResp = + await faucet.requestSuiFromFaucetV1(address); + if (faucetResp.task != null) { + while (true) { + final statusResp = + await faucet.getFaucetRequestStatus(faucetResp.task!); + if (statusResp.status.status == BatchSendStatus.SUCCEEDED) { + break; + } else { + await Future.delayed(const Duration(seconds: 3)); + } + } + } + } catch (e) { + showSnackBar(context, e.toString()); + } finally { + setState(() { + requestingFaucet = false; + }); + } + } + + resp = await suiClient.getBalance(address); + setState(() { + balance = resp.totalBalance; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Padding( + padding: const EdgeInsets.only(top: 40.0), + child: Column( + children: [ + Text( + '${balance / BigInt.from(10).pow(9)} SUI', + style: const TextStyle(fontSize: 20), + ), + const SizedBox(height: 20), + Button(requestingFaucet ? "Inprogress" : "Faucet", _requestFaucet), + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: SelectableText(widget.account.getAddress()), + ) + ], + ), + ), + ), + ); + } + +} \ No newline at end of file diff --git a/example/lib/pages/merge.dart b/example/lib/pages/merge.dart new file mode 100644 index 0000000..47bbc01 --- /dev/null +++ b/example/lib/pages/merge.dart @@ -0,0 +1,19 @@ + +import 'package:flutter/material.dart'; + +class Merge extends StatefulWidget { + const Merge({ Key? key }) : super(key: key); + + @override + _MergeState createState() => _MergeState(); +} + +class _MergeState extends State { + + @override + Widget build(BuildContext context) { + return Container( + + ); + } +} \ No newline at end of file diff --git a/example/lib/pages/split.dart b/example/lib/pages/split.dart new file mode 100644 index 0000000..5910707 --- /dev/null +++ b/example/lib/pages/split.dart @@ -0,0 +1,61 @@ + +import 'dart:math'; + +import 'package:example/components/button.dart'; +import 'package:example/helper/helper.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:sui/sui.dart'; + +class Split extends StatefulWidget { + const Split(this.account, { Key? key }) : super(key: key); + + final SuiAccount account; + + @override + _SplitState createState() => _SplitState(); +} + +class _SplitState extends State { + + final amountTextController = TextEditingController(); + + Future splitSui(String amount, String to) async { + BigInt? value = BigInt.tryParse(amount); + if(value == null) return; + + final amountValue = value * BigInt.from(10).pow(9); + + final txb = TransactionBlock(); + txb.setGasBudget(BigInt.from(20000000)); + final result = txb.splitCoins(txb.gas, [txb.pureInt(amountValue.toInt())]); + txb.transferObjects([result], txb.pureAddress(to)); + try { + await suiClient.signAndExecuteTransactionBlock(widget.account, txb); + showToast(context, "Transaction Send Success"); + } catch(e) { + showErrorToast(context, e.toString()); + } + } + + @override + Widget build(BuildContext context) { + return Container( + width: 400, + child: Column( + children: [ + TextField( + controller: amountTextController, + decoration: const InputDecoration( + suffixText: "SUI" + ), + ), + const SizedBox(height: 50), + Button('Split', () { + splitSui(amountTextController.text, widget.account.getAddress()); + }) + ], + ), + ); + } +} \ No newline at end of file diff --git a/example/lib/pages/token_menu.dart b/example/lib/pages/token_menu.dart index 5a8c6f2..0c46829 100644 --- a/example/lib/pages/token_menu.dart +++ b/example/lib/pages/token_menu.dart @@ -3,7 +3,6 @@ import 'package:example/pages/managed_nft.dart'; import 'package:example/pages/managed_token.dart'; import 'package:example/pages/transfer_coin.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:sui/sui_client.dart'; class TokenMenu extends StatefulWidget { diff --git a/example/lib/pages/transfer.dart b/example/lib/pages/transfer.dart new file mode 100644 index 0000000..c549b95 --- /dev/null +++ b/example/lib/pages/transfer.dart @@ -0,0 +1,76 @@ + +import 'package:example/components/button.dart'; +import 'package:example/helper/helper.dart'; +import 'package:flutter/material.dart'; +import 'package:sui/sui.dart'; + +class Transfer extends StatefulWidget { + const Transfer(this.account, {Key? key}): super(key: key); + + final SuiAccount account; + + @override + State createState() => _TransferState(); + +} + +class _TransferState extends State { + + TextEditingController amountTextController = TextEditingController(text: "1"); + TextEditingController receiverTextController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Container( + width: 400, + child: Column( + children: [ + TextField( + controller: amountTextController, + decoration: const InputDecoration( + hintText: "Token Amount", + suffixText: 'SUI', + border: OutlineInputBorder(), + ) + ), + const SizedBox(height: 30), + TextField( + controller: receiverTextController, + decoration: const InputDecoration( + hintText: "Receiver Address", + border: OutlineInputBorder(), + ) + ), + const SizedBox(height: 50), + Button('Send', () async { + final amount = int.tryParse(amountTextController.text); + if (amount == null) return; + + final receiver = receiverTextController.text; + if (!SuiAccount.isValidAddress(receiver)) return; + + try { + final txb = TransactionBlock(); + txb.setGasBudget(BigInt.from(20000000)); + final result = txb.splitCoins(txb.gas, [txb.pureInt(amount)]); + txb.transferObjects([result], txb.pureAddress(receiver)); + final resp = await suiClient.signAndExecuteTransactionBlock( + widget.account, + txb, + requestType: ExecuteTransaction.WaitForLocalExecution + ); + if(resp.confirmedLocalExecution == true) { + showToast(context, "Transaction Send Success"); + } else { + showErrorToast(context, "Transaction Send Failed"); + } + } catch(e) { + showErrorToast(context, e.toString()); + } + }) + ], + ) + ); + } + +} \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 02e9d1c..62c1312 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: "direct main" description: name: another_flushbar - sha256: fa09f8a4ca582c417669b7b1d0e85ce65bd074d80bb0dcbb1302ad1b22bdc3ef + sha256: "19bf9520230ec40b300aaf9dd2a8fefcb277b25ecd1c4838f530566965befc2a" url: "https://pub.dev" source: hosted - version: "1.12.29" + version: "1.12.30" async: dependency: transitive description: @@ -184,6 +184,14 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c" + url: "https://pub.dev" + source: hosted + version: "8.2.2" focus_detector: dependency: "direct main" description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index a8ae6cf..8abca96 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -37,9 +37,10 @@ dependencies: cupertino_icons: ^1.0.2 sui: path: ../ - another_flushbar: ^1.12.29 + another_flushbar: ^1.12.30 focus_detector: ^2.0.1 shared_preferences: ^2.2.2 + fluttertoast: ^8.2.2 dev_dependencies: flutter_test: