2020-01-04 19:31:52 +00:00
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
|
|
|
import 'package:cake_wallet/generated/i18n.dart';
|
|
|
|
|
|
|
|
abstract class PinCodeWidget extends StatefulWidget {
|
|
|
|
PinCodeWidget({Key key, this.onPinCodeEntered, this.hasLengthSwitcher})
|
|
|
|
: super(key: key);
|
|
|
|
|
|
|
|
final Function(List<int> pin, PinCodeState state) onPinCodeEntered;
|
|
|
|
final bool hasLengthSwitcher;
|
2020-01-08 12:26:34 +00:00
|
|
|
}
|
2020-01-04 19:31:52 +00:00
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
class PinCode extends PinCodeWidget {
|
|
|
|
PinCode(Function(List<int> pin, PinCodeState state) onPinCodeEntered,
|
|
|
|
bool hasLengthSwitcher, Key key)
|
|
|
|
: super(
|
|
|
|
key: key,
|
|
|
|
onPinCodeEntered: onPinCodeEntered,
|
|
|
|
hasLengthSwitcher: hasLengthSwitcher);
|
2020-01-04 19:31:52 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
PinCodeState createState() => PinCodeState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|
|
|
static const defaultPinLength = 4;
|
|
|
|
static const sixPinLength = 6;
|
|
|
|
static const fourPinLength = 4;
|
2020-01-08 12:26:34 +00:00
|
|
|
final _gridViewKey = GlobalKey();
|
2020-01-04 19:31:52 +00:00
|
|
|
|
|
|
|
int pinLength = defaultPinLength;
|
|
|
|
List<int> pin = List<int>.filled(defaultPinLength, null);
|
|
|
|
String title = S.current.enter_your_pin;
|
|
|
|
double _aspectRatio = 0;
|
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
void setTitle(String title) => setState(() => this.title = title);
|
2020-01-04 19:31:52 +00:00
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
void clear() => setState(() => pin = List<int>.filled(pinLength, null));
|
2020-01-04 19:31:52 +00:00
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
void onPinCodeEntered(PinCodeState state) =>
|
|
|
|
widget.onPinCodeEntered(state.pin, this);
|
2020-01-04 19:31:52 +00:00
|
|
|
|
|
|
|
void changePinLength(int length) {
|
2020-01-08 12:26:34 +00:00
|
|
|
final newPin = List<int>.filled(length, null);
|
2020-01-04 19:31:52 +00:00
|
|
|
|
|
|
|
setState(() {
|
|
|
|
pinLength = length;
|
|
|
|
pin = newPin;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
void setDefaultPinLength() {
|
2020-01-04 19:31:52 +00:00
|
|
|
final settingsStore = Provider.of<SettingsStore>(context);
|
|
|
|
|
|
|
|
pinLength = settingsStore.defaultPinLength;
|
|
|
|
changePinLength(pinLength);
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
void calculateAspectRatio() {
|
|
|
|
final renderBox =
|
|
|
|
_gridViewKey.currentContext.findRenderObject() as RenderBox;
|
|
|
|
final cellWidth = renderBox.size.width / 3;
|
|
|
|
final cellHeight = renderBox.size.height / 4;
|
|
|
|
|
|
|
|
if (cellWidth > 0 && cellHeight > 0) {
|
|
|
|
_aspectRatio = cellWidth / cellHeight;
|
|
|
|
}
|
2020-01-04 19:31:52 +00:00
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
void afterLayout(dynamic _) {
|
2020-01-04 19:31:52 +00:00
|
|
|
setDefaultPinLength();
|
2020-01-08 12:26:34 +00:00
|
|
|
calculateAspectRatio();
|
2020-01-04 19:31:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2020-01-08 12:26:34 +00:00
|
|
|
Widget build(BuildContext context) => Scaffold(body: body(context));
|
2020-01-04 19:31:52 +00:00
|
|
|
|
|
|
|
Widget body(BuildContext context) {
|
2020-05-29 15:10:11 +00:00
|
|
|
final deleteIconImage = Image.asset(
|
|
|
|
'assets/images/delete_icon.png',
|
|
|
|
color: Theme.of(context).primaryTextTheme.title.color,
|
|
|
|
);
|
|
|
|
final faceImage = Image.asset(
|
|
|
|
'assets/images/face.png',
|
|
|
|
color: Theme.of(context).primaryTextTheme.title.color,
|
|
|
|
);
|
|
|
|
|
2020-01-04 19:31:52 +00:00
|
|
|
return SafeArea(
|
|
|
|
child: Container(
|
2020-05-29 15:10:11 +00:00
|
|
|
color: Theme.of(context).backgroundColor,
|
2020-01-04 19:31:52 +00:00
|
|
|
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
|
|
|
|
child: Column(children: <Widget>[
|
|
|
|
Spacer(flex: 2),
|
|
|
|
Text(title,
|
2020-04-27 17:31:39 +00:00
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 20,
|
|
|
|
fontWeight: FontWeight.bold,
|
2020-05-29 15:10:11 +00:00
|
|
|
color: Theme.of(context).primaryTextTheme.title.color
|
|
|
|
)),
|
2020-01-04 19:31:52 +00:00
|
|
|
Spacer(flex: 3),
|
|
|
|
Container(
|
|
|
|
width: 180,
|
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: List.generate(pinLength, (index) {
|
|
|
|
const size = 10.0;
|
|
|
|
final isFilled = pin[index] != null;
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
width: size,
|
|
|
|
height: size,
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
shape: BoxShape.circle,
|
2020-05-29 15:10:11 +00:00
|
|
|
color: isFilled
|
|
|
|
? Theme.of(context).primaryTextTheme.title.color
|
|
|
|
: Theme.of(context).primaryTextTheme.caption.color,
|
2020-01-04 19:31:52 +00:00
|
|
|
));
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Spacer(flex: 2),
|
|
|
|
if (widget.hasLengthSwitcher) ...[
|
|
|
|
FlatButton(
|
|
|
|
onPressed: () {
|
|
|
|
changePinLength(pinLength == PinCodeState.fourPinLength
|
|
|
|
? PinCodeState.sixPinLength
|
|
|
|
: PinCodeState.fourPinLength);
|
|
|
|
},
|
|
|
|
child: Text(
|
|
|
|
_changePinLengthText(),
|
2020-05-29 15:10:11 +00:00
|
|
|
style: TextStyle(fontSize: 14.0, color: Theme.of(context).primaryTextTheme.caption.color),
|
2020-01-04 19:31:52 +00:00
|
|
|
))
|
|
|
|
],
|
|
|
|
Spacer(flex: 1),
|
|
|
|
Flexible(
|
|
|
|
flex: 24,
|
|
|
|
child: Container(
|
|
|
|
key: _gridViewKey,
|
|
|
|
child: _aspectRatio > 0
|
|
|
|
? GridView.count(
|
|
|
|
shrinkWrap: true,
|
|
|
|
crossAxisCount: 3,
|
|
|
|
childAspectRatio: _aspectRatio,
|
|
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
|
|
children: List.generate(12, (index) {
|
|
|
|
const double marginRight = 15;
|
|
|
|
const double marginLeft = 15;
|
|
|
|
|
|
|
|
if (index == 9) {
|
|
|
|
return Container(
|
|
|
|
margin: EdgeInsets.only(
|
|
|
|
left: marginLeft, right: marginRight),
|
2020-04-27 17:31:39 +00:00
|
|
|
child: FlatButton(
|
|
|
|
onPressed: () {},
|
2020-05-29 15:10:11 +00:00
|
|
|
color: Theme.of(context).backgroundColor,
|
2020-04-27 17:31:39 +00:00
|
|
|
shape: CircleBorder(),
|
|
|
|
child: faceImage,
|
2020-01-04 19:31:52 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
} else if (index == 10) {
|
|
|
|
index = 0;
|
|
|
|
} else if (index == 11) {
|
|
|
|
return Container(
|
|
|
|
margin: EdgeInsets.only(
|
|
|
|
left: marginLeft, right: marginRight),
|
|
|
|
child: FlatButton(
|
|
|
|
onPressed: () => _pop(),
|
2020-05-29 15:10:11 +00:00
|
|
|
color: Theme.of(context).backgroundColor,
|
2020-01-04 19:31:52 +00:00
|
|
|
shape: CircleBorder(),
|
|
|
|
child: deleteIconImage,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
margin: EdgeInsets.only(
|
|
|
|
left: marginLeft, right: marginRight),
|
|
|
|
child: FlatButton(
|
|
|
|
onPressed: () => _push(index),
|
2020-05-29 15:10:11 +00:00
|
|
|
color: Theme.of(context).backgroundColor,
|
2020-01-04 19:31:52 +00:00
|
|
|
shape: CircleBorder(),
|
|
|
|
child: Text('$index',
|
|
|
|
style: TextStyle(
|
2020-04-27 17:31:39 +00:00
|
|
|
fontSize: 30.0,
|
|
|
|
fontWeight: FontWeight.bold,
|
2020-05-29 15:10:11 +00:00
|
|
|
color: Theme.of(context).primaryTextTheme.title.color)),
|
2020-01-04 19:31:52 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
: null))
|
|
|
|
]),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
void _push(int num) {
|
2020-01-08 12:26:34 +00:00
|
|
|
if (currentPinLength() >= pinLength) {
|
2020-01-04 19:31:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var i = 0; i < pin.length; i++) {
|
|
|
|
if (pin[i] == null) {
|
|
|
|
setState(() => pin[i] = num);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
final _currentPinLength = currentPinLength();
|
|
|
|
|
|
|
|
if (_currentPinLength == pinLength) {
|
2020-01-04 19:31:52 +00:00
|
|
|
onPinCodeEntered(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _pop() {
|
2020-01-08 12:26:34 +00:00
|
|
|
if (currentPinLength() == 0) {
|
2020-01-04 19:31:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var i = pin.length - 1; i >= 0; i--) {
|
|
|
|
if (pin[i] != null) {
|
|
|
|
setState(() => pin[i] = null);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
int currentPinLength() {
|
2020-01-04 19:31:52 +00:00
|
|
|
return pin.fold(0, (v, e) {
|
|
|
|
if (e != null) {
|
|
|
|
return v + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return v;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
String _changePinLengthText() {
|
|
|
|
return S.current.use +
|
|
|
|
(pinLength == PinCodeState.fourPinLength
|
|
|
|
? '${PinCodeState.sixPinLength}'
|
|
|
|
: '${PinCodeState.fourPinLength}') +
|
|
|
|
S.current.digit_pin;
|
|
|
|
}
|
|
|
|
}
|