Направление работ: Mobile Код раздела: 1.2.1 Авторизация > Регистрация > Задать код-пароль и биометрию Статус: Готов Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 13.07.26
1. Назначение
Раздел описывает опциональную настройку быстрого входа после завершения обязательной регистрации в мобильном приложении.
Интерфейс должен позволить пользователю создать код-пароль, включить доступную биометрию или пропустить настройку быстрого входа.
Раздел зависит от Back: 1.1 Организация аккаунтов и Back: 1.1.2 Вход в систему.
2. Ключевые понятия
Код-пароль — короткий цифровой код из 4 цифр для быстрого входа в мобильное приложение на устройстве пользователя.
Биометрия — способ быстрого входа через системные механизмы устройства: Face ID или Touch ID.
Вход только по код-паролю — вариант, при котором пользователь не включает биометрию и использует только код-пароль.
Пропуск настройки — отказ от создания код-пароля в рамках регистрации с переходом к следующему шагу.
3. Scope
Входит
- Предложение создать код-пароль после регистрации
- Пропуск настройки код-пароля
- Ввод и повтор код-пароля
- Проверка совпадения код-пароля
- Определение доступной биометрии на устройстве
- Включение Face ID или Touch ID для входа
- Отказ от биометрии с сохранением входа по код-паролю
- Переход к следующему шагу регистрации
-
Не входит
- Mobile: 1.2: Регистрация
- Mobile: 1.3: Вход
- Mobile: 1.2.2: Регистрация > Подтверждение почты
- Mobile: 1.3.1: Вход по код-паролю
- Mobile: 1.4: Восстановление пароля
4. BR
Бизнес-правила и ограничения системы указаны в Back: 1.1 Организация аккаунтов и Back: 1.1.2 Вход в систему.
В этом документе связи с BR указаны только при прямом соответствии фронтового требования backend-правилу.
5. Функциональные требования (UC + FR)
FR – functional requirements, что интерфейс должен уметь делать по определенному UC (триггеру).
| ID | Сценарий | Функциональное требование | BR | Вес | Статус |
|---|---|---|---|---|---|
| FR-1 | Пользователь переходит на шаг настройки быстрого входа | Интерфейс открывает шаг после завершения обязательной регистрации и предлагает создать код-пароль или пропустить настройку. | — | 1 | Частично \ #OQ1.2.1-4 |
| FR-2 | Пользователь пропускает создание код-пароля | Интерфейс завершает шаг 1.2.1 без настройки быстрого входа и переводит пользователя к следующему шагу регистрации. | — | 1 | Реализовано |
| FR-3 | Пользователь выбирает создание код-пароля | Интерфейс открывает ввод цифрового код-пароля и показывает только действия, необходимые для ввода и удаления цифр. | — | 1 | Реализовано |
| FR-4 | Пользователь вводит код-пароль | Интерфейс принимает только 4 цифры и переходит к повторному вводу после заполнения. | — | 1 | Реализовано \ #OQ1.2.1-11 |
| FR-5 | Пользователь повторяет код-пароль | Интерфейс сравнивает повторный ввод с первым значением код-пароля. Код-пароль совпал: Интерфейс сохраняет настройку код-пароля на устройстве и переходит к предложению биометрии, если устройство ее поддерживает. Код-пароль не совпал: Интерфейс показывает ошибку несовпадения и позволяет повторить ввод без возврата к началу регистрации. | — | 1 | Частично \ #OQ1.2.1-3 \ #OQ1.2.1-6 |
| FR-6 | Проверка устройства на наличие функций биометрии | Интерфейс определяет, какая биометрия доступна на устройстве: Face ID, Touch ID или отсутствие биометрии. | — | 2 | Частично \ #OQ1.2.1-2 \ #OQ1.2.1-10 |
| FR-7 | На устройстве доступен Face ID | Интерфейс предлагает включить Face ID или оставить вход только по код-паролю. | — | 2 | Реализовано |
| FR-8 | На устройстве доступен Touch ID | Интерфейс предлагает включить Touch ID или оставить вход только по код-паролю. | — | 2 | Реализовано |
| FR-9 | На устройстве нет биометрии | Интерфейс не предлагает Face ID или Touch ID и продолжает сценарий с входом по код-паролю. | — | 2 | Частично \ #OQ1.2.1-2 |
| FR-10 | Пользователь выбирает вход только по код-паролю | Интерфейс сохраняет отказ от биометрии и переводит пользователя к следующему шагу регистрации. | — | 2 | Реализовано |
| FR-11 | Пользователь включает биометрию | Интерфейс запускает системную проверку Face ID или Touch ID и включает биометрический вход только после успешного подтверждения устройством. | — | 2 | Реализовано |
| FR-12 | Системная проверка биометрии не выполнена | Интерфейс оставляет доступным вход по код-паролю и позволяет повторить попытку или продолжить без биометрии. | — | 2 | Реализовано \ #OQ1.2.1-12 |
| FR-13 | Шаг быстрого входа завершен | Интерфейс переводит пользователя к Mobile: 1.2.2 Подтверждение почты. | — | 1 | Реализовано |
6. Открытые вопросы
Пин контекста. BRD «1.2.1 Задать код-пароль и биометрию» v0.1. Репозиторий
booking.mobile.flutter, веткаdev, commit89ca50272c7e04b723ce665ce36f41df1158c7af. Дата сверки: 2026-07-15. Якорь домена:lib/src/feature/auth/widget/reg_ask_biometrics_screen.dart,lib/src/feature/auth/widget/pages/biometric/**,lib/src/feature/profile_page/profile_settings/screens/profile_PIN_reset_screen.dart,lib/src/feature/secure/**,lib/src/core/utils/utils.dart,lib/src/core/utils/app_router.dart. Стек: Dart / Flutter, BLoC/Cubit, плагинlocal_auth. Маршрутизация без сверки с гайдбуком (гайдбук не передавался). Исходный BRD (разделы 1–4) не изменялся.Переобход относительно dad0c65 (2026-07-06): снято 4 вопроса — OQ1.2.1-1 (переход на 1.2.2 подтверждён после решения команды о переименовании шага), OQ1.2.1-5 и OQ1.2.1-9 (кнопка «Вход только по Код-паролю» ожила, мёртвая
_checkAvailableBiometrics()удалена), OQ1.2.1-8 (пустые ячейки пинпада стали инертными). Осталось открытым 7 — OQ1.2.1-2, -3, -4, -6, -7, -10, -11 (код в этих местах не менялся). Появился 1 новый — OQ1.2.1-12 (PlatformExceptionотlocal_authне перехватывается на экране регистрации). Статусы ФТ: 6 требований перешли в «Реализовано» (FR-2, FR-3, FR-7, FR-8, FR-10, FR-13), «Не реализовано» не осталось. Механика экрана пин-кода переделана с роута на оверлей (AppLockGate+PinLockView,7610f28/626cbde) — это относится к 1.3.1 (вне scope), но косвенно чинит 1.2.1: авто-лок больше не сбрасывает стек навигации на Home и не выбивает пользователя из цепочки регистрации.Ограничение обхода. Проанализированы файлы флоу код-пароля и биометрии в регистрации, секьюр-слой и смежная навигация. НЕ проверялись: серверная часть, iOS/Android нативные разрешения (Info.plist / AndroidManifest — usage description для Face ID), сценарий входа (1.3.x), содержимое шага 1.2.2. Утверждения без
file:lineпомечены как «не найдено за обход».
| ID | Источник | Вопрос | Кому | Статус | Рекомендации | Итоговое решение |
|---|---|---|---|---|---|---|
| #OQ1.2.1-1 | Код FR-2, FR-13 | Требование говорит: после пропуска или завершения настройки быстрого входа пользователь переходит к следующему шагу регистрации — 1.2.2 Подтверждение почты. В коде перехода на подтверждение почты нет: «Пропустить» и завершение ведут на экран контактных данных (RegSetContactDataRoute), а он заканчивается переходом на главный экран (HomeRoute). Шага «1.2.2 Подтверждение почты» в цепочке навигации не найдено. \ reg_ask_pin_screen.dart:45; profile_PIN_reset_screen.dart:639; reg_set_contact_data.dart:257,282 Переобход 89ca502: исправлено — после решения команды о переименовании шага экран RegSetContactData и есть 1.2.2 (BRD 1.2.2 Scope: «Ввод имени и email», «Пропуск заполнения контактных данных»; в коде — поля имени и email, кнопка «Заполню потом» \ reg_set_contact_data.dart:43-44,265,300,303). Все три выхода из 1.2.1 ведут на 1.2.2: «Пропустить» \ reg_ask_pin_screen.dart:53; завершение без биометрии \ profile_PIN_reset_screen.dart:640-644; включение биометрии и отказ от неё \ reg_ask_biometrics_screen.dart:47-53,74-77. Цепочка 1.2.1 → 1.2.2 подтверждена. | Бизнес (Герман)/Разработка | Closed | Продуктовое решение о целевой цепочке. (А) если подтверждение почты (1.2.2) ещё не реализовано — оставить FR-2/FR-13 целевыми и завести задачу на переход к 1.2.2; (Б) если после регистрации целевой экран — главный, синхронизировать BRD с фактической цепочкой (контактные данные → главный). Тот же разрыв навигации, что и в 1.2 (OQ1.2-9 / FR-26) — решать вместе. | У шага 1.2.2 изменено название с «Контактных данных» на «Подтверждение почты». Уточнить в коде, что после 1.2.1 идёт 1.2.2. |
| #OQ1.2.1-2 | Код FR-6, FR-9 | Требование говорит: интерфейс различает три исхода — Face ID, Touch ID или отсутствие биометрии. В коде определение типа возвращает больше вариантов: помимо face и fingerprint, на Android — iris, weak, strong; экран предложения показывает имя типа как есть, поэтому для них пользователь увидит «Вход по iris» / «weak» / «strong». BRD ограничивает биометрию Face ID и Touch ID. \ utils.dart:963-1009,997-1004; reg_ask_biometrics_screen.dart:53,55 Переобход 89ca502: частично — текст починен, суть вопроса осталась. Подпись теперь через switch: face → «Face ID», fingerprint → «Touch ID», остальное → «биометрии», сырых «iris»/«weak»/«strong» пользователь больше не увидит \ reg_ask_biometrics_screen.dart:28-32. Но getTypeBiometric по-прежнему возвращает iris/weak/strong \ utils.dart:1007-1012, поэтому на таком устройстве экран биометрии всё равно показывается («Вход по биометрии», иконка Touch ID \ reg_ask_biometrics_screen.dart:34-36), тогда как по BRD биометрии там нет и по FR-9 предлагать её не следует. Продуктовый вопрос не снят. | Бизнес/Разработка | Closed | Продуктовое решение о поддерживаемых типах. (А) расширить BRD (раздел 2) под Android-специфику (iris/weak/strong) и дать им человекочитаемые названия; (Б) трактовать iris/weak/strong как «нет биометрии по BRD» и ограничить определение типа face/fingerprint. Связать с OQ1.2.1-10 (поведение при нескольких типах). | Доработать требования: привести FR-6/FR-9 к фактическому поведению — интерфейс не различает Face ID и Touch ID по отдельности. |
| #OQ1.2.1-3 | Код FR-5 | Требование говорит: при несовпадении повтора код-пароля интерфейс показывает ошибку несовпадения и позволяет повторить ввод. В коде при несовпадении поле очищается и проигрывается shake + вибрация, но текстовая ошибка не показывается — лейбл ошибки задан с fontSize: 0 (невидим), снэкбар/сообщение не выводятся. Возврата к началу регистрации нет (соответствует BRD). \ profile_PIN_reset_screen.dart:680-682,465-469 Переобход 89ca502: не исправлено — errorTextStyle c fontSize: 0 на месте \ profile_PIN_reset_screen.dart:465-469; ветка несовпадения по-прежнему только _shake() + pinController.clear() + вибрация, без текста \ profile_PIN_reset_screen.dart:683-687. | Разработка | Closed | FR-5 требует показать ошибку несовпадения; лейбл есть, но скрыт (fontSize: 0) — похоже на случайное подавление. Закрываю сам: вернуть видимый текст ошибки несовпадения → тикет в Bitrix. Если продукт решит, что shake достаточно — отметить это в FR-5 и убрать мёртвый лейбл. | Признано дефектом. Доработать код: показывать текст ошибки несовпадения код-пароля (сейчас скрыт через fontSize: 0). |
| #OQ1.2.1-4 | Код FR-1 | Требование говорит: шаг настройки быстрого входа открывается после завершения обязательной регистрации. В коде вход на шаг реализован через guard роутера: при biometricConfirm == false любой незащищённый переход редиректится на шаг код-пароля, а сам флаг выставляется на экране пароля регистрации. То есть шаг привязан к флагу, а не строго к «завершению обязательной регистрации». \ app_router.dart:182-192; reg_password_screen.dart:96 Переобход 89ca502: не изменилось — guard редиректит на RegAskPinRoute при !biometricConfirm \ app_router.dart:105,176-190; флаг biometricConfirm: false выставляется там же, на экране пароля \ reg_password_screen.dart:94. Вопрос формальный и остаётся открытым. | Бизнес/Разработка | Closed | Фактически шаг открывается после ввода пароля (последнего обязательного шага) — работает. Вопрос формальный: что считать «завершением обязательной регистрации» и не выставляется ли флаг преждевременно. (А) согласовать определение «завершения» и подтвердить корректность триггера; (Б) если триггер по флагу устраивает — зафиксировать это в FR-1. | Вариант Б: скорректировать бизнес-требования под фактическую последовательность шагов регистрации. |
| #OQ1.2.1-5 | Код FR-11, FR-12 | Требование говорит: биометрия включается только после успешной системной проверки (FR-11); при неуспехе — оставить код-пароль и дать «повторить попытку или продолжить без биометрии» (FR-12). В коде FR-11 выполнен (биометрия активируется только при успешной local_auth). Но при неуспехе показывается снэкбар «Ошибка установки биометрии», пользователь остаётся на экране без явного «повторить / продолжить без биометрии»: повтор — только повторным нажатием той же кнопки, а «продолжить без биометрии» ведёт на мёртвую кнопку «Вход только по Код-паролю» (см. OQ1.2.1-9). \ utils.dart:1019-1037; reg_ask_biometrics_screen.dart:192-208,122-158 Переобход 89ca502: исправлено — «продолжить без биометрии» теперь работает: _skipBiometric вызывает _continueToContactData() (переход на 1.2.2, отказ сохраняется как TypeSecure.pass) \ reg_ask_biometrics_screen.dart:74-77,47-53. При didAuthenticate == false пользователь остаётся на экране со снэкбаром «Ошибка установки биометрии» и может либо повторить кнопкой, либо уйти по живой ссылке \ reg_ask_biometrics_screen.dart:55-72,60. FR-11 подтверждён повторно \ utils.dart:1028-1041. Не покрытый ветвью PlatformException случай вынесен в OQ1.2.1-12. | Бизнес/Разработка | Closed | FR-11 — реализовано. По FR-12 вопрос: обязателен ли явный элемент «продолжить без биометрии». (А, рекоменд.) признать обязательным и реализовать (совпадает с починкой кнопки в OQ1.2.1-9); (Б) если достаточно повтора нажатием — смягчить FR-12. Связать с OQ1.2.1-9. | От разработки: реализовано. |
| #OQ1.2.1-6 | Код FR-5 | Проблема: код-пароль сохраняется не в защищённом хранилище (Keychain/Keystore), а в SharedPreferences: хранится Argon2-хэш (не открытый пин) под ключом secure_hashPass, флаг типа секьюрити — тоже в SharedPreferences. Контейнер — не secure storage. \ secure_cubit.dart:182-197; secure_source.dart:23,40-49,60-63 Переобход 89ca502: не исправлено — SecureSettingsSourceLocal по-прежнему наследует PreferencesDao (SharedPreferences), ключи secure_hashPass / secure_typeSecure \ secure_source.dart:18-27,40-49,60-63; хэш пишется через installPass \ secure_cubit.dart:217-231. Новое обстоятельство: flutter_secure_storage: ^9.0.0 уже в зависимостях \ pubspec.yaml:71, в проекте есть готовый SecureStorageDao \ secure_storage_dao.dart:9-13, и токены авторизации уже хранятся через него \ auth_storage_sp.dart:21. То есть пин-хэш — единственный секрет, оставшийся вне Keychain/Keystore; перенос стал дешевле. | Разработка | Closed | Решение тех-лида по модели угроз. (А) перенести хэш пина и флаг секьюрити в flutter_secure_storage (Keychain/Keystore); (Б) явно зафиксировать в BRD/тех-решении, что Argon2-хэш в SharedPreferences допустим. Хэш (не открытый пин) снижает риск, но контейнер незащищён. | Не является дефектом — вопрос закрыт, доработок не требуется. |
| #OQ1.2.1-7 | Код вне BRD | Что нашли: в репозитории две независимые реализации ввода код-пароля — фактически используемая PINResetScreen (сравнение через статический класс PinCheck) и не подключённая install_pin/ (UserPinBloc), которая хранит пин в открытом виде в AppConfig.userPin. Второе — мёртвый код, расходящийся с фактическим хранилищем и потенциально небезопасный, если будет случайно подключён. \ profile_PIN_reset_screen.dart:719-735; install_pin_bloc.dart:61-183,96-100 Переобход 89ca502: не исправлено, объём мёртвого кода вырос. install_pin/ по-прежнему не подключён (InstallPinRoute упоминается только в роутере, push(InstallPinRoute()) в lib/ отсутствует \ app_router.dart:265,286), при этом его пинпад успели отрефакторить \ user_pin_screen.dart:223-224 — полируется мёртвый код. AppConfig.userPin (открытый пин) на месте \ app_config.dart:8; app_config_sp.dart:16,37. PinCheck со static late String pinFirst жив \ profile_PIN_reset_screen.dart:724-740. Добавилась третья реализация — PinLockView (экран-оверлей для входа, 1.3.1) \ pin_lock_view.dart:22, а LogPinScreen/LogPinRoute стали мёртвыми и помечены DEPRECATED «kept only to avoid a router regen» \ log_pin.dart:15-22. | Разработка | Closed | Мёртвый и небезопасный код, продуктового выбора нет. Закрываю сам: удалить неиспользуемый install_pin/ (UserPinBloc/CheckPinCubit/UserPinStorageSP/InstallPinScreen) и AppConfig.userPin (хранение открытого пина) → тикет в Bitrix. Альтернатива, если install_pin/ — целевая архитектура: довести до рабочего состояния и переключить флоу на неё, убрав дубль PINResetScreen (тогда связать с OQ1.2.1-6). | От бизнеса: почистить код. Доработка, вес 3. |
| #OQ1.2.1-8 | Код FR-3 | Проблема: пинпад показывает цифры и backspace, но также содержит пустые ячейки-заглушки, которые рендерятся как пустые неактивные кнопки. FR-3 требует «только действия, необходимые для ввода и удаления цифр» — функционально работает, но пустые кнопки лишние. \ profile_PIN_reset_screen.dart:167; user_pin_screen.dart:209-210 Переобход 89ca502: исправлено — пустая ячейка больше не рендерится кнопкой: pinpadLayout[index][0].isEmpty ? const SizedBox.shrink() : TextButton(...) на обеих страницах ввода \ profile_PIN_reset_screen.dart:252-255,585-588 (в коде помечено как «bug 002»). Пустых неактивных кнопок на пинпаде не осталось; FR-3 выполняется. | Разработка | Closed | Мелкая уборка верстки, продуктового выбора нет. Закрываю сам: убрать пустые ячейки-заглушки из layout пинпада → тикет в Bitrix. | От бизнеса: почистить код. Доработка, вес 3. |
| #OQ1.2.1-9 | Код FR-10 | Требование говорит: при выборе «вход только по код-паролю» интерфейс сохраняет отказ от биометрии и переводит к следующему шагу. В коде обработчик кнопки «Вход только по Код-паролю» на экране предложения биометрии не выполняет действий: навигация и сохранение отказа закомментированы, активна лишь отправка метрики — кнопка мертва. Плюс в файле есть неиспользуемая функция _checkAvailableBiometrics() с устаревшей логикой (на Android всегда возвращает TouchID). \ reg_ask_biometrics_screen.dart:122-158,121,249-267 Переобход 89ca502: исправлено — экран переписан целиком (335 строк изменений, 154 строки в итоге). Кнопка живая: _skipBiometric → метрика + _continueToContactData() → push(RegSetContactDataRoute()) \ reg_ask_biometrics_screen.dart:74-77,47-53. Отказ сохраняется состоянием TypeSecure.pass, выставленным при installPass \ secure_cubit.dart:221-226 — «вход только по код-паролю» и есть это состояние. Мёртвая _checkAvailableBiometrics() удалена (grep по lib/ — совпадений нет). FR-10 выполняется. | Разработка | Closed | FR-10 задаёт поведение однозначно, продуктового выбора нет. Закрываю сам: реализовать обработчик кнопки (сохранить отказ + переход к следующему шагу) и удалить мёртвую _checkAvailableBiometrics() → тикет в Bitrix. Кнопка общая с FR-12 — починка закрывает и «продолжить без биометрии» (см. OQ1.2.1-5). | Не является дефектом — вопрос закрыт, доработок не требуется. |
| #OQ1.2.1-10 | Код FR-6 | Проблема: определение биометрии возвращает первый доступный тип по приоритету (face → fingerprint → iris → weak → strong). Если доступны и Face, и Touch, будет предложен только Face — выбора между ними нет. BRD рассматривает Face ID и Touch ID как взаимоисключающие исходы (FR-7/FR-8), что согласуется с «одним типом», но поведение при нескольких доступных типах явно не зафиксировано. \ utils.dart:979-1008 Переобход 89ca502: не изменилось — getTypeBiometric возвращает первый тип по тому же приоритету \ utils.dart:993-1016. | Разработка | Closed | Замечание, не блокер. Закрываю сам: зафиксировать в BRD поведение «предлагается первый по приоритету при нескольких доступных типах» (FR-7/FR-8 остаются взаимоисключающими) → правка BRD. Связать с OQ1.2.1-2. | Оставляем проверку кейсов на тестирование. |
| #OQ1.2.1-11 | Код FR-4, FR-5 | Что подтверждено: длина код-пароля «ровно 4 цифры» enforce-ится вводом (переход к повтору на 4-м символе), ввод ограничен цифрами клавиатурой пинпада — FR-4 выполнен. Замечание: проверка держится на порядке нажатий (length == 3), а не на валидаторе значения; отдельной валидации «только цифры»/длины на уровне модели нет. \ profile_PIN_reset_screen.dart:274-281,598-602,157-170 Переобход 89ca502: не изменилось — FR-4 по-прежнему выполняется, проверка по-прежнему на порядке нажатий: pinController.text.length == 3 на первом вводе \ profile_PIN_reset_screen.dart:273,283 и на повторе \ profile_PIN_reset_screen.dart:603,689. Та же схема воспроизведена в новом PinLockView \ pin_lock_view.dart:156,173 — хрупкость размножается. | Разработка | Closed | Продуктового вопроса нет. Закрываю сам: вынести длину/тип символов пина в явный валидатор/константу (устраняет хрупкость «по порядку нажатий») → тикет в Bitrix. \ Подтверждение: FR-4 работает, расхождения нет — правка только на живучесть кода. | Не является дефектом — вопрос закрыт, доработок не требуется. |
| #OQ1.2.1-12 | Код FR-12 | Требование говорит: если системная проверка биометрии не выполнена, интерфейс оставляет доступным вход по код-паролю и позволяет повторить попытку или продолжить без биометрии. В коде покрыт только случай, когда пользователь отменил проверку (didAuthenticate == false) — тогда показывается снэкбар «Ошибка установки биометрии» \ reg_ask_biometrics_screen.dart:55-72,60. Но когда local_auth завершается ошибкой устройства (биометрия заблокирована после неудачных попыток, не зарегистрирована, недоступна), плагин бросает PlatformException; setBiometric её пробрасывает наружу \ utils.dart:1044-1046, а _enableBiometric не перехватывает \ reg_ask_biometrics_screen.dart:55-57. Исключение уходит в необработанные — пользователь не видит ни ошибки, ни подсказки: по нажатию кнопки «не происходит ничего». Вход по код-паролю и ссылка «Вход только по Код-паролю» при этом работают. Для сравнения: на экране входа тот же вызов обёрнут в try/catch \ pin_lock_view.dart:46-64,60-62 — поведение экранов расходится. | Разработка | Closed | Продуктового выбора нет: FR-12 требует обратной связи в ветке «проверка не выполнена», а самая частая её причина (lockout после нескольких неудачных попыток) сейчас молчит. Закрываю сам: обернуть _enableBiometric в try/catch по PlatformException и показать тот же снэкбар «Ошибка установки биометрии» (в идеале — с разбором кодов LockedOut / NotEnrolled / NotAvailable) → тикет в Bitrix. Проверить заодно, нужен ли rethrow в AppUtils.setBiometric \ utils.dart:1044-1046, раз ни один вызывающий его не ловит. | Не является дефектом — вопрос закрыт, доработок не требуется. |