[mob][photos] implemeted custom keyboard

This commit is contained in:
Aman Raj Singh Mourya 2024-06-18 13:37:32 +05:30
parent 89e7cfb357
commit aeb3e2be24
7 changed files with 435 additions and 55 deletions

View File

@ -46,6 +46,7 @@ class TextInputWidget extends StatefulWidget {
final ValueNotifier? isEmptyNotifier;
final List<TextInputFormatter>? textInputFormatter;
final TextInputType? textInputType;
final bool? fillColor;
const TextInputWidget({
this.onSubmit,
this.onChange,
@ -74,6 +75,7 @@ class TextInputWidget extends StatefulWidget {
this.isEmptyNotifier,
this.textInputFormatter,
this.textInputType,
this.fillColor = true,
super.key,
});
@ -161,7 +163,7 @@ class _TextInputWidgetState extends State<TextInputWidget> {
decoration: InputDecoration(
hintText: widget.hintText,
hintStyle: textTheme.body.copyWith(color: colorScheme.textMuted),
filled: true,
filled: widget.fillColor,
fillColor: colorScheme.fillFaint,
contentPadding: const EdgeInsets.fromLTRB(
12,

View File

@ -141,10 +141,16 @@ class _LockScreenOptionState extends State<LockScreenOption> {
),
appLock!
? Container()
: Text(
: Padding(
padding: const EdgeInsets.only(
left: 14,
right: 12,
),
child: Text(
'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.',
style: textTheme.smallFaint,
textAlign: TextAlign.center,
style: textTheme.miniFaint,
textAlign: TextAlign.left,
),
),
],
),

View File

@ -31,7 +31,6 @@ class _LockScreenOptionConfirmPasswordState
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 500));
_focusNode.requestFocus();
});
}
@ -51,7 +50,7 @@ class _LockScreenOptionConfirmPasswordState
Navigator.of(context).pop(true);
return;
}
_confirmPasswordController.clear();
await HapticFeedback.vibrate();
throw Exception("Incorrect password");
}
@ -145,6 +144,7 @@ class _LockScreenOptionConfirmPasswordState
child: TextInputWidget(
hintText: S.of(context).confirmPassword,
focusNode: _focusNode,
fillColor: false,
textCapitalization: TextCapitalization.none,
textEditingController: _confirmPasswordController,
isPasswordInput: true,

View File

@ -1,9 +1,7 @@
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:photos/core/configuration.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/common/dynamic_fab.dart";
import "package:photos/ui/components/buttons/icon_button_widget.dart";
import "package:pinput/pinput.dart";
@ -19,10 +17,10 @@ class _LockScreenOptionConfirmPinState
extends State<LockScreenOptionConfirmPin> {
final _confirmPinController = TextEditingController(text: null);
final Configuration _configuration = Configuration.instance;
final _focusNode = FocusNode();
Key _pinputKey = UniqueKey();
final _pinPutDecoration = PinTheme(
height: 50,
width: 50,
height: 48,
width: 48,
decoration: BoxDecoration(
border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)),
borderRadius: BorderRadius.circular(15.0),
@ -31,19 +29,29 @@ class _LockScreenOptionConfirmPinState
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 500));
_focusNode.requestFocus();
});
}
@override
void dispose() {
super.dispose();
_focusNode.dispose();
_confirmPinController.dispose();
}
String _pin = "";
void onClick(String number) {
_pin += number;
_confirmPinController.text = _pin;
}
void removeNum() {
if (_pin.isNotEmpty) {
_pin = _pin.substring(0, _pin.length - 1);
_confirmPinController.text = _pin;
}
return;
}
Future<void> _confirmPinMatch() async {
if (widget.pin == _confirmPinController.text) {
await _configuration.setPin(_confirmPinController.text);
@ -54,29 +62,20 @@ class _LockScreenOptionConfirmPinState
}
await HapticFeedback.vibrate();
_confirmPinController.clear();
_pin = "";
_pinputKey = UniqueKey();
}
@override
Widget build(BuildContext context) {
final colorTheme = getEnteColorScheme(context);
final textTheme = getEnteTextTheme(context);
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
FloatingActionButtonLocation? fabLocation() {
if (isKeypadOpen) {
return null;
} else {
return FloatingActionButtonLocation.centerFloat;
}
}
return Scaffold(
resizeToAvoidBottomInset: isKeypadOpen,
appBar: AppBar(
elevation: 0,
leading: IconButton(
onPressed: () {
FocusScope.of(context).unfocus();
Navigator.of(context).pop(false);
},
icon: Icon(
@ -85,17 +84,6 @@ class _LockScreenOptionConfirmPinState
),
),
),
floatingActionButton: DynamicFAB(
isKeypadOpen: isKeypadOpen,
buttonText: S.of(context).confirm,
isFormValid: _confirmPinController.text.isNotEmpty,
onPressedFunction: () async {
await _confirmPinMatch();
FocusScope.of(context).unfocus();
},
),
floatingActionButtonLocation: fabLocation(),
floatingActionButtonAnimator: NoScalingAnimation(),
body: Center(
child: Column(
children: [
@ -154,11 +142,12 @@ class _LockScreenOptionConfirmPinState
Padding(
padding: const EdgeInsets.fromLTRB(70, 0, 70, 0),
child: Pinput(
key: _pinputKey,
length: 4,
useNativeKeyboard: false,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
controller: _confirmPinController,
focusNode: _focusNode,
defaultPinTheme: _pinPutDecoration,
submittedPinTheme: _pinPutDecoration.copyWith(
textStyle: textTheme.h3Bold,
@ -195,18 +184,203 @@ class _LockScreenOptionConfirmPinState
if (value == widget.pin) {
return null;
} else {
value = null;
return 'PIN does not match';
}
},
onSubmitted: (value) {
_confirmPinMatch();
FocusScope.of(context).unfocus();
onCompleted: (value) async {
await Future.delayed(const Duration(milliseconds: 250));
await _confirmPinMatch();
},
),
),
const Spacer(),
Container(
padding: const EdgeInsets.all(2),
color: colorTheme.strokeFainter,
child: Column(
children: [
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
text: '',
number: '1',
onTap: () {
onClick('1');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
text: "ABC",
number: '2',
onTap: () {
onClick('2');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
text: "DEF",
number: '3',
onTap: () {
onClick('3');
},
),
],
),
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '4',
text: "GHI",
onTap: () {
onClick('4');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '5',
text: 'JKL',
onTap: () {
onClick('5');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '6',
text: 'MNO',
onTap: () {
onClick('6');
},
),
],
),
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '7',
text: 'PQRS',
onTap: () {
onClick('7');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '8',
text: 'TUV',
onTap: () {
onClick('8');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '9',
text: 'WXYZ',
onTap: () {
onClick('9');
},
),
],
),
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '',
text: '',
muteButton: true,
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '0',
text: '',
onTap: () {
onClick('0');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '',
text: '',
icons: const Icon(Icons.backspace_outlined),
onTap: () {
removeNum();
},
),
],
),
],
),
),
],
),
),
);
}
Widget buttonWidget({
colorTheme,
textTheme,
text,
number,
muteButton = false,
icons,
onTap,
}) {
return Expanded(
child: GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(6),
color: muteButton
? colorTheme.fillFaintPressed
: icons == null
? colorTheme.backgroundElevated2
: null,
),
child: Center(
child: muteButton
? Container()
: icons != null
? Container(
child: icons,
)
: Container(
padding: const EdgeInsets.all(4),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
number,
style: textTheme.h3,
),
Text(
text,
style: textTheme.small,
),
],
),
),
),
),
),
);
}
}

