💄 Better otp input

This commit is contained in:
LittleSheep 2025-06-07 02:50:39 +08:00
parent bf77bfce64
commit dff8532229
3 changed files with 55 additions and 22 deletions

View File

@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/auth.dart'; import 'package:island/models/auth.dart';
@ -339,7 +340,7 @@ class _AuthFactorSheet extends HookConsumerWidget {
} }
Future<void> enableFactor() async { Future<void> enableFactor() async {
final passwordController = TextEditingController(); String? password;
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: builder:
@ -350,13 +351,15 @@ class _AuthFactorSheet extends HookConsumerWidget {
children: [ children: [
Text('authFactorEnableHint').tr(), Text('authFactorEnableHint').tr(),
const SizedBox(height: 16), const SizedBox(height: 16),
TextField( OtpTextField(
controller: passwordController, numberOfFields: 6,
obscureText: true, obscureText: false,
decoration: InputDecoration( showFieldAsBox: true,
labelText: 'password'.tr(), focusedBorderColor: Theme.of(context).colorScheme.primary,
border: const OutlineInputBorder(), onSubmit: (String verificationCode) {
), password = verificationCode;
},
textStyle: Theme.of(context).textTheme.titleLarge!,
), ),
], ],
), ),
@ -372,11 +375,10 @@ class _AuthFactorSheet extends HookConsumerWidget {
], ],
), ),
); );
final password = passwordController.text; if (confirmed == false ||
if (confirmed == false || password.isEmpty || !context.mounted) { (password?.isEmpty ?? true) ||
WidgetsBinding.instance.addPostFrameCallback((_) { !context.mounted) {
passwordController.dispose(); return;
});
} }
try { try {
final client = ref.read(apiClientProvider); final client = ref.read(apiClientProvider);
@ -385,9 +387,6 @@ class _AuthFactorSheet extends HookConsumerWidget {
data: jsonEncode(password), data: jsonEncode(password),
); );
if (context.mounted) Navigator.pop(context, true); if (context.mounted) Navigator.pop(context, true);
WidgetsBinding.instance.addPostFrameCallback((_) {
passwordController.dispose();
});
} catch (err) { } catch (err) {
showErrorAlert(err); showErrorAlert(err);
} }
@ -398,12 +397,37 @@ class _AuthFactorSheet extends HookConsumerWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
ListTile( Column(
title: Text(kFactorTypes[factor.type]!.$1).tr(), crossAxisAlignment: CrossAxisAlignment.start,
subtitle: Text(kFactorTypes[factor.type]!.$2).tr(), mainAxisAlignment: MainAxisAlignment.center,
leading: Icon(kFactorTypes[factor.type]!.$3), children: [
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 8), Icon(kFactorTypes[factor.type]!.$3, size: 32),
const Gap(8),
Text(kFactorTypes[factor.type]!.$1).tr(),
const Gap(4),
Text(
kFactorTypes[factor.type]!.$2,
style: Theme.of(context).textTheme.bodySmall,
).tr(),
const Gap(10),
Row(
children: [
if (factor.enabledAt == null)
Badge(
label: Text('Disabled'),
textColor: Theme.of(context).colorScheme.onSecondary,
backgroundColor: Theme.of(context).colorScheme.secondary,
)
else
Badge(
label: Text('Enabled'),
textColor: Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.primary,
), ),
],
),
],
).padding(all: 20),
const Divider(height: 1), const Divider(height: 1),
if (factor.enabledAt != null) if (factor.enabledAt != null)
ListTile( ListTile(

View File

@ -827,6 +827,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.6" version: "2.4.6"
flutter_otp_text_field:
dependency: "direct main"
description:
name: flutter_otp_text_field
sha256: e7e589dc51cde120d63da6db55f3cef618f5d013d12adba76137ca1a51ce1390
url: "https://pub.dev"
source: hosted
version: "1.5.1+1"
flutter_platform_alert: flutter_platform_alert:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -107,6 +107,7 @@ dependencies:
flutter_colorpicker: ^1.1.0 flutter_colorpicker: ^1.1.0
record: ^6.0.0 record: ^6.0.0
qr_flutter: ^4.1.0 qr_flutter: ^4.1.0
flutter_otp_text_field: ^1.5.1+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: