This commit is contained in:
M 2020-10-09 21:34:21 +03:00
parent 04e7c18841
commit f9cc21478a
16 changed files with 796 additions and 303 deletions

View file

@ -219,6 +219,8 @@ extern "C"
bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error)
{
Monero::WalletManagerFactory::setLogLevel(4);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager();
Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType);
@ -228,9 +230,9 @@ extern "C"
wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok)
if (wallet->status() != Monero::Wallet::Status_Ok)
{
error = strdup(errorString.c_str());
error = strdup(wallet->errorString().c_str());
return false;
}
@ -254,7 +256,7 @@ extern "C"
wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok)
if (status != Monero::Wallet::Status_Ok || !errorString.empty())
{
error = strdup(errorString.c_str());
return false;
@ -282,7 +284,7 @@ extern "C"
wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok)
if (status != Monero::Wallet::Status_Ok || !errorString.empty())
{
error = strdup(errorString.c_str());
return false;
@ -349,11 +351,13 @@ extern "C"
uint64_t get_full_balance(uint32_t account_index)
{
// return 0;
return get_current_wallet()->balance(account_index);
}
uint64_t get_unlocked_balance(uint32_t account_index)
{
// return 0;
return get_current_wallet()->unlockedBalance(account_index);
}
@ -681,6 +685,7 @@ extern "C"
void on_startup()
{
Monero::Utils::onStartup();
Monero::WalletManagerFactory::setLogLevel(4);
}
void rescan_blockchain()

View file

@ -2,4 +2,7 @@ class WalletCreationException implements Exception {
WalletCreationException({this.message});
final String message;
@override
String toString() => message;
}

View file

@ -1,4 +1,5 @@
import 'dart:ffi';
import 'package:cw_monero/wallet.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/convert_utf8_to_string.dart';
@ -49,6 +50,8 @@ void createWalletSync(
throw WalletCreationException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
// setupNodeSync(address: "node.moneroworld.com:18089");
}
bool isWalletExistSync({String path}) {

View file

@ -354,7 +354,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -493,7 +493,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -526,7 +526,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (

View file

@ -137,6 +137,11 @@ Future<void> ios_migrate_wallet_passwords() async {
final walletsDir = Directory('${appDocDir.path}/wallets');
final moneroWalletsDir = Directory('${walletsDir.path}/monero');
if (!moneroWalletsDir.existsSync() || moneroWalletsDir.listSync().isEmpty) {
await prefs.setBool('ios_migration_wallet_passwords_completed', true);
return;
}
moneroWalletsDir.listSync().forEach((item) async {
try {
if (item is Directory) {

View file

@ -95,7 +95,7 @@ Future<void> initialSetup(
tradesSource: tradesSource,
templates: templates,
exchangeTemplates: exchangeTemplates);
await bootstrap(navigatorKey);
bootstrap(navigatorKey);
monero_wallet.onStartup();
}

View file

@ -67,7 +67,7 @@ class MoneroWalletService extends WalletService<
return wallet;
} catch (e) {
// TODO: Implement Exception for wallet list service.
print('MoneroWalletsManager Error: $e');
print('MoneroWalletsManager Error: ${e.toString()}');
rethrow;
}
}

View file

@ -8,22 +8,25 @@ import 'package:cake_wallet/store/authentication_store.dart';
ReactionDisposer _onAuthenticationStateChange;
void startAuthenticationStateChange(AuthenticationStore authenticationStore,
GlobalKey<NavigatorState> navigatorKey) {
@required GlobalKey<NavigatorState> navigatorKey) {
_onAuthenticationStateChange ??= autorun((_) async {
final state = authenticationStore.state;
if (state == AuthenticationState.installed) {
await loadCurrentWallet();
return;
}
if (state == AuthenticationState.allowed) {
await navigatorKey.currentState
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
}
if (state == AuthenticationState.denied) {
await navigatorKey.currentState
.pushNamedAndRemoveUntil(Routes.welcome, (_) => false);
return;
}
});
}

View file

@ -58,7 +58,7 @@ class Router {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (BuildContext context, dynamic _) =>
Navigator.pushNamed(context, Routes.newWalletType)),
Navigator.pushNamed(context, Routes.newWallet)),
fullscreenDialog: true);
case Routes.newWalletType:
@ -128,7 +128,7 @@ class Router {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (BuildContext context, dynamic _) =>
Navigator.pushNamed(context, Routes.restoreWalletType)),
Navigator.pushNamed(context, Routes.restoreWalletFromSeed)),
fullscreenDialog: true);
case Routes.seed:
@ -137,15 +137,15 @@ class Router {
getIt.get<WalletSeedPage>(param1: settings.arguments as bool));
case Routes.restoreWalletFromSeed:
final args = settings.arguments as List<dynamic>;
final type = args.first as WalletType;
final language = type == WalletType.monero
? args[1] as String
: LanguageList.english;
// final args = settings.arguments as List<dynamic>;
final type = WalletType.monero; //args.first as WalletType;
// final language = type == WalletType.monero
// ? args[1] as String
// : LanguageList.english;
return CupertinoPageRoute<void>(
builder: (_) =>
RestoreWalletFromSeedPage(type: type, language: language));
RestoreWalletFromSeedPage(type: type));
case Routes.restoreWalletFromKeys:
final args = settings.arguments as List<dynamic>;

