您好!在 Flutter 中,我使用 BLoC(更具体地说是 cubits)来管理我的应用程序的状态。
我的
weather_state.dart
文件中有一些状态:
对于后者,我想在发出状态时传递一条错误消息,正如您在此方法中看到的,来自
weather_cubit.dart
(最后一行):
Future<void> getWeather(String cityName) async {
emit(const WeatherLoading());
try {
final weather = await _weatherRepository.fetchWeather(cityName);
emit(WeatherLoaded(weather));
} on NetworkException {
emit(const WeatherError('Error fetching the data.'));
}
}
现在,我刚刚本地化了我的应用程序,因此也想本地化错误消息。 我在我的 cubit 中导入了必要的包,然后以这种方式访问本地化的错误消息:
emit(WeatherError(AppLocalizations.of(context)!.errorFetchingData));
我收到以下错误:
Undefined name 'context'.
。PS:如果您有不错的解决方法,也请随时提及。
显然,从这个资源,我发现以这种方式提供上下文(在这里,在我的
home_screen.dart
中):
return BlocProvider<WeatherCubit>(
create: (context) => WeatherCubit(),
child: MaterialApp(
...应该使它可以在我的肘部内使用。 正如你可以猜到的,它对我不起作用。 顺便说一句,我正在寻找比在 cubit 构造函数中提供上下文作为参数更好的东西(因为这对于之后的测试来说是一个真正的痛苦)。
我知道我可以在我的方法中将错误消息作为字符串参数传递。我还已经在 WeatherError 状态中使用了错误消息属性。
我的问题是错误消息取决于方法本身的行为,这不可能在内部传递参数。
我之前举了一个简单的例子,但我会在那里提供另一个:
假设我有一个通过二维码扫描获得的 UUID。
我将 UUID 作为 selectCity 方法的参数传递:
void selectCity(String? cityUuid) async {
const uuidPattern =
r'[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';
final regexUuid = RegExp(uuidPattern);
if (cityUuid == null) {
emit(CitySelectionError('The QR code scanner had an error. Please try again.'));
} else if (!regexUuid.hasMatch(cityUuid)) {
emit(CitySelectionError('The QR code does not contain a UUID and cannot match a city.'));
} else {
emit(CitySelected(cityUuid));
}
}
错误状态:
class CitySelectionError extends CityState {
final String errorMessage;
CitySelectionError(this.errorMessage) : super(null);
// Equatable: don't mind
@override
List<Object?> get props => [errorMessage];
}
我首先想检查它是否为空,因为这将是扫描失败的结果,然后再检查字符串是否是实际的 UUID。
正如您所看到的,错误消息显然取决于方法的实现。这意味着我无法将错误消息作为参数传递;这没有任何意义。
知道我缺少什么吗?
我不知道发送上下文是否是个好主意(让我们将树的上下文留给您的小部件,而不是逻辑),在我的情况下,我只发送没有消息的错误:
emit(const WeatherError());
现在,在侦听器中(如果您想显示 Snackbar),您可以收听该状态并显示翻译:
return BlocListener<BlocA, StateA>(
listener: (context, state) {
if (state is WeatherError) {
ScaffoldMessenger.of(context)
..showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.errorFetchingData),
),
);
}
},
child: ...
编辑:
是的,逻辑将是这样的(除非您已经在寻找更好地处理错误的方法),但对于这种情况,我们将制定条件,但是,我们将发送一个密钥,帮助您找到要翻译的内容寻找。
void selectCity(String? cityUuid) async {
const uuidPattern =
r'[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';
final regexUuid = RegExp(uuidPattern);
if (cityUuid == null) {
emit(CitySelectionError('qrError'));
} else if (!regexUuid.hasMatch(cityUuid)) {
emit(CitySelectionError('qrNoMatch'));
} else {
emit(CitySelected(cityUuid));
}
}
在你的监听器中,在你的 AppLocalization 中,你将有一个名为
translate()
的方法,在这个方法中你可以发送该密钥,并通过 .json 它将进行翻译,但为此我给你留下了这个更好地解释它的答案:
return BlocListener<BlocA, StateA>(
listener: (context, state) {
if (state is CitySelectionError) {
ScaffoldMessenger.of(context)
..showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.translate(state.errorMessage)),
),
);
}
},
child: ...
我希望它可以更好地修复或优化你的代码:)
错误属于正常现象,因为
Cubit
与Widget
无关,也不对build
任何事负责。
如何从肘部内部访问上下文?
为了严格回答您的问题,为了在
context
中使用Cubit
,您可以在创建Cubit
时将其作为参数传递。
类似以下内容:
return BlocProvider<WeatherCubit>(
create: (context) => WeatherCubit(context),
child: MaterialApp(
所以
WeatherCubit
应该看起来像:
class WeatherCubit extends Cubit<WeatherState> {
final BuildContext context;
WeatherCubit(this.context) : super(WeatherState());
}
但是,如果目的是向用户显示错误消息,例如在
SnackBar
中,我建议您使用 BlocListener
(如果您也需要 BlocConsumer
,则使用 BlocBuilder
)来处理这个副作用。通过这样做,您可以保持明确的职责分离并尊重 BLoC 模式的理念。
BlocListener<WeatherCubit, WeatherState>(
listenWhen: (previous, current) => previous != current,
listener: (context, state) {
if (state is WeatherError) {
ScaffoldMessenger.of(context)..hideCurrentSnackBar()..showSnackBar(SnackBar(content: Text(state.errorMessage)));
}
},
child: child,
),
最后,
WeatherError
:
class WeatherError extends WeatherState {
final String errorMessage;
const WeatherError({required this.errorMessage});
}
这样您就可以在当前
errorMessage
时使用 state.errorMessage
访问 state is WeatherError
。
我面临同样的问题并找到了更好的解决方案(至少对我来说)。
这是我的肘节的样子:
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class ExampleCubit extends Cubit<ExampleState> {
final AppLocalizations _appLocalizations;
ExampleCubit({
required AppLocalizations appLocalizations,
}) : _appLocalizations = appLocalizations,
super(const ExampleState());
Future<void> initialize() async {
emit(state.copyWith(isLoading: true, error: ''));
try {
// Your logic here...
} catch (e) {
emit(
state.copyWith(
isLoading: false,
// HERE IS HOW I CALL THE LOCALIZATION TEXT
error: _appLocalizations.errorOccurred,
),
);
}
}
}
为了传递 BlocProvider,我使用以下代码:
static Widget create() {
return BlocProvider(
create: (context) => ExampleCubit(
// Using an extension to access AppLocalizations
appLocalizations: context.l10n,
),
child: const YourWidget(),
);
}
注意: 实际上,我正在使用
AppLocalizations
的扩展名,以使其更易于访问。
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
extension BuildContextExt on BuildContext {
AppLocalizations get l10n {
final localization = AppLocalizations.of(this);
// Throwing an exception for demonstration purposes
if (localization == null) throw Exception('Localization not found');
return localization;
}
}
此方法允许您将
AppLocalizations
实例传递给 Cubit,从而使您能够在 Cubit 方法中使用本地化字符串。