无法显示红色或文本字段中出现错误

问题描述 投票:0回答:1

TextField
Flutter 中错误边框和提示颜色未正确更新

我正在开发一个 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
边框和提示文本颜色不会变成红色。显示错误消息,但边框和提示文本颜色保持不变。

我尝试过的:

  1. errorText
    中设置
    InputDecoration
  2. 使用
    hasError
    有条件地更改边框颜色和提示文本颜色。

问题:

TextField
小部件出现验证错误时,如何确保边框和提示文本颜色变为红色?

任何帮助或建议将不胜感激!

flutter dart authentication user-interface colors
1个回答
1
投票

您首先需要使用表单包装两个文本字段并添加表单键,如下所示:

        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 
                    }
                }
© www.soinside.com 2019 - 2024. All rights reserved.