在腕尺中访问构建上下文(Flutter)

pbossiut  于 2023-01-21  发布在  Flutter
关注(0)|答案(2)|浏览(121)
主要问题

你好!在Flutter中,我使用BLoC,更确切地说是腕尺来管理应用的状态。
我的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'..
很明显,我明白为什么,但我的问题是:
我如何从腕尺内部访问上下文?
如果有多种方法,哪一种是最优化的?
附言:如果你有一个不错的变通办法,也可以随时提到它。

我的发现:

显然,在this ressource中,我发现以这种方式提供上下文(在这里,在我的home_screen.dart中):

return BlocProvider<WeatherCubit>(
  create: (context) => WeatherCubit(),
    child: MaterialApp(

...应该使它在我的腕尺中可以访问。它对我不起作用,正如你可以猜到的。顺便说一下,我正在寻找比在腕尺构造函数中提供上下文作为参数更好的东西(因为这将是之后测试的真正痛苦)。

编辑

我知道我可以在方法中以String参数的形式传递错误消息,我也已经在WeatherError状态中使用了错误消息属性。
我的问题是错误信息取决于方法本身的行为,这不可能在内部传递参数。
我之前举过一个简单的例子,但我将提供另一个例子:
假设我有一个通过QR码扫描获得的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。
正如您所看到的,错误消息显然依赖于方法实现,这意味着我不能将错误消息作为参数传递;这是没有任何意义的。
你知道我错过了什么吗

zu0ti5jz

zu0ti5jz1#

Idk如果发送上下文是个好主意**(让我们把树的上下文留给小部件,而不是逻辑)**,在我的例子中,我只发送错误而不发送消息:

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**,它将获取翻译,但对于此,我留给您的答案更好地解释了它:
Flutter中的字符串xml文件

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: ...

我希望它能更好地修复或优化您的代码:)

t3psigkw

t3psigkw2#

该错误是正常的,因为CubitWidget无关,也不对build负责。
我如何从腕尺内部访问上下文?
严格地回答您的问题,为了在Cubit中使用context,您可以在创建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(或者如果您还需要BlocBuilder,则使用BlocConsumer)来处理这个副作用,这样做可以保持责任的明确分离,并尊重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});
}

这样,您就可以在当前state is WeatherError时使用state.errorMessage访问errorMessage

相关问题