以肘单位访问构建上下文(Flutter)

问题描述 投票:0回答:3

主要问题

您好!在 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。
正如您所看到的,错误消息显然取决于方法的实现。这意味着我无法将错误消息作为参数传递;这没有任何意义。
知道我缺少什么吗?

flutter dart bloc cubit
3个回答
4
投票

我不知道发送上下文是否是个好主意(让我们将树的上下文留给您的小部件,而不是逻辑),在我的情况下,我只发送没有消息的错误:

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

我希望它可以更好地修复或优化你的代码:)


0
投票

错误属于正常现象,因为

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


0
投票

我面临同样的问题并找到了更好的解决方案(至少对我来说)。

这是我的肘节的样子:

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 方法中使用本地化字符串。

© www.soinside.com 2019 - 2024. All rights reserved.