- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我真的很难用 flutter_bloc 将我的 MVVM 模式转换为块模式对于一个非常简单的页面。我最关心的问题是我应该把执行副作用的函数放在哪里?
我有这个 BlocListener
要在从 bloc 发出状态时执行副作用,并调用 ViewModel 来执行该副作用:
BlocListener<LoggedOutNicknameCubit, LoggedOutNicknameState>(
listener: (context, state) {
state.maybeWhen(
submitted: () => loggedOutNickNameViewModel.submitPressed(context),
orElse: () {});
})
仅将 ViewModel 注入(inject) View 是否可以,该 View 包含执行副作用的函数,这样我就不必直接在 View 中执行副作用,因此我可以将业务逻辑与 View 分开?我是否还可以向该 ViewModel 添加可变状态,该状态对于不可变根本没有意义,因此处于从集团发出的“集团”状态没有意义?
class LoggedOutNickNameViewModel extends VpViewModel {
LoggedOutNickNameViewModel(
this._saveNickNameLocallyUseCase, this._getUserFieldFromLocalUseCase)
: super(null);
FormGroup get form => _form;
String get nickNameKey => _nickNameKey;
FormGroup _form;
final ISaveNickNameLocallyUseCase _saveNickNameLocallyUseCase;
final IGetUserFieldFromLocalUseCase _getUserFieldFromLocalUseCase;
final String _nickNameKey = UserRegistrationFieldKeys.nickName;
@override
void onClose() {
_saveNickNameLocallyUseCase
.invoke(_form.control(_nickNameKey).value as String ?? '');
}
void onCreate() {
_form = FormGroup({
UserRegistrationFieldKeys.nickName:
FormControl<String>(validators: [Validators.required]),
});
_form.control(_nickNameKey).value =
_getUserFieldFromLocalUseCase.invoke(_nickNameKey);
_form.markAsDirty();
}
void submitPressed(BuildContext context) {
_saveNickNameLocallyUseCase
.invoke(_form.control(_nickNameKey).value as String ?? '');
Navigator.pushNamed(context, Routes.LOGGED_OUT_EMAIL);
}
}
将 bloc 用于某些主要与不可变状态更改相关的事物并将我的 ViewModel 注入(inject)同一 View 以处理表单验证并保持我的副作用执行功能是否有效?
bloc
对于我的表单,其他页面将从
bloc
中获得更多好处比这个:
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'logged_out_nickname_state.dart';
part 'logged_out_nickname_cubit.freezed.dart';
class LoggedOutNicknameCubit extends Cubit<LoggedOutNicknameState> {
LoggedOutNicknameCubit() : super(const LoggedOutNicknameState.initialised());
void submitPressed() => emit(const LoggedOutNicknameState.submitted());
}
集团状态:
part of 'logged_out_nickname_cubit.dart';
@freezed
abstract class LoggedOutNicknameState with _$LoggedOutNicknameState {
const factory LoggedOutNicknameState.initialised() = _Initialised;
const factory LoggedOutNicknameState.submitted() = _Submitted;
}
而我的观点:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get/get.dart';
import 'package:vepo/presentation/widgets/display/buttons/elevated_buttons/submit_button_widget.dart';
import 'package:vepo/presentation/widgets/display/containers/form_container_widget.dart';
import 'package:vepo/presentation/widgets/display/text/caption_widget.dart';
import 'package:vepo/presentation/widgets/display/text/subtitle_1_widget.dart';
import 'package:vepo/presentation/widgets/forms/text_field/text_field_widget.dart';
import 'package:vepo/presentation/widgets/pages/form_page_scaffold_widget.dart';
import 'package:vepo/presentation/widgets/pages/logo_header_widget.dart';
import 'cubit/logged_out_nickname_cubit.dart';
import 'logged_out_nick_name_controller.dart';
import 'logged_out_nick_name_cubit.dart';
class LoggedOutNickNameView extends GetView<LoggedOutNickNameController> {
@override
Widget build(BuildContext context) {
print('building loggedOUtNickName page');
final loggedOutNickNameViewModel =
BlocProvider.of<LoggedOutNickNameViewModel>(context);
final loggedOutNicknameCubit =
BlocProvider.of<LoggedOutNicknameCubit>(context);
return VpFormPageScaffold([
const VpLogoHeader(),
BlocConsumer<LoggedOutNickNameCubit, LoggedOutNickNameState>(
builder: (context, state) {
return state.when(
initialised: () =>
buildContent(loggedOutNickNameViewModel, loggedOutNicknameCubit),
submitted: () =>
buildContent(loggedOutNickNameViewModel, loggedOutNicknameCubit),
);
}, listener: (context, state) {
state.maybeWhen(
submitted: () => loggedOutNickNameViewModel.submitPressed(context),
orElse: () {});
}),
]);
}
Widget buildContent(LoggedOutNickNameViewModel loggedOutNickNameViewModel,
LoggedOutNicknameCubit loggedOutNicknameCubit) {
return VpFormContainer([
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: const [
VpSubtitle1('So nice to meet you! What do your friends call you?',
TextAlign.center),
VpCaption('You can change this any time in settings...',
TextAlign.center),
]),
VpTextField(
loggedOutNickNameViewModel.nickNameKey,
'Nickname...',
validationMessages: (control) =>
{'required': 'Please enter your name / alter ego'},
textAlign: TextAlign.center,
maxLength: 20,
),
Center(
child:
VpSubmitButton('CONTINUE', loggedOutNicknameCubit.submitPressed))
], loggedOutNickNameViewModel.form);
}
}
最佳答案
ViewModel 并不是真正必要的,因为辅助函数和其他任何东西(非集团模式的东西)都可以在集团中进行。因此,诸如表单之类的可变数据可以放在块上。这也可以传递到不可变状态(在我的情况下,表单被传递到 bloc 状态)。
考虑 ViewModel 中的字段应该去哪里,看起来有点像在发出状态后在 BlocBuilder 中使用的字段应该属于该状态。其他东西可以放在你认为合适的任何地方,所以我将它添加到肘部,因为它不是模式的真正一部分,而只是我的 View 消耗的辅助 Material 。
所以这是我目前从 MVVM 到 bloc 的这个页面的转换。随着我对集团模式的理解的增长,我会更新:
状态:
part of 'logged_out_nickname_cubit.dart';
@freezed
abstract class LoggedOutNickNameState with _$LoggedOutNickNameState {
factory LoggedOutNickNameState.initialised(
{String nickNameKey, FormGroup form}) = _Initialised;
factory LoggedOutNickNameState.submitted(
{String nickNameKey, FormGroup form}) = _Submitted;
}
肘:
part 'logged_out_nickname_state.dart';
part 'logged_out_nickname_cubit.freezed.dart';
class LoggedOutNickNameCubit extends Cubit<LoggedOutNickNameState> {
LoggedOutNickNameCubit(this.nickNameKey, this.form,
this.getUserFieldFromLocalUseCase, this.saveNickNameLocallyUseCase)
: super(LoggedOutNickNameState.initialised(
nickNameKey: nickNameKey, form: form)) {
form.control(nickNameKey).value =
getUserFieldFromLocalUseCase.invoke(nickNameKey);
form.markAsDirty();
}
final String nickNameKey;
final FormGroup form;
final GetUserFieldFromLocalUseCase getUserFieldFromLocalUseCase;
final SaveNickNameLocallyUseCase saveNickNameLocallyUseCase;
// the cubit version of "mapEventToState"
void submitPressed() => emit(
LoggedOutNickNameState.submitted(nickNameKey: nickNameKey, form: form));
void initialise() => emit(
LoggedOutNickNameState.initialised(nickNameKey: nickNameKey, form: form));
// just random helper functions for the view to consume
void onSubmitPressed(BuildContext context) {
saveNickNameLocallyUseCase
.invoke(form.control(nickNameKey).value as String ?? '');
Navigator.pushNamed(context, Routes.LOGGED_OUT_EMAIL,
arguments: LoggedOutEmailPageArgs(
form.control(nickNameKey).value as String ?? ''))
.then((value) => initialise());
}
void onBackPressed() {
saveNickNameLocallyUseCase
.invoke(form.control(nickNameKey).value as String ?? '');
close();
}
}
风景:
import 'logged_out_nickname_cubit.dart';
class LoggedOutNickNamePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('building loggedOUtNickName page');
final loggedOutNicknameCubit =
BlocProvider.of<LoggedOutNickNameCubit>(context);
return VpFormPageScaffold([
VpLogoHeader(loggedOutNicknameCubit.onBackPressed),
BlocConsumer<LoggedOutNickNameCubit, LoggedOutNickNameState>(
builder: (context, state) {
return state.when(
initialised: (nickNameKey, form) =>
_buildContent(nickNameKey, form, loggedOutNicknameCubit),
submitted: (nickNameKey, form) =>
_buildContent(nickNameKey, form, loggedOutNicknameCubit),
);
}, listener: (context, state) {
state.maybeWhen(
submitted: (nickNameKey, form) =>
loggedOutNicknameCubit.onSubmitPressed(context),
orElse: () {});
}),
]);
}
Widget _buildContent(
String nickNameKey, FormGroup form, LoggedOutNickNameCubit cubit) {
return VpFormContainer([
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: const [
VpSubtitle1('So nice to meet you! What do your friends call you?',
TextAlign.center),
VpCaption('You can change this any time in settings...',
TextAlign.center),
]),
VpTextField(
nickNameKey,
'Nickname...',
validationMessages: (control) =>
{'required': 'Please enter your name / alter ego'},
textAlign: TextAlign.center,
maxLength: 20,
),
Center(child: VpSubmitButton('CONTINUE', cubit.submitPressed))
], form);
}
}
关于flutter - 在不可变状态不实用的情况下的 Bloc 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65032673/
这几天我一直在学习 Flutter 中的 Bloc Pattern。 我有一个页面需要生成 OTP 并对其进行验证。 有两个 API(generateOtp、validateOtp)两个实现了这个功能
我目前正在创建一个原型(prototype)项目,它实现了很多功能,如导航管理、错误管理、存储管理等 我现在面临一个关于 Bloc 的问题。我想知道哪些是使用 blocs 的最佳实践,事实上,假设我有
我在 Flutter 工作了很长时间,并且有很多已发布的产品。我从来不喜欢 BLoC,更喜欢使用 Provider 或后来的 Riverpod。 我只是不明白那个事件的概念。为什么我们还需要它?我很困
我对 Flutter 和 BLoC 模式还比较陌生,所以我仍在尝试了解所有内容。 假设我有一个测验应用程序,其中有一个名为 QuestionBloc 的 BLoC它使用存储库从文件中获取问题。事件于Q
我正在使用这个包:https://pub.dartlang.org/packages/bloc .我有两个 View :在第一个 View 中,我使用“bloc1”显示元素列表,通过 Floating
Bloc 相对于 Cubit 的实际优势是什么? 除了可追溯性(您也可以通过适当的 Cubit 日志记录来实现)和高级事件转换(我想不出任何“高级”事件Cubit 无法执行的转换,因为总有一种方法可以
我正在使用 Bloc 模式开发 Flutter 应用程序。认证成功后,UserSate 有 User 对象。在所有其他 Bloc 中,我需要访问 UserState 中的 User 对象。我尝试在其他
我有两个BLoC。 EstateBloc EstateTryBloc 我的应用程序基本上是从API获取遗产,并以类似的fashion显示它们 现在,我想添加排序功能,但是我只能通过特定状态访问遗产 l
这是 Bloc (简体): import 'package:autobleidas_flutter/bloc/bloc_base.dart'; import 'package:firebase_aut
我目前正在将我的代码重构为 bloc 模式,并为屏幕创建了一个 bloc,该屏幕从 Assets 中的 json 文件中获取位置列表。事件是获取,状态是初始、加载和加载。 在我的 UI 屏幕上,我想使
我有一个包含 2 个页面的应用程序,当点击主页按钮时,它会导航到“设置”页面。 IconButton( onPressed:(){ Navigator.push(
我有这些针对腕尺状态的密封类: part of 'logged_out_nickname_cubit.dart'; @freezed abstract class LoggedOutNickNameS
我的项目是一个使用 flutter、dart(前端)和 Nodejs 作为后端的社交网络混合移动应用程序, 我聘请的前端开发人员使项目的一部分(占项目的 35%)使用 GetX 作为状态管理,然后在某
Error: I/flutter ( 5919): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═════════════════════════════════
我是 Flutter 和 Bloc 架构的新手,我正在尝试使用 Bloc 来实现我的登录功能。我正在尝试调用我的 Bloc 文件中的函数,但我不知道该怎么做。如果您能帮我看看我在使用 Bloc 时是否
在使用 BLoC 库时,我们将所有变量存储在一个状态类中。但是在哪里存储TextEditingController,它不会改变,但它的值会改变? 假设我有一个这样的状态类(仅作为示例): @freez
场景 我正在尝试创建一个具有两个屏幕的 Flutter 应用程序:ContactsScreen和 EditContactScreen .在 ContactsScreen , 用户将看到 Dropdow
我在尝试在 DartPad 上运行我的代码时遇到了这个问题。 'runZoned' is deprecated and shouldn't be used. This will be removed
我在尝试在 DartPad 上运行我的代码时遇到了这个问题。 'runZoned' is deprecated and shouldn't be used. This will be removed
产品 Bloc 类 final ProductRepositoryImpl _productRepositoryImpl; ProductBloc(this._productRepositor
我是一名优秀的程序员,十分优秀!