Вопрос или проблема
Для отладки я вставил оператор вывода внутри метода сборки и заметил, что весь виджет пересоздается несколько раз, когда нажимают на TextFormField.
Клавиатура открывается, а затем сразу закрывается.
Вот мой код:
class LoginScreen extends StatelessWidget {
LoginScreen({super.key});
static Route<void> route() {
return MaterialPageRoute<void>(builder: (_) => LoginScreen());
}
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _selectUrl = TextEditingController();
@override
Widget build(BuildContext context) {
if (!kReleaseMode) {
_emailController.text="";
_passwordController.text="";
_selectUrl.text = baseUrl;
}
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.fromLTRB(
20.0,
),
child: Image.asset(
'assets/images/logo.png',
color: isDarkMode(context)
? Theme.of(context).colorScheme.primary
: null,
),
),
),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Введите ваш email',
),
),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Введите ваш пароль',
),
obscureText: true,
),
BlocConsumer<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state is AuthenticationFailureState) {
_emailController.text = state.fields['email'] ?? '';
_passwordController.text = state.fields['password'] ?? '';
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Не удалось войти'),
content: Text(
state.errorMessage,
textAlign: TextAlign.center,
),
icon: const Icon(
Icons.error,
color: Colors.red,
));
});
} else if (state is AuthenticationSuccessState) {
context
.read<CalendarDataBloc>()
.add(FetchCalendarEvents());
context.read<HomeBloc>().add(FetchCheckedInEvents());
Navigator.pushReplacementNamed(context, '/home');
}
},
builder: (context, state) {
return state is AuthenticationLoadingState &&
(state).isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SizedBox(
child: FilledButton(
onPressed: () {
BlocProvider.of<AuthenticationBloc>(context)
.add(
LoginUser(
_emailController.text.trim(),
_passwordController.text.trim(),
),
);
},
child: const Text(
'Войти',
style: TextStyle(
fontSize: 20,
),
),
),
);
},
),
],
),
),
),
),
);
}
}
Ответ или решение
Проблема: Блиц-перезагрузка клавиатуры при вводе текста в TextFormField
Открытие и немедленное закрытие клавиатуры на экране входа может быть следствием множественной перезагрузки вашего виджета. Ниже мы рассмотрим основные причины этой проблемы, а также предложим оптимизации кода вашего приложения.
1. Перезагрузка виджета
Ваша реализация LoginScreen
вызывает метод build
несколько раз, и если вы добавили print
утверждение внутри него, значит, это может быть связано с изменением состояния или вызовом, который приводит к перезагрузке всего виджета. В вашем коде используются контроллеры TextEditingController
, которые могут сильно повлиять на производительность, если они не используются должным образом. Так как ваш LoginScreen
является статическим виджетом, он может быть повторно создан каждый раз, когда происходит изменение состояния.
2. Проблемы с контроллерами
Ваши контроллеры _emailController
, _passwordController
, и _selectUrl
создаются внутри объекта LoginScreen
, что может значительно повлиять на функциональность введённого текста. При каждом вызове build
, они повторно инициализируются, что приводит к сбросу введённых данных и закрытию клавиатуры.
Решение: Переместите создание контроллеров в конструктор или используйте состояние через StatefulWidget. Это сохранит состояния контроллеров между перестройками виджета.
Пример:
class LoginScreen extends StatefulWidget {
LoginScreen({Key? key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Ваш код
}
}
3. Переосмысленный подход к управлению состояниями
Вы используете BlocConsumer
, который хорошо подходит для управления состояниями, однако, стоит убедиться, что состояние, обновляемое пользователем (например, текст в TextFormField
), не вызывает ненужные перестройки. Если состояние аутентификации изменяется, это может нелестно влиять на взаимодействие с TextFormField
.
4. Избегайте нецелевых обновлений
Проверьте, используете ли вы BlocListener
вместо BlocConsumer
, если вам нужно только слушать события, а не перестраивать виджет. Это может уменьшить количество перестроек виджета, тем самым предотвращая неисправности с клавиатурой.
Итоговые рекомендации
- Перейдите на StatefulWidget, чтобы контроллеры не сбрасывались при каждом вызове метода
build
. - Проверьте ваш логику изменения состояния, чтобы исключить ненужные перезагрузки виджета.
- Ограничьте использование
BlocConsumer
, если только не требуется.
Следуя вышеизложенным рекомендациям, вы сможете улучшить пользовательский интерфейс вашего приложения и устранить проблему с клавиатурой, которая открывается и закрывается немедленно.