Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 14 additions & 61 deletions lib/shared/widgets/password_visibility_control.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';

/// #644: We want the password to be obscured most of the time
Expand All @@ -19,77 +16,33 @@ class PasswordVisibilityControl extends StatefulWidget {

class _PasswordVisibilityControlState extends State<PasswordVisibilityControl> {
bool _isObscured = true;
Offset _tapStartPosition = const Offset(0, 0);
Timer? _timer;

void _setObscureTo(bool isObscured) {
if (!mounted) {
return;
}
_timer?.cancel();
setState(() {
_isObscured = isObscured;
});
widget.onVisibilityChange(_isObscured);
}

bool _wasLongPressMoved(Offset position) {
final double distance = sqrt(pow(_tapStartPosition.dx - position.dx, 2) +
pow(_tapStartPosition.dy - position.dy, 2));
return distance > 20;
}

@override
Widget build(BuildContext context) {
return GestureDetector(
// NB: Both the long press and the tap start with `onTabDown`.
onTapDown: (TapDownDetails details) {
_tapStartPosition = details.globalPosition;
_setObscureTo(!_isObscured);
},
// #644: Most users expect the eye to react to the taps (behaving as a toggle)
// whereas long press handling starts too late to produce any visible reaction.
// Flashing the password for a few seconds in order not to befuddle the users.
onTapUp: (TapUpDetails details) {
_timer = Timer(const Duration(seconds: 2), () {
_setObscureTo(true);
});
},
onLongPressStart: (LongPressStartDetails details) {
_timer?.cancel();
},
onLongPressEnd: (LongPressEndDetails details) {
_setObscureTo(true);
},

// #644: Fires when we press on the eye and *a few seconds later* drag the finger off screen.
onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) {
if (_wasLongPressMoved(details.globalPosition)) {
_setObscureTo(true);
}
},
// #644: Fires when we press on the eye and *immediately* drag the finger off screen.
onVerticalDragStart: (DragStartDetails details) {
_setObscureTo(true);
},
onHorizontalDragStart: (DragStartDetails details) {
_setObscureTo(true);
},

child: InkWell(
mouseCursor: SystemMouseCursors.click,
child: SizedBox(
width: 60,
child: Icon(
_isObscured
? Icons.visibility_off_outlined
: Icons.visibility_outlined,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color
?.withValues(alpha: 0.7)),
),
return InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () => _setObscureTo(!_isObscured),
child: SizedBox(
width: 60,
child: Icon(
_isObscured
? Icons.visibility_off_outlined
: Icons.visibility_outlined,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color
?.withValues(alpha: 0.7)),
),
);
}
Expand Down
Loading