View file

@ -0,0 +1,83 @@
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
class RestoreFromKeysFrom extends StatefulWidget {
@override
_RestoreFromKeysFromState createState() => _RestoreFromKeysFromState();
}
class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
final _formKey = GlobalKey<FormState>();
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
final _nameController = TextEditingController();
final _addressController = TextEditingController();
final _viewKeyController = TextEditingController();
final _spendKeyController = TextEditingController();
final _wifController = TextEditingController();
@override
void initState() {
// _nameController.addListener(() =>
// widget.walletRestorationFromKeysVM.name = _nameController.text);
// _addressController.addListener(() =>
// widget.walletRestorationFromKeysVM.address = _addressController.text);
// _viewKeyController.addListener(() =>
// widget.walletRestorationFromKeysVM.viewKey = _viewKeyController.text);
// _spendKeyController.addListener(() =>
// widget.walletRestorationFromKeysVM.spendKey = _spendKeyController.text);
// _wifController.addListener(() =>
// widget.walletRestorationFromKeysVM.wif = _wifController.text);
super.initState();
}
@override
void dispose() {
_nameController.dispose();
_addressController.dispose();
_viewKeyController.dispose();
_spendKeyController.dispose();
_wifController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
BaseTextFormField(
controller: _addressController,
keyboardType: TextInputType.multiline,
maxLines: null,
hintText: S.of(context).restore_address,
),
Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _viewKeyController,
hintText: S.of(context).restore_view_key_private,
)),
Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _spendKeyController,
hintText: S.of(context).restore_spend_key_private,
)),
BlockchainHeightWidget(
key: _blockchainHeightKey,
onHeightChange: (height) {
// widget.walletRestorationFromKeysVM.height = height;
print(height);
}),
]),
),
);
}
}

View file

