如何在不强制转换的情况下使用冻结表示共享状态
How to represent shared state with freezed without casting
我正在使用 freezed package to generate state objects which are consumed by the bloc library。
我喜欢为小部件的状态定义联合 类 的功能,这样我就可以表达小部件具有的不同且通常不相交的状态。例如:
@freezed
class ResultsReportState with _$ResultsReportState {
const factory ResultsReportState.loading() = ResultsReportLoading;
const factory ResultsReportState.success({
required ReportViewViewModel report,
}) = ResultsReportSuccess;
const factory ResultsReportState.refreshing({
required ReportViewViewModel report,
}) = ResultsReportRefreshing;
const factory ResultsReportState.error() = ResultsReportError;
}
在上面的代码片段中,我的意图是在出现错误或加载小部件时不显示任何数据,但我仍然希望在成功加载或用户刷新小部件时显示数据。所以 ResultsReportSuccess
和 ResultsReportRefreshing
状态有一个共享状态 ReportViewViewModel
。但是,即使在执行类型检查后我也无法访问这些共享属性 as suggested here.
例如,如果没有明确的类型转换,这将不起作用:
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) {
if (state is ResultsReportSuccess || state is ResultsReportRefreshing) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = state.report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: state.report.serviceCategories.length,
),
);
} else if (state is ResultsReportLoading) {
return ResultsScreenLoadingSkeleton();
} else {
return SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
);
}
},
);
}
}
但是我没有什么可以显式类型转换的,因为它可能是任何一种类型。所以,我尝试了这种方法,它引入了一个我可以参考的接口:
part of 'results_report_bloc.dart';
abstract class ReportPopulated {
ReportViewViewModel get report;
}
@freezed
class ResultsReportState with _$ResultsReportState {
const factory ResultsReportState.loading() = ResultsReportLoading;
@Implements<ReportPopulated>()
const factory ResultsReportState.success({
required ReportViewViewModel report,
}) = ResultsReportSuccess;
@Implements<ReportPopulated>()
const factory ResultsReportState.refreshing({
required ReportViewViewModel report,
}) = ResultsReportRefreshing;
const factory ResultsReportState.error() = ResultsReportError;
}
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) {
if (state is ReportPopulated) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = state.report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: state.report.serviceCategories.length,
),
);
} else if (state is ResultsReportLoading) {
return ResultsScreenLoadingSkeleton();
} else {
return SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
);
}
},
);
}
}
但这也需要类型转换。所以,我可以这样做:
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) {
if (state is ReportPopulated) {
ReportPopulated currentState = state as ReportPopulated;
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = currentState.report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: currentState.report.serviceCategories.length,
),
);
} else if (state is ResultsReportLoading) {
return ResultsScreenLoadingSkeleton();
} else {
return SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
);
}
},
);
}
}
但我想知道为什么必须进行类型转换,因为它感觉很麻烦。欢迎任何人就如何以不同方式实现我的共享状态目标提供任何见解。
我认为您面临的问题可能与 Dart 类型的提升有关,它并不总是像您预期的那样有效。解释的很透彻here.
但是,我如何使用 freezed
处理此问题是使用生成的联合方法。渲染 UI 时,您可以像这样使用它们:
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) => state.maybeWhen(
loading: () => ResultsScreenLoadingSkeleton(),
success: (report) => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: report.serviceCategories.length,
),
),
refreshing: (report) => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: report.serviceCategories.length,
),
),
error: () => SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
),
),
);
}
}
请注意 success
和 refreshing
状态的代码是重复的,因此您可能应该将其提取到单独的小部件中。
我正在使用 freezed package to generate state objects which are consumed by the bloc library。
我喜欢为小部件的状态定义联合 类 的功能,这样我就可以表达小部件具有的不同且通常不相交的状态。例如:
@freezed
class ResultsReportState with _$ResultsReportState {
const factory ResultsReportState.loading() = ResultsReportLoading;
const factory ResultsReportState.success({
required ReportViewViewModel report,
}) = ResultsReportSuccess;
const factory ResultsReportState.refreshing({
required ReportViewViewModel report,
}) = ResultsReportRefreshing;
const factory ResultsReportState.error() = ResultsReportError;
}
在上面的代码片段中,我的意图是在出现错误或加载小部件时不显示任何数据,但我仍然希望在成功加载或用户刷新小部件时显示数据。所以 ResultsReportSuccess
和 ResultsReportRefreshing
状态有一个共享状态 ReportViewViewModel
。但是,即使在执行类型检查后我也无法访问这些共享属性 as suggested here.
例如,如果没有明确的类型转换,这将不起作用:
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) {
if (state is ResultsReportSuccess || state is ResultsReportRefreshing) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = state.report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: state.report.serviceCategories.length,
),
);
} else if (state is ResultsReportLoading) {
return ResultsScreenLoadingSkeleton();
} else {
return SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
);
}
},
);
}
}
但是我没有什么可以显式类型转换的,因为它可能是任何一种类型。所以,我尝试了这种方法,它引入了一个我可以参考的接口:
part of 'results_report_bloc.dart';
abstract class ReportPopulated {
ReportViewViewModel get report;
}
@freezed
class ResultsReportState with _$ResultsReportState {
const factory ResultsReportState.loading() = ResultsReportLoading;
@Implements<ReportPopulated>()
const factory ResultsReportState.success({
required ReportViewViewModel report,
}) = ResultsReportSuccess;
@Implements<ReportPopulated>()
const factory ResultsReportState.refreshing({
required ReportViewViewModel report,
}) = ResultsReportRefreshing;
const factory ResultsReportState.error() = ResultsReportError;
}
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) {
if (state is ReportPopulated) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = state.report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: state.report.serviceCategories.length,
),
);
} else if (state is ResultsReportLoading) {
return ResultsScreenLoadingSkeleton();
} else {
return SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
);
}
},
);
}
}
但这也需要类型转换。所以,我可以这样做:
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) {
if (state is ReportPopulated) {
ReportPopulated currentState = state as ReportPopulated;
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = currentState.report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: currentState.report.serviceCategories.length,
),
);
} else if (state is ResultsReportLoading) {
return ResultsScreenLoadingSkeleton();
} else {
return SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
);
}
},
);
}
}
但我想知道为什么必须进行类型转换,因为它感觉很麻烦。欢迎任何人就如何以不同方式实现我的共享状态目标提供任何见解。
我认为您面临的问题可能与 Dart 类型的提升有关,它并不总是像您预期的那样有效。解释的很透彻here.
但是,我如何使用 freezed
处理此问题是使用生成的联合方法。渲染 UI 时,您可以像这样使用它们:
class ResultsReport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ResultsReportBloc, ResultsReportState>(
builder: (context, state) => state.maybeWhen(
loading: () => ResultsScreenLoadingSkeleton(),
success: (report) => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: report.serviceCategories.length,
),
),
refreshing: (report) => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
var serviceCategory = report.serviceCategories[index];
return ServiceCategoryBlock(
viewModel: serviceCategory,
);
},
childCount: report.serviceCategories.length,
),
),
error: () => SliverFillRemaining(
child: ErrorStateContent(
onErrorRetry: () {
context
.read<ResultsReportBloc>()
.add(ResultsReportEvent.retryButtonTapped());
},
),
),
),
);
}
}
请注意 success
和 refreshing
状态的代码是重复的,因此您可能应该将其提取到单独的小部件中。