这是一个 Stack Overflow 问题,您可以使用它来寻求有关您遇到的问题的帮助:
标题: Flutter 中 TextField 错误边框和提示颜色未正确更新
身体:
我正在开发一个 Flutter 应用程序,并试图实现一种 UI 行为,当出现验证错误时,
TextField
小部件的边框和提示文本颜色会变成红色。但是,边框和提示文本颜色并未按预期更改为红色。
这是处理身份验证的
AuthService
类的代码:
import 'dart:async';
import 'dart:convert';
import 'package:SolidCheck/core/constants/constants.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class AuthService extends ChangeNotifier {
Timer? _authTimer;
DateTime? _expiryDate;
String? _token;
String? get token => _token ?? '';
Future<void> login(
String username,
String password, {
required bool rememberMe,
}) async {
final url = Uri.parse('${AppConstants.baseURL}/auth/login');
final response = await http.post(
url,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: jsonEncode({
'username': username,
'password': password,
}),
);
if (response.statusCode == 401) {
throw Exception(
'The email or password you entered did not match our records. Please try again!');
} else if (response.statusCode != 200) {
final responseData = jsonDecode(response.body);
final errorMessage =
responseData['error'] ?? responseData['message'] ?? 'Unknown error';
throw Exception(errorMessage);
}
final responseData = jsonDecode(response.body);
_token = responseData['token'];
final expiresIn = responseData['expires_in'] as int? ?? 21600;
_expiryDate = DateTime.now().add(Duration(seconds: expiresIn));
await _persistToken(_token!, _expiryDate!);
}
Future<void> refreshToken() async {
final url = Uri.parse('${AppConstants.baseURL}/getUserRefresh');
final response = await http.post(
url,
headers: {
'Authorization': 'Bearer $_token',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
);
if (response.statusCode != 200) {
throw Exception('Failed to refresh token: ${response.reasonPhrase}');
}
final responseData = jsonDecode(response.body);
_token = responseData['token'];
final expiresIn = responseData['expires_in'] as int? ?? 21600;
_expiryDate = DateTime.now().add(Duration(seconds: expiresIn));
await _persistToken(_token!, _expiryDate!);
}
Future<bool> isLoggedIn() async {
await _loadPersistedToken();
return _token != null && _expiryDate!.isAfter(DateTime.now());
}
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('token');
await prefs.remove('expiryDate');
_token = null;
_expiryDate = null;
_authTimer?.cancel();
_authTimer = null;
notifyListeners();
}
Future<void> _persistToken(String token, DateTime expiryDate) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', token);
await prefs.setString('expiryDate', expiryDate.toIso8601String());
_token = token;
_expiryDate = expiryDate;
_autoLogout();
notifyListeners();
}
Future<void> _loadPersistedToken() async {
final prefs = await SharedPreferences.getInstance();
_token = prefs.getString('token');
final expiryDateString = prefs.getString('expiryDate');
if (expiryDateString == null) {
notifyListeners();
return;
}
_expiryDate = DateTime.parse(expiryDateString);
if (_expiryDate!.isBefore(DateTime.now())) {
await logout();
} else {
_autoLogout();
}
notifyListeners();
}
void _autoLogout() {
_authTimer?.cancel();
if (_expiryDate == null) return;
final timeToExpiry = _expiryDate!.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timeToExpiry), logout);
}
}
这是
TextFieldWidget
的实现:
import 'package:SolidCheck/core/constants/app_colors.dart';
import 'package:SolidCheck/core/utils/responsive_util.dart';
import 'package:flutter/material.dart';
class TextFieldWidget {
static Widget buildTextField({
required TextEditingController controller,
required String fieldTitle,
required String hintText,
bool isPassword = false,
required BuildContext context,
bool obscureText = false,
void Function()? toggleVisibility,
String? errorText,
String? Function(String?)? validator,
}) {
final isMobile = ResponsiveUtil.isMobile(context);
final isMobileLarge = ResponsiveUtil.isMobileLarge(context);
final hasError = errorText != null;
return Padding(
padding: EdgeInsets.symmetric(horizontal: isMobile ? 10.0 : 60.5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
fieldTitle,
style: TextStyle(
color: isMobile ? AppColors.kBlackColor : AppColors.kWhiteColor,
),
),
const SizedBox(height: 8.0),
TextFormField(
controller: controller,
obscureText: obscureText,
decoration: InputDecoration(
errorText: hasError ? errorText : null,
errorStyle: const TextStyle(color: AppColors.kRedColor),
hintText: hintText,
hintStyle: TextStyle(
color: hasError ? AppColors.kRedColor : AppColors.kBlackColor,
),
filled: true,
fillColor: isMobile || isMobileLarge
? Colors.grey.shade300
: AppColors.kWhiteColor,
suffixIcon: isPassword
? IconButton(
icon: Icon(
obscureText
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
color: hasError
? AppColors.kRedColor
: AppColors.kGreyColor,
),
onPressed: toggleVisibility,
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: hasError
? AppColors.kRedColor
: AppColors.transparentColor,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
color: hasError ? AppColors.kRedColor : AppColors.kBlackColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
color: hasError
? AppColors.kRedColor
: AppColors.transparentColor,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: const BorderSide(color: AppColors.kRedColor),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: const BorderSide(color: AppColors.kRedColor),
),
),
validator: validator,
style: TextStyle(
color: hasError ? AppColors.kRedColor : AppColors.kBlackColor,
),
),
],
),
);
}
}
以下是我在登录屏幕中使用
TextFieldWidget
的方法:
TextFieldWidget.buildTextField(
controller: _userNameController,
fieldTitle: 'Username',
hintText: 'Enter your username',
context: context,
errorText: _usernameError,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Username is required';
}
return null;
},
),
const SizedBox(height: 15.0),
TextFieldWidget.buildTextField(
controller: _passwordController,
fieldTitle: 'Password',
hintText: 'Enter your password',
context: context,
isPassword: true,
obscureText: obscureText,
toggleVisibility: () {
ref.read(obscureTextProvider.notifier).state = !obscureText;
},
errorText: _passwordError,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Password is required';
}
return null;
},
),
问题: 当出现验证错误时,
TextField
边框和提示文本颜色不会变成红色。显示错误消息,但边框和提示文本颜色保持不变。
我尝试过的:
errorText
中设置 InputDecoration
。hasError
有条件地更改边框颜色和提示文本颜色。问题: 当
TextField
小部件出现验证错误时,如何确保边框和提示文本颜色变为红色?
任何帮助或建议将不胜感激!
您首先需要使用表单包装两个文本字段并添加表单键,如下所示:
static const loginFormKey = GlobalObjectKey<FormState>
('loginFormKey');
Form(
key: loginFormKey,
child: Column(
children: [
usernameTextfield,
passwordTextField
],
),
),
你的用户界面中必须有一个登录按钮,你需要检查那里的表单状态,如下所示
onTap: () {
bool isFieldValid =
loginFormKey.currentState!.validate();
if (isFieldValid) {
// call login api
}
}