Flutter Web показывает серый экран только на хостинге без каких-либо ошибок в режиме отладки или выпуска

Я создал веб-приложение Flutter, которое отлично работает в режиме отладки и выпуска из моей IDE. Однако после хостинга я вижу серый экран в одном из виджетов.

Я знаю, что этот вопрос задавался много раз, и обычно он сводится к запуску в режиме отладки и проверке журнала на наличие каких-либо проблем, таких как родительский виджет, расширенный, гибкий и т. д.

Я вообще не вижу никаких ошибок или предупреждений.

Однако если я запущу F12 в режиме хостинга, я увижу следующую ошибку:

Исключение: переменная APP_TYPE не найдена. Для отсутствующих записей требуется ненулевой резервный вариант.

Кроме того, в моем режиме отладки/выпуска, созданном из моей IDE, вообще нет проблем.

Вот инспектор флаттера проблемного виджета (виджет серого экрана)

Копируем сюда проблемный код виджетов:

Expanded(
                      flex: 3,
                      child: Column(
                        children: [
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: [
                              AnimatedPackageStatusButton(
                                index: 0,
                                selectedIndex: _selectedPackageStatusIndex,
                                onChange: (idx) =>
                                    selectPackageStatusAction(idx, context),
                                title: paymentStatusName[
                                        PaymentStatus.values[0]]!
                                    .translate(context),
                                // packageStatus: getPackageStatusByIndex(0),
                              ),
                              context.getResponsiveValue(12).hSpace,
                              AnimatedPackageStatusButton(
                                index: 1,
                                selectedIndex: _selectedPackageStatusIndex,
                                onChange: (idx) =>
                                    selectPackageStatusAction(idx, context),
                                title: paymentStatusName[
                                        PaymentStatus.values[1]]!
                                    .translate(context),
                                // packageStatus: getPackageStatusByIndex(1),
                              ),
                            ],
                          ),
                          context.getResponsiveValue(16).vSpace,
                          Expanded(
                            child: Container(
                              clipBehavior: Clip.antiAlias,
                              decoration: BoxDecoration(
                                color: context.colorsTheme.tabBar,
                                borderRadius: BorderRadius.only(
                                  topLeft: context
                                      .getResponsiveValue(16)
                                      .cRadius,
                                  topRight: context
                                      .getResponsiveValue(16)
                                      .cRadius,
                                ),
                              ),
                              child: Column(
                                children: [
                                  PackagesListWidget(
                                    editPackageAction: (Package package) =>
                                        showEditPackageCostModal(package),
                                    packagesList: tabPackagesList,
                                    isItemDisabled: true,
                                    onChecked: (newValue, package) {
                                      int idx =
                                          tabPackagesList.indexOf(package);
                                      setState(
                                        () {
                                          tabPackagesList.removeAt(idx);
                                          tabPackagesList.insert(
                                            idx,
                                            package.copyWith(
                                                isChecked: newValue),
                                          );
                                        },
                                      );
                                    },
                                  ),
                                  AnimatedSwitcher(
                                    duration:
                                        const Duration(milliseconds: 300),
                                    child: tabPackagesList.isNotEmpty
                                        ? StoreBillingPackagesListFooterActions(
                                            packagesList: tabPackagesList
                                                .where((p) => p.isChecked)
                                                .toList(),
                                            showPrint:
                                                _selectedPackageStatusIndex ==
                                                    0,
                                            updateFunc: () => {
                                                  setState(() {
                                                    updateTabPackages(
                                                        context);
                                                  })
                                                })
                                        : const SizedBox.shrink(),
                                  ),
                                ],
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),

Два виджета, использованные выше: 1 виджет списка пакетов

Expanded(
  child: widget.packagesList.isEmpty
      ? EmptyListContainer(
          title: 'noPackagesFoundMessage'.translate(context),
        )
      : ListView.separated(
          padding: EdgeInsets.symmetric(
            vertical: context.responsiveValue(
              mobile: context.getResponsiveValue(20),
              largeTablet: 0,
            ),
          ),
          itemCount: widget.packagesList.length,
          itemBuilder: (BuildContext context, int index) {
            return PackageListItem(
              package: widget.packagesList[index],
              selectPackageAction: widget.selectPackageAction,
              isSelected:
                  widget.selectedPackage == widget.packagesList[index],
              isDisabled: widget.isItemDisabled,
              onChecked: widget.onChecked,
              editPackageAction: // todo: this condition should not be inside the package list in this case you can not use the edit in the future for such status
                  widget.packagesList[index].paymentStatus !=
                          PaymentStatus.paid
                      ? widget.editPackageAction
                      : null,
            );
          },
          separatorBuilder: (BuildContext context, int index) {
            return context.responsiveValue(
              mobile: context.getResponsiveValue(12).vSpace,
              largeTablet: Divider(
                color: context.colorsTheme.background,
              ),
            );
          },
        ),
);

и нижний колонтитул:

BlocListener<PackagesBloc, PackagesState>(
  listener: (context, state) {
    if (state.status == PackagesStatus.loading) {
      log('loading packages');
      context.read<GlobalLoadingBloc>().add(GlobalStartLoading());
    } else if (state.status == PackagesStatus.failed) {
      log('loading packages');
      context.read<GlobalLoadingBloc>().add(GlobalEndLoading());
      showFlashMessage(
        flashType: FlashType.error,
        bodyText: state.message.translate(context),
      );
    } else {
      context.read<GlobalLoadingBloc>().add(GlobalEndLoading());
      updateFunc();
      showFlashMessage(
        flashType: FlashType.success,
        bodyText: "packagesMarkedAsPaidMsg".translate(context),
      );
    }
  },
  child: Container(
    padding: EdgeInsets.symmetric(
      horizontal: context.getResponsiveValue(16),
      vertical: context.getResponsiveValue(16),
    ),
    decoration: BoxDecoration(
      color: context.colorsTheme.secondary,
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(
          context.getResponsiveValue(16),
        ),
        topRight: Radius.circular(
          context.getResponsiveValue(16),
        ),
      ),
    ),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Text(
                  "${'totalSelectedPackages'.translate(context)}: ",
                  style: context.textTheme.normalMedium.copyWith(
                    color: context.colorsTheme.onSecondary,
                  ),
                ),
                context.getResponsiveValue(8).hSpace,
                Text(
                  packagesList.length.toString(),
                  style: context.textTheme.boldMedium.copyWith(
                    color: context.colorsTheme.onSecondary,
                  ),
                ),
              ],
            ),
            Row(
              children: [
                Text(
                  "${'totalToPay'.translate(context)}: ",
                  style: context.textTheme.normalMedium.copyWith(
                    color: context.colorsTheme.onSecondary,
                  ),
                ),
                Text(
                  (packagesList.fold(
                      0.0,
                      (previousValue, element) =>
                          previousValue + element.cost)).toStringAsFixed(2),
                  style: context.textTheme.boldMedium.copyWith(
                    color: context.colorsTheme.onSecondary,
                  ),
                ),
              ],
            ),
          ],
        ),
        !showPrint || packagesList.isEmpty
            ? const SizedBox.shrink()
            : Link(
                text: 'markSelectedAsPaid'.translate(context),
                onTap: () {
                  showMarkPackageAsPaidModal(context);
                },
                textColor: context.colorsTheme.onSecondary,
              ),
        !showPrint || packagesList.isEmpty
            ? const SizedBox.shrink()
            : Link(
                text: 'printSelected'.translate(context),
                onTap: () {
                  context.push(
                    RoutePaths.packagePDFScreenRoute.path,
                    extra: {
                      "packagesList": packagesList,
                      "summary": true,
                      "pdfFor": "store",
                    },
                  );
                },
                textColor: context.colorsTheme.onSecondary,
              ),
      ],
    ),
  ),
);

