yumi-flutter/lib/ui_kit/widgets/room/room_password_dialog.dart
2026-05-15 17:43:49 +08:00

268 lines
7.3 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
typedef RoomPasswordSubmit = Future<String?> Function(String password);
class RoomPasswordDialog extends StatefulWidget {
static const String dialogTag = 'showRoomPasswordDialog';
const RoomPasswordDialog({
super.key,
required this.onSubmit,
required this.onCancel,
required this.onSuccess,
});
final RoomPasswordSubmit onSubmit;
final VoidCallback onCancel;
final ValueChanged<String> onSuccess;
static Future<String?> show({required RoomPasswordSubmit onSubmit}) {
final completer = Completer<String?>();
var isCompleted = false;
void complete(String? password) {
if (isCompleted) {
return;
}
isCompleted = true;
completer.complete(password);
}
SmartDialog.dismiss(tag: dialogTag);
SmartDialog.show(
tag: dialogTag,
alignment: Alignment.center,
animationType: SmartAnimationType.fade,
backType: SmartBackType.block,
clickMaskDismiss: false,
maskColor: Colors.black.withValues(alpha: 0.58),
onDismiss: () {
complete(null);
},
builder: (_) {
return RoomPasswordDialog(
onSubmit: onSubmit,
onCancel: () {
complete(null);
SmartDialog.dismiss(tag: dialogTag);
},
onSuccess: (password) {
complete(password);
SmartDialog.dismiss(tag: dialogTag);
},
);
},
);
return completer.future;
}
@override
State<RoomPasswordDialog> createState() => _RoomPasswordDialogState();
}
class _RoomPasswordDialogState extends State<RoomPasswordDialog> {
final TextEditingController _passwordController = TextEditingController();
String _errorText = "";
bool _isSubmitting = false;
@override
void dispose() {
_passwordController.dispose();
super.dispose();
}
Future<void> _handleEnter() async {
if (_isSubmitting) {
return;
}
final password = _passwordController.text.trim();
if (password.isEmpty) {
setState(() {
_errorText = "Please enter password";
});
return;
}
setState(() {
_errorText = "";
_isSubmitting = true;
});
final errorText = await widget.onSubmit(password);
if (!mounted) {
return;
}
if ((errorText ?? "").trim().isNotEmpty) {
setState(() {
_errorText = errorText!.trim();
_isSubmitting = false;
});
return;
}
widget.onSuccess(password);
}
@override
Widget build(BuildContext context) {
return Material(
type: MaterialType.transparency,
child: Center(
child: Container(
width: 316.w,
padding: EdgeInsets.fromLTRB(18.w, 18.w, 18.w, 16.w),
decoration: BoxDecoration(
color: const Color(0xff09372E),
borderRadius: BorderRadius.circular(12.w),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Room lock",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w600,
height: 1,
),
),
SizedBox(height: 18.w),
_buildPasswordInput(),
SizedBox(height: _errorText.isEmpty ? 14.w : 8.w),
if (_errorText.isNotEmpty) _buildErrorText(),
SizedBox(height: 12.w),
Row(
children: [
Expanded(
child: _buildButton(
label: "cancel",
filled: false,
onTap: _isSubmitting ? null : widget.onCancel,
),
),
SizedBox(width: 12.w),
Expanded(
child: _buildButton(
label: "enter",
filled: true,
onTap: _isSubmitting ? null : _handleEnter,
),
),
],
),
],
),
),
),
);
}
Widget _buildPasswordInput() {
return Container(
height: 44.w,
decoration: BoxDecoration(
color: const Color(0xff08251E),
borderRadius: BorderRadius.circular(8.w),
border: Border.all(color: const Color(0xffB2FBCC), width: 1),
),
child: TextField(
controller: _passwordController,
autofocus: true,
obscureText: true,
cursorColor: SocialChatTheme.primaryColor,
keyboardType: TextInputType.visiblePassword,
textInputAction: TextInputAction.done,
inputFormatters: [LengthLimitingTextInputFormatter(24)],
onSubmitted: (_) {
_handleEnter();
},
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
hintText: "Please enter password",
hintStyle: TextStyle(
color: Colors.white.withValues(alpha: 0.55),
fontSize: 14.sp,
height: 16 / 14,
),
filled: true,
fillColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 12.w),
),
style: TextStyle(
color: Colors.white,
backgroundColor: Colors.transparent,
fontSize: 14.sp,
fontWeight: FontWeight.w400,
height: 16 / 14,
),
),
);
}
Widget _buildErrorText() {
return SizedBox(
width: double.infinity,
child: Text(
_errorText,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: const Color(0xffff4d4d),
fontSize: 12.sp,
fontWeight: FontWeight.w400,
height: 14 / 12,
),
),
);
}
Widget _buildButton({
required String label,
required bool filled,
required VoidCallback? onTap,
}) {
final child = Container(
height: 38.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: filled ? SocialChatTheme.primaryColor : Colors.transparent,
borderRadius: BorderRadius.circular(19.w),
border: Border.all(
color: filled ? SocialChatTheme.primaryColor : Colors.white,
width: 1.w,
),
),
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
color: filled ? Colors.black : Colors.white,
fontSize: 14.sp,
fontWeight: FontWeight.w600,
height: 1,
),
),
);
return Opacity(
opacity: onTap == null ? 0.55 : 1,
child: SCDebounceWidget(onTap: onTap ?? () {}, child: child),
);
}
}