@ -1,3 +1,11 @@
import 'package:cake_wallet/src/screens/restore/restore_from_keys.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/routes.dart';
@ -7,48 +15,162 @@ import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/core/seed_validator.dart';
import 'package:cake_wallet/core/mnemonic_length.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class RestoreWalletFromSeedPage extends BasePage {
RestoreWalletFromSeedPage({@required this.type, @required this.language});
RestoreWalletFromSeedPage({@required this.type});
final WalletType type;
final String language;
final formKey = GlobalKey<_RestoreFromSeedFormState>();
final String language = 'en';
// final formKey = GlobalKey<_RestoreFromSeedFormState>();
// final formKey = GlobalKey<_RestoreFromSeedFormState>();
@override
String get title => S.current.restore_title_from_seed;
@override
Color get titleColor => Colors.white;
final controller = PageController(initialPage: 0);
Widget _page(BuildContext context, int index) {
if (_pages == null || _pages.isEmpty) {
_setPages(context);
}
return _pages[index];
}
int _pageLength(BuildContext context) {
if (_pages == null || _pages.isEmpty) {
_setPages(context);
}
return _pages.length;
}
void _setPages(BuildContext context) {
_pages = <Widget>[
Container(
padding: EdgeInsets.only(left: 25, right: 25),
child: Column(children: [
SeedWidget(
maxLength: mnemonicLength(WalletType.monero),
onMnemonicChange: (seed) => null,
onFinish: () => null,
// Navigator.of(context).pushNamed(
// Routes.restoreWalletFromSeedDetails,
// arguments: [WalletType.monero, '', '']),
validator: SeedValidator(type: WalletType.monero, language: ''),
),
GestureDetector(
onTap: () async {
final selected = await showPopUp<String>(
context: context,
builder: (BuildContext context) =>
SeedLanguagePicker(selected: 'English')); //key: _pickerKey
print('Seletec $selected');
},
child: Container(
color: Colors.transparent,
padding: EdgeInsets.only(top: 20.0),
child: IgnorePointer(child: BaseTextFormField(
enableInteractiveSelection: false,
readOnly: true,
hintText: 'Language',
initialValue: 'English (Seed language)')))),
BlockchainHeightWidget(
// key: _blockchainHeightKey,
onHeightChange: (height) {
// widget.walletRestorationFromKeysVM.height = height;
print(height);
})
])),
RestoreFromKeysFrom(),
// Container(color: Colors.yellow)
];
}
List<Widget> _pages;
@override
Color get backgroundLightColor => Colors.transparent;
Widget body(BuildContext context) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Expanded(
child: PageView.builder(
onPageChanged: (page) {
print('Page index $page');
},
controller: controller,
itemCount: _pageLength(context),
itemBuilder: (context, index) => _page(context, index))),
Padding(
padding: EdgeInsets.only(top: 10),
child: SmoothPageIndicator(
controller: controller,
count: _pageLength(context),
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
activeDotColor: Theme.of(context).hintColor),
)),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 40, left: 25, right: 25),
child: PrimaryButton(
text: S.of(context).restore_recover,
isDisabled: false,
onPressed: () => null,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white)),
]);
@override
Color get backgroundDarkColor => Colors.transparent;
@override
bool get resizeToAvoidBottomPadding => false;
@override
Widget body(BuildContext context) =>
RestoreFromSeedForm(key: formKey, type: type, language: language,
leading: leading(context), middle: middle(context));
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
body: Container(
color: Theme.of(context).backgroundColor,
child: body(context)
)
);
// return GestureDetector(
// onTap: () =>
// SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
// child: ScrollableWithBottomSection(
// bottomSection: Column(children: [
// GestureDetector(
// onTap: () {},
// child: Text('Switch to restore from keys',
// style: TextStyle(fontSize: 15, color: Theme.of(context).hintColor))),
// SizedBox(height: 30),
// PrimaryButton(
// text: S.of(context).restore_next,
// isDisabled: false,
// onPressed: () => null,
// color: Theme.of(context).accentTextTheme.body2.color,
// textColor: Colors.white)
// ]),
// contentPadding: EdgeInsets.only(bottom: 24),
// content: Container(
// padding: EdgeInsets.only(left: 25, right: 25),
// child: Column(children: [
// SeedWidget(
// maxLength: mnemonicLength(type),
// onMnemonicChange: (seed) => null,
// onFinish: () => Navigator.of(context).pushNamed(
// Routes.restoreWalletFromSeedDetails,
// arguments: [type, language, '']),
// validator: SeedValidator(type: type, language: language),
// ),
// // SizedBox(height: 15),
// // BaseTextFormField(hintText: 'Language', initialValue: 'English'),
// BlockchainHeightWidget(
// // key: _blockchainHeightKey,
// onHeightChange: (height) {
// // widget.walletRestorationFromKeysVM.height = height;
// print(height);
// })
// ]))),
// );
}
}
class RestoreFromSeedForm extends StatefulWidget {
RestoreFromSeedForm({Key key, this.type, this.language, this.leading, this.middle}) : super(key: key);
RestoreFromSeedForm(
{Key key, this.type, this.language, this.leading, this.middle})
: super(key: key);
final WalletType type;
final String language;
final Widget leading;
@ -59,27 +181,46 @@ class RestoreFromSeedForm extends StatefulWidget {
}
class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
final _seedKey = GlobalKey<SeedWidgetState>();
// final _seedKey = GlobalKey<SeedWidgetState>();
String mnemonic() => _seedKey.currentState.items.map((e) => e.text).join(' ');
String mnemonic() =>
''; // _seedKey.currentState.items.map((e) => e.text).join(' ');
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () =>
SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
child: SeedWidget(
key: _seedKey,
maxLength: mnemonicLength(widget.type),
onMnemonicChange: (seed) => null,
onFinish: () => Navigator.of(context).pushNamed(
Routes.restoreWalletFromSeedDetails,
arguments: [widget.type, widget.language, mnemonic()]),
leading: widget.leading,
middle: widget.middle,
validator:
SeedValidator(type: widget.type, language: widget.language),
),
child: Container(
padding: EdgeInsets.only(left: 25, right: 25),
// color: Colors.blue,
// height: 300,
child: Column(children: [
SeedWidget(
// key: _seedKey,
maxLength: mnemonicLength(widget.type),
onMnemonicChange: (seed) => null,
onFinish: () => Navigator.of(context).pushNamed(
Routes.restoreWalletFromSeedDetails,
arguments: [widget.type, widget.language, mnemonic()]),
leading: widget.leading,
middle: widget.middle,
validator:
SeedValidator(type: widget.type, language: widget.language),
),
BlockchainHeightWidget(
// key: _blockchainHeightKey,
onHeightChange: (height) {
// widget.walletRestorationFromKeysVM.height = height;
print(height);
}),
Container(
color: Colors.green,
width: 100,
height: 56,
child: BaseTextFormField(
hintText: 'Language', initialValue: 'English')),
])),
);
}
}

View file

@ -35,13 +35,15 @@ class SeedLanguageFormState extends State<SeedLanguageForm> {
static const aspectRatioImage = 1.22;
final walletNameImage = Image.asset('assets/images/wallet_name.png');
final walletNameLightImage = Image.asset('assets/images/wallet_name_light.png');
final walletNameLightImage =
Image.asset('assets/images/wallet_name_light.png');
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
@override
Widget build(BuildContext context) {
final walletImage = getIt.get<SettingsStore>().isDarkTheme
? walletNameImage : walletNameLightImage;
? walletNameImage
: walletNameLightImage;
return Container(
padding: EdgeInsets.only(top: 24),
@ -78,8 +80,8 @@ class SeedLanguageFormState extends State<SeedLanguageForm> {
bottomSection: Observer(
builder: (context) {
return PrimaryButton(
onPressed: () => widget
.onConfirm(context, _languageSelectorKey.currentState.selected),
onPressed: () => widget.onConfirm(
context, _languageSelectorKey.currentState.selected),
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white);

View file

@ -175,7 +175,7 @@ class WalletListBodyState extends State<WalletListBody> {
SizedBox(height: 10.0),
PrimaryImageButton(
onPressed: () =>
Navigator.of(context).pushNamed(Routes.restoreWalletType),
Navigator.of(context).pushNamed(Routes.restoreWalletFromSeed),
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).accentTextTheme.caption.color,

View file

@ -103,7 +103,7 @@ class WelcomePage extends BasePage {
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
onPressed: () => Navigator.pushNamed(context, Routes.newWallet),
onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context).accentTextTheme.subtitle.decorationColor,
@ -113,7 +113,7 @@ class WelcomePage extends BasePage {
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptions),
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome),
image: restoreWalletImage,
text: S.of(context).restore_wallet,
color: Theme.of(context).accentTextTheme.caption.color,

View file

@ -19,11 +19,14 @@ class BaseTextFormField extends StatelessWidget {
this.suffix,
this.suffixIcon,
this.enabled = true,
this.readOnly = false,
this.enableInteractiveSelection = true,
this.validator,
this.textStyle,
this.placeholderTextStyle,
this.maxLength,
this.focusNode});
this.focusNode,
this.initialValue});
final TextEditingController controller;
final TextInputType keyboardType;
@ -46,10 +49,16 @@ class BaseTextFormField extends StatelessWidget {
final TextStyle textStyle;
final int maxLength;
final FocusNode focusNode;
final bool readOnly;
final bool enableInteractiveSelection;
String initialValue;
@override
Widget build(BuildContext context) {
return TextFormField(
enableInteractiveSelection: enableInteractiveSelection,
readOnly: readOnly,
initialValue: initialValue,
focusNode: focusNode,
controller: controller,
keyboardType: keyboardType,
@ -60,9 +69,11 @@ class BaseTextFormField extends StatelessWidget {
inputFormatters: inputFormatters,
enabled: enabled,
maxLength: maxLength,
style: textStyle ?? TextStyle(
fontSize: 16.0,
color: textColor ?? Theme.of(context).primaryTextTheme.title.color),
style: textStyle ??
TextStyle(
fontSize: 16.0,
color:
textColor ?? Theme.of(context).primaryTextTheme.title.color),
decoration: InputDecoration(
prefix: prefix,
prefixIcon: prefixIcon,
@ -75,15 +86,18 @@ class BaseTextFormField extends StatelessWidget {
hintText: hintText,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor,
color: borderColor ??
Theme.of(context).primaryTextTheme.title.backgroundColor,
width: 1.0)),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor,
color: borderColor ??
Theme.of(context).primaryTextTheme.title.backgroundColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor,
color: borderColor ??
Theme.of(context).primaryTextTheme.title.backgroundColor,
width: 1.0))),
validator: validator,
);

View file

@ -1,3 +1,5 @@
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -8,6 +10,166 @@ import 'package:cake_wallet/entities/mnemonic_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/widgets.dart';
class Annotation extends Comparable<Annotation> {
Annotation({@required this.range, this.style});
final TextRange range;
final TextStyle style;
@override
int compareTo(Annotation other) => range.start.compareTo(other.range.start);
}
class TextAnnotation extends Comparable<TextAnnotation> {
TextAnnotation({@required this.text, this.style});
final TextStyle style;
final String text;
@override
int compareTo(TextAnnotation other) => text.compareTo(other.text);
}
class AnnotatedEditableText extends EditableText {
AnnotatedEditableText({
Key key,
FocusNode focusNode,
TextEditingController controller,
TextStyle style,
ValueChanged<String> onChanged,
ValueChanged<String> onSubmitted,
Color cursorColor,
Color selectionColor,
Color backgroundCursorColor,
TextSelectionControls selectionControls,
@required this.words,
}) : textAnnotations = words
.map((word) => TextAnnotation(
text: word,
style: TextStyle(
color: Colors.black,
backgroundColor: Colors.transparent,
fontWeight: FontWeight.normal,
fontSize: 20)))
.toList(),
super(
maxLines: null,
key: key,
focusNode: focusNode,
controller: controller,
cursorColor: cursorColor,
style: style,
keyboardType: TextInputType.text,
autocorrect: false,
autofocus: false,
selectionColor: selectionColor,
selectionControls: selectionControls,
backgroundCursorColor: backgroundCursorColor,
onChanged: onChanged,
onSubmitted: onSubmitted,
toolbarOptions: const ToolbarOptions(
copy: true,
cut: true,
paste: true,
selectAll: true,
),
enableSuggestions: false,
enableInteractiveSelection: true,
showSelectionHandles: true,
showCursor: true,
) {
textAnnotations.add(TextAnnotation(
text: ' ', style: TextStyle(backgroundColor: Colors.transparent)));
}
final List<String> words;
final List<TextAnnotation> textAnnotations;
@override
AnnotatedEditableTextState createState() => AnnotatedEditableTextState();
}
class AnnotatedEditableTextState extends EditableTextState {
@override
AnnotatedEditableText get widget => super.widget as AnnotatedEditableText;
List<Annotation> getRanges() {
final source = widget.textAnnotations
.map((item) => range(item.text, textEditingValue.text)
.map((range) => Annotation(style: item.style, range: range)))
.expand((e) => e)
.toList();
final result = List<Annotation>();
final text = textEditingValue.text;
source.sort();
Annotation prev;
for (var item in source) {
if (prev == null) {
if (item.range.start > 0) {
result.add(Annotation(
range: TextRange(start: 0, end: item.range.start),
style: TextStyle(
color: Colors.black, backgroundColor: Colors.transparent)));
}
result.add(item);
prev = item;
continue;
} else {
if (prev.range.end > item.range.start) {
// throw StateError('Invalid (intersecting) ranges for annotated field');
} else if (prev.range.end < item.range.start) {
result.add(Annotation(
range: TextRange(start: prev.range.end, end: item.range.start),
style: TextStyle(
color: Colors.red, backgroundColor: Colors.transparent)));
}
result.add(item);
prev = item;
}
}
if (result.length > 0 && result.last.range.end < text.length) {
result.add(Annotation(
range: TextRange(start: result.last.range.end, end: text.length),
style: TextStyle( backgroundColor: Colors.transparent)));
}
return result;
}
List<TextRange> range(String pattern, String source) {
final result = List<TextRange>();
for (int index = source.indexOf(pattern);
index >= 0;
index = source.indexOf(pattern, index + 1)) {
final start = index;
final end = start + pattern.length;
result.add(TextRange(start: start, end: end));
}
return result;
}
@override
TextSpan buildTextSpan() {
final text = textEditingValue.text;
final ranges = getRanges();
if (ranges.isNotEmpty) {
return TextSpan(
style: widget.style,
children: ranges
.map((item) => TextSpan(
style: item.style, text: item.range.textInside(text)))
.toList());
}
return TextSpan(style: widget.style, text: text);
}
}
class SeedWidget extends StatefulWidget {
SeedWidget(
{Key key,
@ -48,10 +210,13 @@ class SeedWidgetState extends State<SeedWidget> {
@override
void initState() {
super.initState();
showPlaceholder = true;
isValid = false;
isCurrentMnemonicValid = false;
_seedController
.addListener(() => changeCurrentMnemonic(_seedController.text));
focusNode.addListener(() => setState(() =>
showPlaceholder = !focusNode.hasFocus && controller.text.isEmpty));
}
void addMnemonic(String text) {
@ -198,239 +363,308 @@ class SeedWidgetState extends State<SeedWidget> {
return isValid;
}
final controller = TextEditingController();
final focusNode = FocusNode();
bool showPlaceholder;
final words =
SeedValidator.getWordList(type: WalletType.monero, language: 'en');
Future<void> _pasteAddress() async {
final value = await Clipboard.getData('text/plain');
if (value?.text?.isNotEmpty ?? false) {
controller.text = value.text;
}
}
@override
Widget build(BuildContext context) {
print('build');
return Container(
child: Column(children: [
Flexible(
fit: FlexFit.tight,
flex: 2,
child: Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(0),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [
Theme.of(context).primaryTextTheme.subhead.color,
Theme.of(context).primaryTextTheme.subhead.decorationColor,
], begin: Alignment.topLeft, end: Alignment.bottomRight)),
child: Column(
children: <Widget>[
CupertinoNavigationBar(
leading: widget.leading,
middle: widget.middle,
backgroundColor: Colors.transparent,
border: null,
),
Expanded(
child: Container(
padding: EdgeInsets.all(24),
alignment: Alignment.topLeft,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).restore_active_seed,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.textTheme
.overline
.backgroundColor),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Wrap(
children: items.map((item) {
final isValid =
widget.validator.isValid(item);
final isSelected = selectedItem == item;
return InkWell(
onTap: () => onMnemonicTap(item),
child: Container(
decoration: BoxDecoration(
color: isValid
? Colors.transparent
: Palette.red),
margin: EdgeInsets.only(
right: 7, bottom: 8),
child: Text(
item.toString(),
style: TextStyle(
color: isValid
? Colors.white
: Colors.grey,
fontSize: 16,
fontWeight: isSelected
? FontWeight.w900
: FontWeight.w600,
decoration: isSelected
? TextDecoration.underline
: TextDecoration.none),
)),
);
}).toList(),
))
],
),
),
))
],
)),
),
Flexible(
fit: FlexFit.tight,
flex: 3,
child: Padding(
padding:
EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
S.of(context).restore_new_seed,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Stack(children: [
SizedBox(height: 35),
if (showPlaceholder)
Positioned(
top: 10,
left: 0,
child: Text('Enter your seed',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color:
Theme.of(context).primaryTextTheme.title.color),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: TextFormField(
key: _seedTextFieldKey,
onFieldSubmitted: (text) => isCurrentMnemonicValid
? saveCurrentMnemonicToItems()
: null,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.normal,
color:
Theme.of(context).primaryTextTheme.title.color),
controller: _seedController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
suffixIcon: GestureDetector(
behavior: HitTestBehavior.opaque,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 145),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text('${items.length}/$maxLength',
style: TextStyle(
color: Theme.of(context)
.accentTextTheme
.display2
.decorationColor,
fontWeight: FontWeight.normal,
fontSize: 16)),
SizedBox(width: 10),
InkWell(
onTap: () async =>
Clipboard.getData('text/plain').then(
(clipboard) =>
replaceText(clipboard.text)),
child: Container(
height: 35,
padding: EdgeInsets.all(7),
decoration: BoxDecoration(
color: Theme.of(context)
.accentTextTheme
.caption
.color,
borderRadius:
BorderRadius.circular(10.0)),
child: Text(
S.of(context).paste,
style: TextStyle(
color: Palette.blueCraiola),
)),
)
],
),
),
),
hintStyle: TextStyle(
color: Theme.of(context)
.accentTextTheme
.display2
.decorationColor,
fontWeight: FontWeight.normal,
fontSize: 16),
hintText:
S.of(context).restore_from_seed_placeholder,
errorText: _errorMessage,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentTextTheme
.subtitle
.backgroundColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentTextTheme
.subtitle
.backgroundColor,
width: 1.0))),
enableInteractiveSelection: false,
),
)
]),
)),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: Row(
children: <Widget>[
Flexible(
child: Padding(
padding: EdgeInsets.only(right: 8),
child: PrimaryButton(
onPressed: clear,
text: S.of(context).clear,
color: Colors.orange,
textColor: Colors.white,
isDisabled: items.isEmpty,
),
)),
Flexible(
child: Padding(
padding: EdgeInsets.only(left: 8),
child: (selectedItem == null && items.length == maxLength)
? PrimaryButton(
text: S.of(context).restore_next,
isDisabled: !isSeedValid(),
onPressed: () => widget.onFinish != null
? widget.onFinish()
: null,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white)
: PrimaryButton(
text: selectedItem != null
? S.of(context).save
: S.of(context).add_new_word,
onPressed: () => isCurrentMnemonicValid
? saveCurrentMnemonicToItems()
: null,
onDisabledPressed: () => showErrorIfExist(),
isDisabled: !isCurrentMnemonicValid,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white),
),
)
],
))
]),
);
fontSize: 16.0, color: Theme.of(context).hintColor))),
Padding(
padding: EdgeInsets.only(right: 40, top: 10),
child: AnnotatedEditableText(
cursorColor: Colors.green,
backgroundCursorColor: Colors.blue,
style: TextStyle(
fontSize: 20,
color: Colors.red,
fontWeight: FontWeight.normal,
backgroundColor: Colors.transparent),
focusNode: focusNode,
controller: controller,
words: words)),
Positioned(
top: 0,
right: 0,
child: Container(
width: 34,
height: 34,
child: InkWell(
onTap: () async => _pasteAddress(),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).hintColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/duplicate.png',
color: Theme.of(context)
.primaryTextTheme
.display1
.decorationColor)),
)))
]),
Container(
margin: EdgeInsets.only(top: 15),
height: 1.0,
color: Theme.of(context).primaryTextTheme.title.backgroundColor),
]));
// return Container(
// child: Column(children: [
// Flexible(
// fit: FlexFit.tight,
// flex: 2,
// child: Container(
// width: double.infinity,
// height: double.infinity,
// padding: EdgeInsets.all(0),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.only(
// bottomLeft: Radius.circular(24),
// bottomRight: Radius.circular(24)),
// gradient: LinearGradient(colors: [
// Theme.of(context).primaryTextTheme.subhead.color,
// Theme.of(context).primaryTextTheme.subhead.decorationColor,
// ], begin: Alignment.topLeft, end: Alignment.bottomRight)),
// child: Column(
// children: <Widget>[
// CupertinoNavigationBar(
// leading: widget.leading,
// middle: widget.middle,
// backgroundColor: Colors.transparent,
// border: null,
// ),
// Expanded(
// child: Container(
// padding: EdgeInsets.all(24),
// alignment: Alignment.topLeft,
// child: SingleChildScrollView(
// child: Column(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: <Widget>[
// Text(
// S.of(context).restore_active_seed,
// style: TextStyle(
// fontSize: 14,
// fontWeight: FontWeight.w500,
// color: Theme.of(context)
// .textTheme
// .overline
// .backgroundColor),
// ),
// Padding(
// padding: EdgeInsets.only(top: 5),
// child: Wrap(
// children: items.map((item) {
// final isValid =
// widget.validator.isValid(item);
// final isSelected = selectedItem == item;
//
// return InkWell(
// onTap: () => onMnemonicTap(item),
// child: Container(
// decoration: BoxDecoration(
// color: isValid
// ? Colors.transparent
// : Palette.red),
// margin: EdgeInsets.only(
// right: 7, bottom: 8),
// child: Text(
// item.toString(),
// style: TextStyle(
// color: isValid
// ? Colors.white
// : Colors.grey,
// fontSize: 16,
// fontWeight: isSelected
// ? FontWeight.w900
// : FontWeight.w600,
// decoration: isSelected
// ? TextDecoration.underline
// : TextDecoration.none),
// )),
// );
// }).toList(),
// ))
// ],
// ),
// ),
// ))
// ],
// )),
// ),
// Flexible(
// fit: FlexFit.tight,
// flex: 3,
// child: Padding(
// padding:
// EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: <Widget>[
// Text(
// S.of(context).restore_new_seed,
// style: TextStyle(
// fontSize: 20,
// fontWeight: FontWeight.w500,
// color:
// Theme.of(context).primaryTextTheme.title.color),
// ),
// Padding(
// padding: EdgeInsets.only(top: 24),
// child: TextFormField(
// key: _seedTextFieldKey,
// onFieldSubmitted: (text) => isCurrentMnemonicValid
// ? saveCurrentMnemonicToItems()
// : null,
// style: TextStyle(
// fontSize: 16.0,
// fontWeight: FontWeight.normal,
// color:
// Theme.of(context).primaryTextTheme.title.color),
// controller: _seedController,
// textInputAction: TextInputAction.done,
// decoration: InputDecoration(
// suffixIcon: GestureDetector(
// behavior: HitTestBehavior.opaque,
// child: ConstrainedBox(
// constraints: BoxConstraints(maxWidth: 145),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: <Widget>[
// Text('${items.length}/$maxLength',
// style: TextStyle(
// color: Theme.of(context)
// .accentTextTheme
// .display2
// .decorationColor,
// fontWeight: FontWeight.normal,
// fontSize: 16)),
// SizedBox(width: 10),
// InkWell(
// onTap: () async =>
// Clipboard.getData('text/plain').then(
// (clipboard) =>
// replaceText(clipboard.text)),
// child: Container(
// height: 35,
// padding: EdgeInsets.all(7),
// decoration: BoxDecoration(
// color: Theme.of(context)
// .accentTextTheme
// .caption
// .color,
// borderRadius:
// BorderRadius.circular(10.0)),
// child: Text(
// S.of(context).paste,
// style: TextStyle(
// color: Palette.blueCraiola),
// )),
// )
// ],
// ),
// ),
// ),
// hintStyle: TextStyle(
// color: Theme.of(context)
// .accentTextTheme
// .display2
// .decorationColor,
// fontWeight: FontWeight.normal,
// fontSize: 16),
// hintText:
// S.of(context).restore_from_seed_placeholder,
// errorText: _errorMessage,
// focusedBorder: UnderlineInputBorder(
// borderSide: BorderSide(
// color: Theme.of(context)
// .accentTextTheme
// .subtitle
// .backgroundColor,
// width: 1.0)),
// enabledBorder: UnderlineInputBorder(
// borderSide: BorderSide(
// color: Theme.of(context)
// .accentTextTheme
// .subtitle
// .backgroundColor,
// width: 1.0))),
// enableInteractiveSelection: false,
// ),
// )
// ]),
// )),
// Padding(
// padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
// child: Row(
// children: <Widget>[
// Flexible(
// child: Padding(
// padding: EdgeInsets.only(right: 8),
// child: PrimaryButton(
// onPressed: clear,
// text: S.of(context).clear,
// color: Colors.orange,
// textColor: Colors.white,
// isDisabled: items.isEmpty,
// ),
// )),
// Flexible(
// child: Padding(
// padding: EdgeInsets.only(left: 8),
// child: (selectedItem == null && items.length == maxLength)
// ? PrimaryButton(
// text: S.of(context).restore_next,
// isDisabled: !isSeedValid(),
// onPressed: () => widget.onFinish != null
// ? widget.onFinish()
// : null,
// color: Theme.of(context).accentTextTheme.body2.color,
// textColor: Colors.white)
// : PrimaryButton(
// text: selectedItem != null
// ? S.of(context).save
// : S.of(context).add_new_word,
// onPressed: () => isCurrentMnemonicValid
// ? saveCurrentMnemonicToItems()
// : null,
// onDisabledPressed: () => showErrorIfExist(),
// isDisabled: !isCurrentMnemonicValid,
// color: Theme.of(context).accentTextTheme.body2.color,
// textColor: Colors.white),
// ),
// )
// ],
// ))
// ]),
// );
}
}