View File

@ -29,7 +29,6 @@ class _LockScreenOptionPasswordState extends State<LockScreenOptionPassword> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 500));
_focusNode.requestFocus();
});
}
@ -159,6 +158,7 @@ class _LockScreenOptionPasswordState extends State<LockScreenOptionPassword> {
child: TextInputWidget(
hintText: S.of(context).password,
focusNode: _focusNode,
fillColor: false,
textCapitalization: TextCapitalization.none,
textEditingController: _passwordController,
isPasswordInput: true,

View File

@ -20,22 +20,31 @@ class LockScreenOptionPin extends StatefulWidget {
class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
final _pinController = TextEditingController(text: null);
final _focusNode = FocusNode();
Key _pinputKey = UniqueKey();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 500));
_focusNode.requestFocus();
});
}
@override
void dispose() {
super.dispose();
_pinController.dispose();
_focusNode.dispose();
}
String _pin = "";
void onClick(String number) {
_pin += number;
_pinController.text = _pin;
}
void removeNum() {
if (_pin.isNotEmpty) {
_pin = _pin.substring(0, _pin.length - 1);
_pinController.text = _pin;
}
return;
}
Future<bool> confirmPinAuth(String code) async {
@ -43,6 +52,9 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
Navigator.of(context).pop(true);
return true;
}
_pinController.clear();
_pin = "";
_pinputKey = UniqueKey();
await HapticFeedback.vibrate();
return false;
}
@ -59,6 +71,7 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
),
);
_pinController.clear();
_pin = "";
}
}
@ -70,6 +83,7 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
borderRadius: BorderRadius.circular(15.0),
),
);
@override
Widget build(BuildContext context) {
final colorTheme = getEnteColorScheme(context);
@ -79,7 +93,6 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
elevation: 0,
leading: IconButton(
onPressed: () {
FocusScope.of(context).unfocus();
Navigator.of(context).pop(false);
},
icon: Icon(
@ -149,10 +162,11 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
Padding(
padding: const EdgeInsets.fromLTRB(70, 0, 70, 0),
child: Pinput(
key: _pinputKey,
length: 4,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
focusNode: _focusNode,
useNativeKeyboard: false,
controller: _pinController,
defaultPinTheme: _pinPutDecoration,
submittedPinTheme: _pinPutDecoration.copyWith(
@ -191,12 +205,10 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
return 'Invalid PIN';
}
: null,
autofocus: true,
errorText: '',
obscureText: true,
obscuringCharacter: '*',
onCompleted: (value) async {
FocusScope.of(context).unfocus();
await Future.delayed(const Duration(milliseconds: 250));
await _confirmPin(_pinController.text);
},
@ -204,9 +216,193 @@ class _LockScreenOptionPinState extends State<LockScreenOptionPin> {
),
],
),
const Spacer(),
Container(
padding: const EdgeInsets.all(2),
color: colorTheme.strokeFainter,
child: Column(
children: [
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
text: '',
number: '1',
onTap: () {
onClick('1');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
text: "ABC",
number: '2',
onTap: () {
onClick('2');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
text: "DEF",
number: '3',
onTap: () {
onClick('3');
},
),
],
),
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '4',
text: "GHI",
onTap: () {
onClick('4');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '5',
text: 'JKL',
onTap: () {
onClick('5');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '6',
text: 'MNO',
onTap: () {
onClick('6');
},
),
],
),
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '7',
text: 'PQRS',
onTap: () {
onClick('7');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '8',
text: 'TUV',
onTap: () {
onClick('8');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '9',
text: 'WXYZ',
onTap: () {
onClick('9');
},
),
],
),
Row(
children: [
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '',
text: '',
muteButton: true,
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '0',
text: '',
onTap: () {
onClick('0');
},
),
buttonWidget(
colorTheme: colorTheme,
textTheme: textTheme,
number: '',
text: '',
icons: const Icon(Icons.backspace_outlined),
onTap: () {
removeNum();
},
),
],
),
],
),
),
],
),
),
);
}
Widget buttonWidget({
colorTheme,
textTheme,
text,
number,
muteButton = false,
icons,
onTap,
}) {
return Expanded(
child: GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(6),
color: muteButton
? colorTheme.fillFaintPressed
: icons == null
? colorTheme.backgroundElevated2
: null,
),
child: Center(
child: muteButton
? Container()
: icons != null
? Container(
child: icons,
)
: Container(
padding: const EdgeInsets.all(4),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
number,
style: textTheme.h3,
),
Text(
text,
style: textTheme.small,
),
],
),
),
),
),
),
);
}
}

View File

@ -143,6 +143,8 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
captionedTextWidget: const CaptionedTextWidget(
title: 'App lock',
),
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
final bool result = await requestAuthentication(
context,