From f535bea93248ec6bcf6cc15a1d748527b7d125d4 Mon Sep 17 00:00:00 2001 From: poka Date: Sat, 28 May 2022 19:13:30 +0200 Subject: [PATCH] Persitents profiles images; bugs fix; balances on wallets_home --- lib/globals.dart | 1 + lib/models/wallet_data.dart | 11 +- lib/models/wallet_data.g.dart | 8 +- lib/providers/generate_wallets.dart | 2 +- lib/providers/home.dart | 9 ++ lib/providers/my_wallets.dart | 59 ++--------- lib/providers/wallet_options.dart | 72 +++++-------- lib/screens/common_elements.dart | 48 +++++++++ lib/screens/myWallets/choose_wallet.dart | 8 +- lib/screens/myWallets/unlocking_wallet.dart | 2 +- lib/screens/myWallets/wallet_options.dart | 52 +++++---- lib/screens/myWallets/wallets_home.dart | 112 +++++++++++--------- lib/screens/onBoarding/10.dart | 2 +- pubspec.lock | 7 -- pubspec.yaml | 4 +- 15 files changed, 203 insertions(+), 194 deletions(-) diff --git a/lib/globals.dart b/lib/globals.dart index 69f5154..1c7816f 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -24,6 +24,7 @@ late Box chestBox; late Box configBox; late Box g1WalletsBox; // late Box keystoreBox; +late Directory imageDirectory; // String cesiumPod = "https://g1.data.le-sou.org"; String cesiumPod = "https://g1.data.presles.fr"; diff --git a/lib/models/wallet_data.dart b/lib/models/wallet_data.dart index c13b894..38c8c64 100644 --- a/lib/models/wallet_data.dart +++ b/lib/models/wallet_data.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:hive_flutter/hive_flutter.dart'; part 'wallet_data.g.dart'; @@ -23,10 +22,10 @@ class WalletData extends HiveObject { int? derivation; @HiveField(6) - String? imageName; + String? imageDefaultPath; @HiveField(7) - File? imageFile; + String? imageCustomPath; WalletData( {this.version, @@ -35,8 +34,8 @@ class WalletData extends HiveObject { this.number, this.name, this.derivation, - this.imageName, - this.imageFile}); + this.imageDefaultPath, + this.imageCustomPath}); // representation of WalletData when debugging @override @@ -46,7 +45,7 @@ class WalletData extends HiveObject { // creates the ':'-separated string from the WalletData String inLine() { - return "$chest:$number:$name:$derivation:$imageName"; + return "$chest:$number:$name:$derivation:$imageDefaultPath"; } // returns only the id part of the ':'-separated string diff --git a/lib/models/wallet_data.g.dart b/lib/models/wallet_data.g.dart index b577e03..0ea0e8c 100644 --- a/lib/models/wallet_data.g.dart +++ b/lib/models/wallet_data.g.dart @@ -23,8 +23,8 @@ class WalletDataAdapter extends TypeAdapter { number: fields[3] as int?, name: fields[4] as String?, derivation: fields[5] as int?, - imageName: fields[6] as String?, - imageFile: fields[7] as File?, + imageDefaultPath: fields[6] as String?, + imageCustomPath: fields[7] as String?, ); } @@ -45,9 +45,9 @@ class WalletDataAdapter extends TypeAdapter { ..writeByte(5) ..write(obj.derivation) ..writeByte(6) - ..write(obj.imageName) + ..write(obj.imageDefaultPath) ..writeByte(7) - ..write(obj.imageFile); + ..write(obj.imageCustomPath); } @override diff --git a/lib/providers/generate_wallets.dart b/lib/providers/generate_wallets.dart index 5cc67e3..56d8127 100644 --- a/lib/providers/generate_wallets.dart +++ b/lib/providers/generate_wallets.dart @@ -87,7 +87,7 @@ class GenerateWalletsProvider with ChangeNotifier { number: 0, name: _name, derivation: 2, - imageName: '0.png'); + imageDefaultPath: '0.png'); await walletBox.add(myWallet); await configBox.put('currentChest', chestKey); diff --git a/lib/providers/home.dart b/lib/providers/home.dart index 44ad2da..d43e581 100644 --- a/lib/providers/home.dart +++ b/lib/providers/home.dart @@ -16,6 +16,7 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:path_provider/path_provider.dart' as pp; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; class HomeProvider with ChangeNotifier { @@ -47,6 +48,14 @@ class HomeProvider with ChangeNotifier { } else { await Hive.initFlutter(); } + + // Init app folders + final documentDir = await getApplicationDocumentsDirectory(); + imageDirectory = Directory('${documentDir.path}/images'); + + if (!await imageDirectory.exists()) { + await imageDirectory.create(); + } } Future getAppVersion() async { diff --git a/lib/providers/my_wallets.dart b/lib/providers/my_wallets.dart index 5049c5e..536295c 100644 --- a/lib/providers/my_wallets.dart +++ b/lib/providers/my_wallets.dart @@ -4,6 +4,7 @@ import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:gecko/screens/common_elements.dart'; import 'package:provider/provider.dart'; class MyWalletsProvider with ChangeNotifier { @@ -11,6 +12,7 @@ class MyWalletsProvider with ChangeNotifier { late String pinCode; late String mnemonic; int? pinLenght; + bool isNewDerivationLoading = false; int? getCurrentChest() { if (configBox.get('currentChest') == null) { @@ -71,7 +73,8 @@ class MyWalletsProvider with ChangeNotifier { try { log.w('DELETE ALL WALLETS ?'); - final bool? _answer = await (_confirmDeletingAllWallets(context)); + final bool? _answer = await (confirmPopop( + context, 'Êtes-vous sûr de vouloir oublier tous vos coffres ?')); if (_answer!) { await walletBox.clear(); await chestBox.clear(); @@ -88,58 +91,13 @@ class MyWalletsProvider with ChangeNotifier { } } - Future _confirmDeletingAllWallets(context) async { - return showDialog( - context: context, - barrierDismissible: true, // user must tap button! - builder: (BuildContext context) { - return AlertDialog( - backgroundColor: backgroundColor, - content: const Text( - 'Êtes-vous sûr de vouloir oublier tous vos coffres ?', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500), - ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - key: const Key('confirmDeletingAllWallets'), - child: const Text( - "Oui", - style: TextStyle( - fontSize: 20, - color: Color(0xffD80000), - ), - ), - onPressed: () { - Navigator.pop(context, true); - }, - ), - const SizedBox(width: 20), - TextButton( - child: const Text( - "Non", - style: TextStyle(fontSize: 20), - ), - onPressed: () { - Navigator.pop(context, false); - }, - ), - const SizedBox(height: 120) - ], - ) - ], - ); - }, - ); - } - Future generateNewDerivation(context, String _name) async { + isNewDerivationLoading = true; + notifyListeners(); int _newDerivationNbr; int _newWalletNbr; int? _chest = getCurrentChest(); + List _walletConfig = readAllWallets(_chest); if (_walletConfig.isEmpty) { @@ -168,10 +126,11 @@ class MyWalletsProvider with ChangeNotifier { number: _newWalletNbr, name: _name, derivation: _newDerivationNbr, - imageName: '${_newWalletNbr % 4}.png'); + imageDefaultPath: '${_newWalletNbr % 4}.png'); await walletBox.add(newWallet); + isNewDerivationLoading = false; notifyListeners(); } diff --git a/lib/providers/wallet_options.dart b/lib/providers/wallet_options.dart index 553f149..c60ef93 100644 --- a/lib/providers/wallet_options.dart +++ b/lib/providers/wallet_options.dart @@ -5,6 +5,7 @@ import 'package:gecko/globals.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:gecko/screens/common_elements.dart'; import 'package:image_picker/image_picker.dart'; import 'package:provider/provider.dart'; @@ -57,7 +58,8 @@ class WalletOptionsProvider with ChangeNotifier { } Future deleteWallet(context, WalletData wallet) async { - final bool? _answer = await (_confirmDeletingWallet(context, wallet.name)); + final bool? _answer = await (confirmPopop(context, + 'Êtes-vous sûr de vouloir oublier le portefeuille "${wallet.name}" ?')); if (_answer!) { await walletBox.delete(wallet.key); @@ -71,61 +73,40 @@ class WalletOptionsProvider with ChangeNotifier { return 0; } - Future _confirmDeletingWallet(context, _walletName) async { - return showDialog( - context: context, - barrierDismissible: true, // user must tap button! - builder: (BuildContext context) { - return AlertDialog( - title: Text( - 'Êtes-vous sûr de vouloir supprimer le portefeuille "$_walletName" ?'), - content: SingleChildScrollView( - child: ListBody( - children: const [ - Text('Vous pourrez restaurer ce portefeuille plus tard.'), - ], - ), - ), - actions: [ - TextButton( - child: const Text("Non", key: Key('cancelDeleting')), - onPressed: () { - Navigator.pop(context, false); - }, - ), - TextButton( - child: const Text("Oui", key: Key('confirmDeleting')), - onPressed: () { - Navigator.pop(context, true); - }, - ), - ], - ); - }, - ); - } - void bluringBalance() { isBalanceBlur = !isBalanceBlur; notifyListeners(); } - Future changeAvatar() async { - File _image; + Future changeAvatar() async { + // File _image; final picker = ImagePicker(); XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { - _image = File(pickedFile.path); - ////TODO: Store image on disk, store path in walletBox.imagePath - log.i(pickedFile.path); - return _image; + File imageFile = File(pickedFile.path); + if (!await imageDirectory.exists()) { + log.e("Image folder doesn't exist"); + return ''; + } + + final newPath = "${imageDirectory.path}/${pickedFile.name}"; + + await imageFile.copy(newPath); + // final File newImage = File(newPath); + + // await newImage.writeAsBytes(await pickedFile.readAsBytes()); + // await pickedFile.saveTo(newPath); + // await Future.delayed(const Duration(milliseconds: 100)); + + log.i(newPath); + return newPath; } else { log.w('No image selected.'); - return null; + return ''; } } @@ -269,7 +250,8 @@ class WalletOptionsProvider with ChangeNotifier { Map balanceCache = {}; -Widget balance(BuildContext context, String address, double size) { +Widget balance(BuildContext context, String address, double size, + [Color _color = Colors.black]) { return Column(children: [ Consumer(builder: (context, _sdk, _) { return FutureBuilder( @@ -280,8 +262,7 @@ Widget balance(BuildContext context, String address, double size) { if (balanceCache[address] != null) { return Text(balanceCache[address]!, style: TextStyle( - fontSize: isTall ? size : size * 0.9, - )); + fontSize: isTall ? size : size * 0.9, color: _color)); } else { return SizedBox( height: 15, @@ -298,6 +279,7 @@ Widget balance(BuildContext context, String address, double size) { balanceCache[address]!, style: TextStyle( fontSize: isTall ? size : size * 0.9, + color: _color, ), ); }); diff --git a/lib/screens/common_elements.dart b/lib/screens/common_elements.dart index 9aef3b6..f9daa8f 100644 --- a/lib/screens/common_elements.dart +++ b/lib/screens/common_elements.dart @@ -192,6 +192,54 @@ class FaderTransition extends PageRouteBuilder { ); } +Future confirmPopop(BuildContext context, String title) async { + return showDialog( + context: context, + barrierDismissible: true, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: backgroundColor, + content: Text( + title, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + key: const Key('confirmPopop'), + child: const Text( + "Oui", + style: TextStyle( + fontSize: 21, + color: Color(0xffD80000), + ), + ), + onPressed: () { + Navigator.pop(context, true); + }, + ), + const SizedBox(width: 20), + TextButton( + child: const Text( + "Non", + style: TextStyle(fontSize: 21), + ), + onPressed: () { + Navigator.pop(context, false); + }, + ), + const SizedBox(height: 120) + ], + ) + ], + ); + }, + ); + } + // Widget geckoAppBar() { // return AppBar( // toolbarHeight: 60 * ratio, diff --git a/lib/screens/myWallets/choose_wallet.dart b/lib/screens/myWallets/choose_wallet.dart index 86fc4e0..cdd6000 100644 --- a/lib/screens/myWallets/choose_wallet.dart +++ b/lib/screens/myWallets/choose_wallet.dart @@ -152,14 +152,14 @@ class ChooseWalletScreen extends StatelessWidget { const Color(0xFFE7E7A6), ], )), - child: _repository.imageFile == null + child: _repository.imageCustomPath == null ? Image.asset( - 'assets/avatars/${_repository.imageName}', + 'assets/avatars/${_repository.imageDefaultPath}', alignment: Alignment.bottomCenter, scale: 0.5, ) - : Image.file( - _repository.imageFile!, + : Image.asset( + _repository.imageCustomPath!, alignment: Alignment.bottomCenter, scale: 0.5, ), diff --git a/lib/screens/myWallets/unlocking_wallet.dart b/lib/screens/myWallets/unlocking_wallet.dart index 210d268..a898567 100644 --- a/lib/screens/myWallets/unlocking_wallet.dart +++ b/lib/screens/myWallets/unlocking_wallet.dart @@ -189,7 +189,7 @@ class UnlockingWallet extends StatelessWidget { enableActiveFill: false, errorAnimationController: errorController, controller: _enterPin, - keyboardType: TextInputType.text, + keyboardType: TextInputType.visiblePassword, boxShadows: const [ BoxShadow( offset: Offset(0, 1), diff --git a/lib/screens/myWallets/wallet_options.dart b/lib/screens/myWallets/wallet_options.dart index 7e313c9..05346fc 100644 --- a/lib/screens/myWallets/wallet_options.dart +++ b/lib/screens/myWallets/wallet_options.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; @@ -42,6 +43,7 @@ class WalletOptions extends StatelessWidget { onWillPop: () { _walletOptions.isEditing = false; _walletOptions.isBalanceBlur = false; + _myWalletProvider.rebuildWidget(); Navigator.pop(context); return Future.value(true); }, @@ -55,13 +57,14 @@ class WalletOptions extends StatelessWidget { onPressed: () { _walletOptions.isEditing = false; _walletOptions.isBalanceBlur = false; + _myWalletProvider.rebuildWidget(); Navigator.pop(context); }), title: SizedBox( height: 22, child: Consumer( builder: (context, walletProvider, _) { - return Text(_walletOptions.nameController.text); + return Text(wallet.name!); }), ), ), @@ -146,20 +149,31 @@ class WalletOptions extends StatelessWidget { children: [ InkWell( onTap: () async { - wallet.imageFile = await (walletProvider.changeAvatar()); - if (wallet.imageFile != null) { + final _newPath = await (walletProvider.changeAvatar()); + if (_newPath != '') { + wallet.imageCustomPath = _newPath; walletBox.put(wallet.key, wallet); } walletProvider.reloadBuild(); }, - child: wallet.imageFile == null + child: wallet.imageCustomPath == null || wallet.imageCustomPath == '' ? Image.asset( - 'assets/avatars/${wallet.imageName}', + 'assets/avatars/${wallet.imageDefaultPath}', width: 110, ) - : Image.file( - wallet.imageFile!, - width: 110, + : Container( + width: 120, + height: 120, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.transparent, + image: DecorationImage( + fit: BoxFit.contain, + image: FileImage( + File(wallet.imageCustomPath!), + ), + ), + ), ), ), Positioned( @@ -167,7 +181,7 @@ class WalletOptions extends StatelessWidget { top: 0, child: InkWell( onTap: () async { - wallet.imageFile = await (walletProvider.changeAvatar()); + wallet.imageCustomPath = await (walletProvider.changeAvatar()); walletProvider.reloadBuild(); }, child: Image.asset( @@ -182,12 +196,10 @@ class WalletOptions extends StatelessWidget { Widget walletName(WalletOptionsProvider walletProvider, WalletOptionsProvider _walletOptions) { - bool _isNewNameValid = false; - if (_isNewNameValid == false) { + WidgetsBinding.instance.addPostFrameCallback((_) { _walletOptions.nameController.text = wallet.name!; - } else { - wallet.name = _walletOptions.nameController.text; - } + // _walletOptions.reloadBuild(); + }); return SizedBox( width: 260, @@ -219,8 +231,8 @@ class WalletOptions extends StatelessWidget { child: InkWell( key: const Key('renameWallet'), onTap: () async { - _isNewNameValid = - walletProvider.editWalletName(wallet.id(), isCesium: false); + // _isNewNameValid = + walletProvider.editWalletName(wallet.id(), isCesium: false); await Future.delayed(const Duration(milliseconds: 30)); walletProvider.walletNameFocus.requestFocus(); }, @@ -309,13 +321,13 @@ class WalletOptions extends StatelessWidget { MaterialPageRoute(builder: (context) { return HistoryScreen( pubkey: walletProvider.address.text, - avatar: wallet.imageFile == null + avatar: wallet.imageCustomPath == null ? Image.asset( - 'assets/avatars/${wallet.imageName}', + 'assets/avatars/${wallet.imageDefaultPath}', width: 110, ) - : Image.file( - wallet.imageFile!, + : Image.asset( + wallet.imageCustomPath!, width: 110, )); }), diff --git a/lib/screens/myWallets/wallets_home.dart b/lib/screens/myWallets/wallets_home.dart index f00d707..cd08c67 100644 --- a/lib/screens/myWallets/wallets_home.dart +++ b/lib/screens/myWallets/wallets_home.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; @@ -65,12 +67,6 @@ class WalletsHome extends StatelessWidget { ); } - // Widget cesiumWalletOptions(BuildContext context) { - // return Column(children: const [ - // Center(child: Text('This is a Cesium wallet')), - // ]); - // } - Widget chestOptions( BuildContext context, MyWalletsProvider _myWalletProvider) { return Column(children: [ @@ -171,6 +167,7 @@ class WalletsHome extends StatelessWidget { return CustomScrollView(slivers: [ const SliverToBoxAdapter(child: SizedBox(height: 20)), + SliverGrid.count( key: const Key('listWallets'), crossAxisCount: nTule, @@ -227,19 +224,29 @@ class WalletsHome extends StatelessWidget { child: // SvgPicture.asset('assets/chopp-gecko2.png', // semanticsLabel: 'Gecko', height: 48), - _repository.imageFile == null + _repository.imageCustomPath == null || + _repository.imageCustomPath == '' ? Image.asset( - 'assets/avatars/${_repository.imageName}', + 'assets/avatars/${_repository.imageDefaultPath}', alignment: Alignment.bottomCenter, scale: 0.5, ) - : Image.file( - _repository.imageFile!, - alignment: Alignment.bottomCenter, - scale: 0.5, + : Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.transparent, + image: DecorationImage( + fit: BoxFit.contain, + image: FileImage( + File( + _repository.imageCustomPath!), + ), + ), + ), ), )), - // balanceBuilder(context, _walletOptions.pubkey.text), + balanceBuilder(context, _repository.address!, + _repository.id()[1] == defaultWallet!.id()[1]), ListTile( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( @@ -308,36 +315,24 @@ class WalletsHome extends StatelessWidget { ]); } - Widget balanceBuilder(context, String _pubkey) { - return Query( - options: QueryOptions( - document: gql(getBalance), - variables: { - 'pubkey': _pubkey, - }, - // pollInterval: Duration(seconds: 1), - ), - builder: (QueryResult result, - {VoidCallback? refetch, FetchMore? fetchMore}) { - if (result.hasException) { - return Text(result.exception.toString()); - } - - if (result.isLoading) { - return const Text('Loading'); - } - String wBalanceUD; - if (result.data!['balance'] == null) { - wBalanceUD = '0.0'; - } else { - int wBalanceG1 = result.data!['balance']['amount']; - int currentUD = result.data!['currentUd']['amount']; - double wBalanceUDBrut = wBalanceG1 / currentUD; // .toString(); - wBalanceUD = - double.parse((wBalanceUDBrut).toStringAsFixed(2)).toString(); - } - return Text(wBalanceUD); - }); + Widget balanceBuilder(context, String _address, bool isDefault) { + return Container( + width: double.infinity, + color: isDefault ? orangeC : yellowC, + child: SizedBox( + height: 25, + child: Column(children: [ + const Spacer(), + // Text( + // '0.0 gd', + // textAlign: TextAlign.center, + // style: TextStyle(color: isDefault ? Colors.white : Colors.black), + // ), + balance( + context, _address, 15, isDefault ? Colors.white : Colors.black) + ]), + ), + ); } Widget addNewDerivation(context) { @@ -355,21 +350,32 @@ class WalletsHome extends StatelessWidget { child: InkWell( key: const Key('addDerivation'), onTap: () async { - await _myWalletProvider.generateNewDerivation( - context, _newDerivationName); + if (!_myWalletProvider.isNewDerivationLoading) { + await _myWalletProvider.generateNewDerivation( + context, _newDerivationName); + } }, child: Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration(color: floattingYellow), - child: const Center( - child: Text( - '+', - style: TextStyle( - fontSize: 150, - fontWeight: FontWeight.w700, - color: Color(0xFFFCB437)), - )), + child: Center( + child: _myWalletProvider.isNewDerivationLoading + ? SizedBox( + height: 60, + width: 60, + child: CircularProgressIndicator( + color: orangeC, + strokeWidth: 7, + ), + ) + : const Text( + '+', + style: TextStyle( + fontSize: 150, + fontWeight: FontWeight.w700, + color: Color(0xFFFCB437)), + )), )), ), ]))); diff --git a/lib/screens/onBoarding/10.dart b/lib/screens/onBoarding/10.dart index ff99af3..12f1204 100644 --- a/lib/screens/onBoarding/10.dart +++ b/lib/screens/onBoarding/10.dart @@ -116,7 +116,7 @@ class OnboardingStepTen extends StatelessWidget { enableActiveFill: false, errorAnimationController: errorController, controller: _enterPin, - keyboardType: TextInputType.text, + keyboardType: TextInputType.visiblePassword, boxShadows: const [ BoxShadow( offset: Offset(0, 1), diff --git a/pubspec.lock b/pubspec.lock index 3a70454..9fe5831 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -588,13 +588,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.3" - image_gallery_saver: - dependency: "direct main" - description: - name: image_gallery_saver - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.1" image_picker: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 8294cd6..21c7e01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: Pay with G1. # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.0.6+8 +version: 0.0.7+1 environment: sdk: '>=2.12.0 <3.0.0' @@ -36,7 +36,7 @@ dependencies: hive: ^2.0.4 hive_flutter: ^1.1.0 http: ^0.13.4 - image_gallery_saver: ^1.6.9 + # image_gallery_saver: ^1.6.9 image_picker: ^0.8.4 infinite_scroll_pagination: ^3.1.0 intl: ^0.17.0