214 lines
6.2 KiB
Dart
214 lines
6.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:lamiter/Class/Question/question.dart';
|
|
import 'package:lamiter/Class/Question/question_layout_attributes.dart';
|
|
import 'package:lamiter/Extension/build_context.dart';
|
|
import 'package:lamiter/Extension/string.dart';
|
|
import 'package:lamiter/Component/q_title.dart';
|
|
import 'package:lamiter/Provider/Form/form_provider.dart';
|
|
import 'package:lamiter/Provider/Language/language_provider.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
// ignore: must_be_immutable
|
|
class SDQ<T extends FormProvider> extends Question<String> {
|
|
final GlobalKey<SDQLayoutState> SDQKey = GlobalKey<SDQLayoutState>();
|
|
final TextEditingController controller = TextEditingController();
|
|
final SDQLayoutAttributes attributes;
|
|
final SDQValidator validator;
|
|
|
|
SDQ({
|
|
super.key,
|
|
required super.id,
|
|
required super.required,
|
|
super.answer,
|
|
required this.attributes,
|
|
required this.validator,
|
|
});
|
|
|
|
@override
|
|
void setAnswerLayout(String? _answer) {
|
|
if (_answer == null) return;
|
|
answer = _answer;
|
|
controller.text = _answer;
|
|
}
|
|
|
|
@override
|
|
void lockAnswerLayouot() {
|
|
SDQKey.currentState?.lockAnswer();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
void layoutSetAnswer(String? layoutAnswer) {
|
|
if (validator.validator(layoutAnswer) != null) {
|
|
answer = null;
|
|
} else {
|
|
answer = layoutAnswer;
|
|
}
|
|
context.read<T>().refresh();
|
|
}
|
|
|
|
return _Layout(
|
|
key: SDQKey,
|
|
controller: controller,
|
|
attributes: attributes,
|
|
validator: validator,
|
|
setAnswer: layoutSetAnswer,
|
|
);
|
|
}
|
|
}
|
|
|
|
enum SDQValidator {
|
|
none,
|
|
nonNull,
|
|
email,
|
|
phoneNumber,
|
|
positiveNumber;
|
|
|
|
const SDQValidator();
|
|
|
|
String? validator(value) {
|
|
switch (this) {
|
|
case SDQValidator.none:
|
|
return null;
|
|
case SDQValidator.nonNull:
|
|
if (value == null || value.isEmpty) return '此欄位為必填。';
|
|
return null;
|
|
case SDQValidator.email:
|
|
if (value == null || value.isEmpty) return '此欄位為必填。';
|
|
value as String;
|
|
if (!value.validateEmail()) return '請輸入正確的電子郵件格式。';
|
|
return null;
|
|
case SDQValidator.phoneNumber:
|
|
if (value == null || value.isEmpty) return '此欄位為必填。';
|
|
value as String;
|
|
if (!value.validatePhoneNumber()) return '請輸入正確的電話號碼。';
|
|
return null;
|
|
case SDQValidator.positiveNumber:
|
|
if (value == null || value.isEmpty) return '此欄位為必填。';
|
|
value as String;
|
|
try {
|
|
if (double.parse(value) <= 0) return '請輸入大於零的數字。';
|
|
} catch (e) {
|
|
return '請輸入有效數字。';
|
|
}
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class SDQLayoutAttributes extends QuestionLayoutAttributes<String> {
|
|
final String? hintText;
|
|
final int maxLines;
|
|
final bool obscureText;
|
|
final TextInputType keyboardType;
|
|
final TextCapitalization textCapitalization;
|
|
|
|
SDQLayoutAttributes({
|
|
super.title,
|
|
required super.required,
|
|
this.hintText,
|
|
this.maxLines = 1,
|
|
this.obscureText = false,
|
|
this.keyboardType = TextInputType.text,
|
|
this.textCapitalization = TextCapitalization.none,
|
|
});
|
|
}
|
|
|
|
class _Layout extends StatefulWidget {
|
|
// Short description question
|
|
final TextEditingController controller;
|
|
final SDQLayoutAttributes attributes;
|
|
final SDQValidator validator;
|
|
final Function(String?) setAnswer;
|
|
|
|
const _Layout({
|
|
super.key,
|
|
required this.controller,
|
|
required this.attributes,
|
|
required this.validator,
|
|
required this.setAnswer,
|
|
});
|
|
|
|
@override
|
|
State<_Layout> createState() => SDQLayoutState();
|
|
}
|
|
|
|
class SDQLayoutState extends State<_Layout> {
|
|
bool _readOnly = false;
|
|
|
|
late bool _obscureText;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_obscureText = widget.attributes.obscureText;
|
|
setState(() {});
|
|
}
|
|
|
|
void lockAnswer() {
|
|
_readOnly = true;
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final languageProvider = Provider.of<LanguageProvider>(context);
|
|
|
|
return Form(
|
|
child: Column(
|
|
children: [
|
|
// question title
|
|
widget.attributes.title != null && widget.attributes.title!.isNotEmpty
|
|
? QTitle(
|
|
title: languageProvider
|
|
.getLocaleString(widget.attributes.title!),
|
|
readOnly: _readOnly,
|
|
required: widget.attributes.required,
|
|
)
|
|
: const SizedBox.shrink(),
|
|
// answer TextField
|
|
TextFormField(
|
|
controller: widget.controller,
|
|
minLines: 1,
|
|
maxLines:
|
|
widget.attributes.obscureText ? 1 : widget.attributes.maxLines,
|
|
obscureText: _obscureText,
|
|
autocorrect: false,
|
|
readOnly: _readOnly,
|
|
keyboardType: widget.attributes.keyboardType,
|
|
textCapitalization: widget.attributes.textCapitalization,
|
|
// textInputAction: TextInputAction.done,
|
|
decoration: InputDecoration(
|
|
isDense: false,
|
|
contentPadding: REdgeInsets.only(top: 9.sp, bottom: 6.sp),
|
|
hintText: languageProvider
|
|
.getLocaleString(widget.attributes.hintText ?? ''),
|
|
hintStyle: context.bL!.copyWith(color: context.primary),
|
|
enabledBorder:
|
|
UnderlineInputBorder(borderSide: context.QBorderSide),
|
|
suffixIcon: widget.attributes.obscureText
|
|
? GestureDetector(
|
|
onTap: () => setState(
|
|
() {
|
|
_obscureText = !_obscureText;
|
|
},
|
|
),
|
|
child: Icon(
|
|
_obscureText ? Icons.visibility : Icons.visibility_off,
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
validator: widget.validator.validator,
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
onChanged: widget.setAnswer,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|