https://medium.com/theotherde...
首先,由於這不是一個基本的教學,我們理所當然地認為這是一個路線的知識,我們也包含了一點點的 validation
與 formoz
包來建立可重用的模型; 這不是本教學的目的,以顯示這將如何工作,您將看到這在下一個教學。對於登入部分,出於教學的目的,我們還使用了 BLoC (Cubit)
的子集,因此您將看到這兩者之間的區別。
https://github.com/Alessandro...
在我們開始之前,讓我們在 pubspec.yaml 中新增一些必要的包:
equatable: ^2.0.0
flutter_bloc: ^7.0.0
formz: ^0.3.2
新增 equatable
包只會使您的工作更加容易,但是如果您想手動比較類的範例,只需要重寫 "==" 和 hashCode。
讓我們從一個包含表單狀態和所有欄位狀態的類開始:
class LoginState extends Equatable {
const LoginState({
this.email = const Email.pure(),
this.password = const Password.pure(),
this.status = FormzStatus.pure,
this.exceptionError,
});
final Email email;
final Password password;
final FormzStatus status;
final String exceptionError;
@override
List<Object> get props => [email, password, status, exceptionError];
LoginState copyWith({
Email email,
Password password,
FormzStatus status,
String error,
}) {
return LoginState(
email: email ?? this.email,
password: password ?? this.password,
status: status ?? this.status,
exceptionError: error ?? this.exceptionError,
);
}
}
現在讓我們建立我們的 LoginCubit,它將負責執行邏輯,例如通過 emit 獲取電子郵件和輸出新狀態:
class LoginCubit extends Cubit<LoginState> {
LoginCubit() : super(const LoginState());
void emailChanged(String value) {
final email = Email.dirty(value);
emit(state.copyWith(
email: email,
status: Formz.validate([
email,
state.password
]),
));
}
void passwordChanged(String value) {
final password = Password.dirty(value);
emit(state.copyWith(
password: password,
status: Formz.validate([
state.email,
password
]),
));
}
Future<void> logInWithCredentials() async {
if (!state.status.isValidated) return;
emit(state.copyWith(status: FormzStatus.submissionInProgress));
try {
await Future.delayed(Duration(milliseconds: 500));
emit(state.copyWith(status: FormzStatus.submissionSuccess));
} on Exception catch (e) {
emit(state.copyWith(status: FormzStatus.submissionFailure, error: e.toString()));
}
}
}
但是我們如何將腕尺與我們的使用者介面連線起來呢?下面是對 BlocProvider
的解救,這是一個小部件,它使用: BlocProvider.of<logincubit>(context)
為其子部件提供一個區塊
BlocProvider(
create: (_) => LoginCubit(),
child: LoginForm(),
),
既然現在似乎都在他自己的地方,是時候解決我們的最後一塊 puzzle,整個使用者介面
class LoginForm extends StatelessWidget {
const LoginForm({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocConsumer<LoginCubit, LoginState>(
listener: (context, state) {
if (state.status.isSubmissionFailure) {
print('submission failure');
} else if (state.status.isSubmissionSuccess) {
print('success');
}
},
builder: (context, state) => Stack(
children: [
Positioned.fill(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(38.0, 0, 38.0, 8.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
_WelcomeText(),
_EmailInputField(),
_PasswordInputField(),
_LoginButton(),
_SignUpButton(),
],
),
),
),
),
state.status.isSubmissionInProgress
? Positioned(
child: Align(
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
) : Container(),
],
)
);
}
}
為了對 Cubit 發出的新狀態做出反應,我們需要將我們的表單包裹在一個 BlocConsumer 中,現在我們將暴露一個監聽者和一個建造者。
這裡我們將監聽狀態更改,例如,在響應 API 呼叫時顯示錯誤或執行導航。
在這裡,我們將顯示 ui 反應狀態的變化,我們的 Cubit
。
我們的使用者介面由一個列和 5 個子元素組成,但是我們只展示 2 個簡短的小部件:
class _EmailInputField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<LoginCubit, LoginState>(
buildWhen: (previous, current) => previous.email != current.email,
builder: (context, state) {
return AuthTextField(
hint: 'Email',
key: const Key('loginForm_emailInput_textField'),
keyboardType: TextInputType.emailAddress,
error: state.email.error.name,
onChanged: (email) => context
.read<LoginCubit>()
.emailChanged(email),
);
},
);
}
}
class _LoginButton extends StatelessWidget {
const _LoginButton({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<LoginCubit, LoginState>(
buildWhen: (previous, current) => previous.status != current.status,
builder: (context, state) {
return CupertinoButton(
child: Text('Login'),
onPressed: state.status.isValidated
? () => context.read<LoginCubit>().logInWithCredentials()
: null
);
},
);
}
}
這兩個小部件都包裝在一個 BlocBuilder
中,只有當肘位為它們各自的評估屬性發出新的狀態時,BlocBuilder
才負責重新構建這些小部件,因此,例如,如果使用者沒有在 email 欄位中鍵入任何內容,EmailInputField
將永遠不會被重新構建。
相反,如果所有欄位都經過驗證,按鈕將呼叫 logInWithCredentials()
函數,該函數將根據 API 響應發出一個新狀態(失敗或成功)。
© 貓哥
https://github.com/ducafecat/...
https://github.com/ducafecat/...
https://ducafecat.tech/catego...
https://space.bilibili.com/40...
https://space.bilibili.com/40...
https://space.bilibili.com/40...
https://space.bilibili.com/40...
https://space.bilibili.com/40...
https://space.bilibili.com/40...
https://space.bilibili.com/40...