From 23858d718345223d397f1d1f98ad1511f9bb76a7 Mon Sep 17 00:00:00 2001 From: Yii Date: Sun, 1 May 2022 19:22:57 +0800 Subject: [PATCH] leave some comment finished import wallet by privateKey in HomePage --- lib/core/provider/shared_provider.dart | 40 +++--- .../util/extension/riverpod_extension.dart | 2 +- .../domain/providers/begin_provider.dart | 22 +-- .../dialogs/add_wallet_dialog.dart | 26 ++-- .../begin/presentation/pages/begin_page.dart | 44 ++---- .../begin/presentation/pages/home_page.dart | 131 ++++++++++-------- .../presentation/widgets/home/token_tab.dart | 7 +- 7 files changed, 143 insertions(+), 129 deletions(-) diff --git a/lib/core/provider/shared_provider.dart b/lib/core/provider/shared_provider.dart index 2d205d3..bc33363 100644 --- a/lib/core/provider/shared_provider.dart +++ b/lib/core/provider/shared_provider.dart @@ -1,5 +1,6 @@ import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart'; import 'package:web3dart/web3dart.dart'; @@ -8,7 +9,6 @@ import '../../pages/begin/data/models/wallet_info.dart'; import '../../pages/begin/data/states/begin_state.dart'; import '../../pages/begin/data/states/load_coin_state.dart'; import '../../pages/begin/data/states/load_nft_state.dart'; -import '../../pages/begin/presentation/pages/home_page.dart'; import '../util/constant.dart'; import '../util/web3/abi/erc20.g.dart'; import '../util/web3/abi/stream_chicken_2.g.dart'; @@ -51,10 +51,13 @@ class WalletConnectedNotifier extends StateNotifier> { Future addWallet(WalletInfo walletInfo) async { final bool haveSameWallet = state.where((element) => element.address == walletInfo.address).isNotEmpty; if (haveSameWallet) { + Fluttertoast.showToast(msg: '此錢包已匯入過囉'); return; } + + // 檢查是不是已經有從Metamask連結過的錢包,有的話其他只能透過私鑰匯入 final bool alreadyHaveEthWallet = state.where((element) => element.isFromMetamask).isNotEmpty; - if (alreadyHaveEthWallet) { + if (alreadyHaveEthWallet && walletInfo.isFromMetamask) { return; } @@ -65,7 +68,7 @@ class WalletConnectedNotifier extends StateNotifier> { final double doubleAmount = etherAmount.getValueInUnit(EtherUnit.ether); walletInfo.etherAmount = doubleAmount; - state.add(walletInfo); + state = [...state, walletInfo]; } void removeWallet(String walletAddress) { @@ -77,7 +80,7 @@ class WalletConnectedNotifier extends StateNotifier> { } /// 刷新錢包的貨幣資料 -final loadCoinDataProvider = StateNotifierProvider((ref) { +final loadCoinDataProvider = StateNotifierProvider.autoDispose((ref) { WalletConnectedNotifier walletConnectedNotifier = ref.watch(walletConnectedProvider.notifier); WalletHelper walletHelper = ref.watch(walletHelperProvider); String currentWalletAddress = ref.watch(currentWalletProvider)?.address ?? ''; @@ -126,7 +129,7 @@ class LoadCoinDataNotifier extends StateNotifier { } /// 刷新錢包的NFT資料 -final loadNFTDataProvider = StateNotifierProvider((ref) { +final loadNFTDataProvider = StateNotifierProvider.autoDispose((ref) { WalletConnectedNotifier walletConnectedNotifier = ref.watch(walletConnectedProvider.notifier); WalletHelper walletHelper = ref.watch(walletHelperProvider); String currentWalletAddress = ref.watch(currentWalletProvider)?.address ?? ''; @@ -245,47 +248,52 @@ Provider walletHelperProvider = Provider((ref) { return WalletHelper(ref: ref); }); +/// 匯入錢包、匯入PrivateKey final importWalletProvider = StateNotifierProvider( (ref) { final Web3Client client = ref.read(web3ClientProvider); + final WalletConnectedNotifier walletConnectedNotifier = ref.watch(walletConnectedProvider.notifier); return ImportWalletNotifier( client: client, + walletConnectedNotifier: walletConnectedNotifier, ); }, ); class ImportWalletNotifier extends StateNotifier { final Web3Client client; + final WalletConnectedNotifier walletConnectedNotifier; bool isConnectWallet = false; ImportWalletNotifier({ required this.client, + required this.walletConnectedNotifier, }) : super(const ConnectWalletState.init()); - void showHomePage(context) { - HomePage.show(context); - } - - Future importWallet(String privateKey) async { + Future importWallet({required String privateKey}) async { state = const ConnectWalletState.loading(); try { - final private = EthPrivateKey.fromHex(privateKey); - final address = await private.extractAddress(); + final EthPrivateKey ethPrivateKey = EthPrivateKey.fromHex(privateKey); + final EthereumAddress address = await ethPrivateKey.extractAddress(); isConnectWallet = true; - print('Eth Address: $address'); - final WalletInfo wallet = WalletInfo( + final WalletInfo walletInfo = WalletInfo( address: address.toString(), importMethod: WalletImportMethod.privateKey, privateKey: privateKey, ); - state = ConnectWalletState.data(walletInfo: wallet); + walletConnectedNotifier.addWallet(walletInfo); + state = ConnectWalletState.data(walletInfo: walletInfo); } catch (e) { isConnectWallet = false; - state = const ConnectWalletState.error(msg: '無法匯入錢包,請再試一次'); + const String msg = '無法匯入錢包,請再試一次'; + + Fluttertoast.showToast(msg: msg); + state = const ConnectWalletState.error(msg: msg); + state = const ConnectWalletState.init(); } } } diff --git a/lib/core/util/extension/riverpod_extension.dart b/lib/core/util/extension/riverpod_extension.dart index aecb123..e627c0e 100644 --- a/lib/core/util/extension/riverpod_extension.dart +++ b/lib/core/util/extension/riverpod_extension.dart @@ -5,7 +5,7 @@ extension AsyncValueUI on AsyncValue { // isLoading shorthand (AsyncLoading is a subclass of AsycValue) bool get isLoading => this is AsyncLoading; - // show a snackbar on error only + // 在有錯誤狀態的時候觸發,顯示吐司條 void showMessageOnError() => whenOrNull( error: (error, _) { Fluttertoast.showToast(msg: error.toString()); diff --git a/lib/pages/begin/domain/providers/begin_provider.dart b/lib/pages/begin/domain/providers/begin_provider.dart index 48e0d44..ad33c87 100644 --- a/lib/pages/begin/domain/providers/begin_provider.dart +++ b/lib/pages/begin/domain/providers/begin_provider.dart @@ -1,20 +1,22 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import '../../../../core/provider/shared_provider.dart'; import '../../../../core/util/web3/metamask_wallet_connect_helper.dart'; import '../../../../core/util/web3/wallet_helper.dart'; import '../../data/models/wallet_info.dart'; import '../../data/states/begin_state.dart'; -import '../../presentation/pages/home_page.dart'; final StateNotifierProvider connectWalletProvider = StateNotifierProvider( (ref) { final MetamaskWalletConnectHelper walletConnectHelper = ref.read(metamaskWalletConnectHelperProvider); + final WalletConnectedNotifier walletConnectedNotifier = ref.watch(walletConnectedProvider.notifier); final WalletHelper walletHelper = ref.read(walletHelperProvider); return ConnectWalletNotifier( walletConnectHelper: walletConnectHelper, + walletConnectedNotifier: walletConnectedNotifier, walletHelper: walletHelper, ); }, @@ -22,33 +24,37 @@ final StateNotifierProvider connectWa class ConnectWalletNotifier extends StateNotifier { final MetamaskWalletConnectHelper walletConnectHelper; + final WalletConnectedNotifier walletConnectedNotifier; final WalletHelper walletHelper; bool isConnectWallet = false; ConnectWalletNotifier({ required this.walletConnectHelper, + required this.walletConnectedNotifier, required this.walletHelper, }) : super(const ConnectWalletState.init()); - void showHomePage(context) { - HomePage.show(context); - } - Future connectWallet() async { state = const ConnectWalletState.loading(); isConnectWallet = await walletConnectHelper.initSession(); if (isConnectWallet) { final String address = walletConnectHelper.getEthereumCredentials().getEthereumAddress().toString(); + isConnectWallet = true; - final WalletInfo wallet = WalletInfo( + final WalletInfo walletInfo = WalletInfo( address: address, importMethod: WalletImportMethod.metamask, ); - state = ConnectWalletState.data(walletInfo: wallet); + walletConnectedNotifier.addWallet(walletInfo); + state = ConnectWalletState.data(walletInfo: walletInfo); } else { - state = const ConnectWalletState.error(msg: '無法與錢包連結,請再試一次'); + isConnectWallet = false; + const String msg = '無法與錢包連結,請再試一次'; + + Fluttertoast.showToast(msg: msg); + state = const ConnectWalletState.error(msg: msg); state = const ConnectWalletState.init(); } } diff --git a/lib/pages/begin/presentation/dialogs/add_wallet_dialog.dart b/lib/pages/begin/presentation/dialogs/add_wallet_dialog.dart index a86e6aa..c4ad303 100644 --- a/lib/pages/begin/presentation/dialogs/add_wallet_dialog.dart +++ b/lib/pages/begin/presentation/dialogs/add_wallet_dialog.dart @@ -21,12 +21,14 @@ class AddWalletDialog extends StatelessWidget { ); } - final TextEditingController privateKeyEditController = TextEditingController(); - + /// 匯入方式 final importMethodProvider = StateProvider((ref) { return WalletImportMethod.privateKey; }); + final TextEditingController privateKeyEditController = TextEditingController(); + + /// 取得匯入方式的文字 String getImportMethodText(WalletImportMethod method) { String text = ''; switch (method) { @@ -41,8 +43,10 @@ class AddWalletDialog extends StatelessWidget { return text; } - void _onSelectType({required WidgetRef ref}) {} + /// 選擇匯入錢包的方式 + void _onSelectImportMethod({required WidgetRef ref}) {} + /// 貼上複製到剪貼簿的文字 Future _onPastePrivateKey() async { ClipboardData? clipboardData = await Clipboard.getData(Clipboard.kTextPlain); String copiedtext = clipboardData?.text ?? ''; @@ -51,6 +55,13 @@ class AddWalletDialog extends StatelessWidget { TextSelection.fromPosition(TextPosition(offset: privateKeyEditController.text.length)); } + /// 確認送出 + void _onConfirm(BuildContext context) { + if (privateKeyEditController.text.isNotEmpty) { + Navigator.maybePop(context, privateKeyEditController.text); + } + } + @override Widget build(BuildContext context) { return Dialog( @@ -90,7 +101,7 @@ class AddWalletDialog extends StatelessWidget { final String text = getImportMethodText(method); return CommonButton( - onPress: () => _onSelectType(ref: ref), + onPress: () => _onSelectImportMethod(ref: ref), padding: const EdgeInsets.all(16.0), color: CustomTheme.primaryColor, child: Text( @@ -104,7 +115,6 @@ class AddWalletDialog extends StatelessWidget { ], ), const SizedBox(height: 36.0), - Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -125,11 +135,7 @@ class AddWalletDialog extends StatelessWidget { CommonTextField(textEditingController: privateKeyEditController), const SizedBox(height: 24.0), CommonButton( - onPress: () { - if (privateKeyEditController.text.isNotEmpty) { - Navigator.maybePop(context, privateKeyEditController.text); - } - }, + onPress: () => _onConfirm(context), padding: const EdgeInsets.symmetric(vertical: 20.0), color: CustomTheme.secondColor, child: const Text( diff --git a/lib/pages/begin/presentation/pages/begin_page.dart b/lib/pages/begin/presentation/pages/begin_page.dart index d5571c3..fd22bdf 100644 --- a/lib/pages/begin/presentation/pages/begin_page.dart +++ b/lib/pages/begin/presentation/pages/begin_page.dart @@ -4,7 +4,6 @@ import 'package:fluttertoast/fluttertoast.dart'; import '../../../../core/provider/shared_provider.dart'; import '../../../../core/util/theme.dart'; -import '../../data/states/begin_state.dart'; import '../../domain/providers/begin_provider.dart'; import '../dialogs/add_wallet_dialog.dart'; import '../widgets/common_button.dart'; @@ -17,35 +16,23 @@ class BeginPage extends StatelessWidget { required BuildContext context, required WidgetRef ref, }) { - if (ref.read(walletConnectedProvider.notifier).isWalletConnected || - ref.read(importWalletProvider.notifier).isConnectWallet) { + // 檢查是否已有錢包連結了 + if (ref.read(walletConnectedProvider.notifier).isWalletConnected) { HomePage.show(context); } else { Fluttertoast.showToast(msg: '請先連結或匯入錢包'); } } - void listenConnectWallet(WidgetRef ref) { - ref.listen(connectWalletProvider, (previous, next) { - if (next is ConnectWalletData) { - ref.read(walletConnectedProvider.notifier).addWallet(next.walletInfo); - } else if (next is ConnectWalletError) { - Fluttertoast.showToast(msg: next.msg); - } - }); - } - - void listenImportWallet(WidgetRef ref) { - ref.listen( - importWalletProvider, - (previous, next) { - if (next is ConnectWalletData) { - ref.read(walletConnectedProvider.notifier).addWallet(next.walletInfo); - } else if (next is ConnectWalletError) { - Fluttertoast.showToast(msg: next.msg); - } - }, - ); + /// 匯入錢包 + Future _importWallet({ + required BuildContext context, + required WidgetRef ref, + }) async { + final privateKey = await AddWalletDialog.show(context) as String?; + if (privateKey != null) { + ref.read(importWalletProvider.notifier).importWallet(privateKey: privateKey); + } } @override @@ -99,7 +86,6 @@ class BeginPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ Consumer(builder: (context, ref, _) { - listenConnectWallet(ref); return CommonButton( onPress: () => ref.read(connectWalletProvider.notifier).connectWallet(), color: const Color.fromRGBO(255, 255, 255, 1), @@ -137,14 +123,8 @@ class BeginPage extends StatelessWidget { }), const SizedBox(height: 18), Consumer(builder: (context, ref, _) { - listenImportWallet(ref); return CommonButton( - onPress: () async { - final privateKey = await AddWalletDialog.show(context) as String?; - if (privateKey != null) { - ref.read(importWalletProvider.notifier).importWallet(privateKey); - } - }, + onPress: () => _importWallet(context: context, ref: ref), color: CustomTheme.secondColor, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), diff --git a/lib/pages/begin/presentation/pages/home_page.dart b/lib/pages/begin/presentation/pages/home_page.dart index cecdbfd..baa012c 100644 --- a/lib/pages/begin/presentation/pages/home_page.dart +++ b/lib/pages/begin/presentation/pages/home_page.dart @@ -5,7 +5,6 @@ import 'package:matrix4_transform/matrix4_transform.dart'; import '../../../../core/provider/shared_provider.dart'; import '../../../../core/util/theme.dart'; -import '../../data/models/token_info.dart'; import '../../data/models/wallet_info.dart'; import '../../data/states/load_coin_state.dart'; import '../../data/states/load_nft_state.dart'; @@ -33,18 +32,29 @@ class HomePage extends ConsumerStatefulWidget { } class _HomePageState extends ConsumerState with SingleTickerProviderStateMixin { + late SwiperController _swiperController; late TabController _tabController; - // 一開始先取得錢包相關資料,保持最新 + /// 一開始先取得錢包相關資料,保持最新 void _loadData() { ref.read(loadCoinDataProvider.notifier).loadCoinData(); ref.read(loadNFTDataProvider.notifier).loadNFTData(); } - void onTokenTransfer(TokenInfo tokenInfo) {} + /// 匯入錢包 + Future _importWallet( + BuildContext context, { + required WidgetRef ref, + }) async { + final privateKey = await AddWalletDialog.show(context) as String?; + if (privateKey != null) { + ref.read(importWalletProvider.notifier).importWallet(privateKey: privateKey); + } + } @override void initState() { + _swiperController = SwiperController(); _tabController = TabController(vsync: this, length: 2); _loadData(); super.initState(); @@ -53,11 +63,14 @@ class _HomePageState extends ConsumerState with SingleTickerProviderSt @override void dispose() { _tabController.dispose(); + _swiperController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { + final List walletList = ref.watch(walletConnectedProvider); + return WillPopScope( onWillPop: () async { return false; @@ -76,66 +89,73 @@ class _HomePageState extends ConsumerState with SingleTickerProviderSt child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - const Text( - 'Wallet', - style: CustomTheme.textBoldWhite, - ), - const SizedBox(width: 5), - GestureDetector( - onTap: () => AddWalletDialog.show(context), - child: const Icon( - Icons.add_circle_outlined, - color: CustomTheme.primaryColor, + Expanded( + child: Row( + children: [ + const Text( + 'Wallet', + style: CustomTheme.textBoldWhite, ), - ) - ], + const SizedBox(width: 5), + GestureDetector( + onTap: () => _importWallet(context, ref: ref), + child: const Icon( + Icons.add_circle_outlined, + color: CustomTheme.primaryColor, + ), + ) + ], + ), + ), + Consumer( + builder: (context, ref, _) { + return IconButton( + onPressed: () { + _loadData(); + }, + icon: const Icon( + Icons.refresh_rounded, + color: Colors.white, + ), + ); + }, + ), + Consumer( + builder: (context, ref, _) { + return IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon( + Icons.door_back_door, + color: Colors.white, + ), + ); + }, ), - Consumer(builder: (context, ref, _) { - return IconButton( - onPressed: () { - _loadData(); - }, - icon: const Icon( - Icons.refresh_rounded, - color: Colors.white, - ), - ); - }), - // GestureDetector( - // onTap: () => Navigator.of(context).pop(), - // child: const Text( - // 'logOut', - // style: CustomTheme.textPrimary, - // ), - // ) ], ), ), const SizedBox(height: 15), SizedBox( height: 210, - child: Consumer(builder: (context, ref, _) { - final List walletList = ref.watch(walletConnectedProvider); - - return Swiper( - controller: SwiperController(), - itemBuilder: (BuildContext context, int index) { - final WalletInfo walletInfo = walletList[index]; - return _WalletCard( - index: index, - walletInfo: walletInfo, - ); - }, - itemCount: walletList.length, - viewportFraction: 0.8, - scale: 0.9, - onIndexChanged: (index) { - ref.read(currentWalletIndexProvider.notifier).state = index; - }, - ); - }), + child: Swiper( + controller: _swiperController, + itemBuilder: (BuildContext context, int index) { + final WalletInfo walletInfo = walletList[index]; + return _WalletCard( + index: index, + walletInfo: walletInfo, + ); + }, + itemCount: walletList.length, + viewportFraction: 0.8, + scale: 0.9, + onIndexChanged: (index) { + // 更新目前的錢包索引 + ref.read(currentWalletIndexProvider.notifier).state = index; + // 刷新 + _loadData(); + }, + ), ), const SizedBox(height: 30), Expanded( @@ -199,7 +219,6 @@ class _HomePageState extends ConsumerState with SingleTickerProviderSt loading: () => const CustomLoading(), data: (tokenInfoList) => TokenTab( tokenInfoList: walletInfo?.tokenInfoList ?? [], - onTransfer: onTokenTransfer, ), ), ), diff --git a/lib/pages/begin/presentation/widgets/home/token_tab.dart b/lib/pages/begin/presentation/widgets/home/token_tab.dart index 53a12c1..fa9b35a 100644 --- a/lib/pages/begin/presentation/widgets/home/token_tab.dart +++ b/lib/pages/begin/presentation/widgets/home/token_tab.dart @@ -7,12 +7,10 @@ import '../common_button.dart'; class TokenTab extends StatelessWidget { final List tokenInfoList; - final Function(TokenInfo) onTransfer; const TokenTab({ Key? key, required this.tokenInfoList, - required this.onTransfer, }) : super(key: key); @override @@ -23,7 +21,6 @@ class TokenTab extends StatelessWidget { itemBuilder: (context, index) { final TokenInfo tokenInfo = tokenInfoList[index]; return _CoinItem( - onTransfer: onTransfer, tokenInfo: tokenInfo, ); }, @@ -34,11 +31,9 @@ class TokenTab extends StatelessWidget { class _CoinItem extends StatelessWidget { const _CoinItem({ Key? key, - required this.onTransfer, required this.tokenInfo, }) : super(key: key); - final Function(TokenInfo p1) onTransfer; final TokenInfo tokenInfo; @override @@ -52,7 +47,7 @@ class _CoinItem extends StatelessWidget { padding: const EdgeInsets.only(bottom: 16.0), child: CommonButton( color: Colors.white, - onPress: () => onTransfer(tokenInfo), + onPress: () {}, child: Padding( padding: const EdgeInsets.symmetric(vertical: 7), child: Row(