Без кода невозможно понять, почему

Vega 06.07.2024 10:06

это большой проект, и я не могу воспроизвести его в режиме выпуска/отладки, поэтому не знаю, в чем проблема вставить сюда соответствующий код. Попробую сюда выложить виджет, который не рендерится

Saeed isa 06.07.2024 10:30

Это называется минимальный воспроизводимый пример

Vega 06.07.2024 10:48

Спасибо, я понимаю необходимость кода, у меня просто возникла проблема с созданием этого «минимального» кода для воспроизведения, потому что я не могу воспроизвести на своей машине весь код, это происходит только на хостинге, прикрепил все виджеты и компоненты.

Saeed isa 06.07.2024 10:56

Я нашел проблему, пожалуйста, посмотрите решение в ответе ниже.

Saeed isa 06.07.2024 19:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Глядя на ваше дерево виджетов, я вижу, что один из виджетов Expanded находится внутри виджета Padding.

PackageListItem -> Row -> Padding -> Expanded

Должно быть, это вызывает Invalid Parent data widget exception, который создает проблему «серого экрана». Пожалуйста, обратитесь к приложенному изображению, указывающему точный уровень дерева, на котором возникла проблема.

Попробуйте держать Expanded прямо под Row, а затем обернуть его под Padding.

Ссылка: https://docs.flutter.dev/testing/common-errors#incorrect-use-of-parentdata-widget [введите описание изображения здесь] 1

Привет, спасибо! Заполнение только дочернего элемента строки, а расширенное - это другой дочерний элемент. расширенное не является дочерним элементом заполнения в PackageListItem

Saeed isa 06.07.2024 18:06

Я нашел проблему, пожалуйста, посмотрите решение в ответе ниже.

Saeed isa 06.07.2024 19:10

@Saeedisa Ты прав. Я неправильно понял дерево. Спасибо что подметил это

Ashwin Pandey 07.07.2024 12:23
Ответ принят как подходящий

Это использование .env!

очевидно, веб-приложение не поддерживает файлы .env, файлы должны быть .env, я переименовал файл в dotenv_prod.env (и изменил соответствующие варианты использования), и все сработало!

Я взял ответ из этой ветки переполнения стека, полагаясь на ответ и комментарий Кельвина Джоу.

Получение сообщения «Выдано другое исключение: экземпляр 'minified:bV<void>'» при запуске приложения Flutter в Интернете (локальный хост)

Другие вопросы по теме