#flutter #dart #bloc #flutter-bloc
Вопрос:
Чего я хочу достичь?
Я использую библиотеку блоков Flutter для своего компонента аутентификации. Внутри страницы входа в систему у меня есть несколько текстовых полей, таких как имя пользователя/пароль, которыми я также собираюсь поделиться с другими страницами, такими как страница регистрации, забытый пароль, смена пароля и т.д. В основном, следуя СУХОМУ принципу.
Каждая страница (Регистрация, Логин, Забытый пароль и т.д.) имеет свой собственный компонент блока.
Моя проблема, я не мог найти способ отделить виджет от блока. Я хочу иметь возможность передавать в виджет с отслеживанием состояния любой компонент блока в зависимости от страницы, на которой он используется.
Чтобы понять смысл написанного выше, давайте взглянем на мой код.
войдите в систему.введите фрагмент кода из метода сборки со страницы входа в систему.
Widget _loginForm(){
return BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
final status = state.formStatus;
if (status is SubmissionFailed) {
...
}
},
child: Form(
key: _formKey,
child: Column(
children: [
// The reusable widget: Email input field.
BlocBuilder<LoginBloc, LoginState>(
builder:(context, state){
return emailInputField(context, state);
}
),
...
Теперь давайте взглянем на виджет Поле ввода электронной почты
Widget emailInputField(BuildContext context, dynamic state) {
return TextFormField(
validator: (value) =>
state.isValidEmail ? null : 'Please input a valid email',
onChanged: (value) =>
// HERE - I want to decouple this widget from "LoginBloc".
context.read<LoginBloc>().add(LoginUsernameChanged(username: value)),
labelText: 'Email',
...
);
}
login_bloc.запустите Блок Входа в систему
class LoginBloc extends Bloc<BaseEvent, LoginState>{
LoginBloc() : super(LoginState());
@override
Stream<LoginState> mapEventToState(BaseEvent event) async*{
yield* event.handleEvent(state);
}
}
И класс событий входа в систему, для получения полной картины login_event.dart
abstract class LoginEvent extends BaseEvent {
AuthenticationService authService = GetIt.I.get<AuthenticationService>();
}
// Event 1
class LoginUsernameChanged extends LoginEvent {
final String username;
LoginUsernameChanged({this.username});
@override
Stream<LoginState> handleEvent(BaseState state) async* {
// Dart style down-casting...
LoginState loginState = state;
yield loginState.copyWith(username: username);
}
}
base_event.дротик
abstract class BaseEvent {
Stream<BaseState> handleEvent(BaseState state);
}
И опять же, есть ли способ отделить эту точку зрения от блока?
Я надеюсь, что мой вопрос имеет смысл после прочтения этого.
P. S
Чтобы сделать вещи еще проще, одна идея, о которой я думаю, состоит в том, чтобы сохранить один компонент блока, который будет обрабатывать группу связанных полей ввода (пароль/имя пользователя), а затем просто переходить в разные состояния, как я сделал на странице login.dart при вызове виджета emailInput.
Ответ №1:
Что бы я сделал, так это полностью отделил виджет от блока. Вместо того , чтобы брать блок state
и использовать bloc.add
, создайте зависимости, которые вы можете заполнить любым параметром.
В вашем примере вы бы имели:
Widget emailInputField({
required BuildContext context,
required bool isValidEmail,
required void Function(String?) onChange,
}) {
return TextFormField(
validator: (value) => isValidEmail ? null : 'Please input a valid email',
onChanged: onChange,
labelText: 'Email',
...
);
}
Затем вы можете использовать emailInputField
с любым блоком, который вы хотите. Или любой государственной библиотеки управления, если на то пошло.
Для вашего примера это дало бы:
Widget _loginForm(){
return BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
final status = state.formStatus;
if (status is SubmissionFailed) {
...
}
},
child: Form(
key: _formKey,
child: Column(
children: [
// The reusable widget: Email input field.
BlocBuilder<LoginBloc, LoginState>(
builder:(context, state){
return emailInputField(
context: context,
isValidEmail: state.isValidEmail,
onChanged: (value) => context.read<LoginBloc>().add(LoginUsernameChanged(username: value))
);
}
),
...
Комментарии:
1. Это отличная идея, но как передать новое значение функции onChange?
2. Смотрите мой обновленный ответ