#flutter #flutter-provider
#флаттер #флаттер-поставщик
Вопрос:
Я нахожусь на завершающей стадии завершения довольно большого проекта и теперь сталкиваюсь с новой проблемой с поставщиком. К настоящему времени я прочитал почти все доступные вопросы по этой теме, но все еще не могу найти решение.
У меня есть две модели ChangeNotifier, а именно: AuthProvider и UserProvider. Ошибка, о которой идет речь, возникает, когда я пытаюсь вызвать «AuthProvider.logout ()» с другого отправленного маршрута (страница настроек), который работает как ожидалось, но выдает ошибку, которая
UserProvider был использован после удаления.
AuthProvider создается над приложением MaterialApp следующим образом:
MultiProvider(
providers: [
ChangeNotifierProvider<AuthProvider>(
create: (BuildContext context) => AuthProvider.instance(),
),
...
],
child: MaterialApp(
home: MainPage(),
),
),
Главная страница использует оператор switch для отображения экрана входа или рабочего стола на основе значения из AuthProvider .
@override
Widget build(BuildContext context) {
final AuthStatus authStatus = context.select<AuthProvider, AuthStatus>(
(AuthProvider auth) => auth.status,
);
switch (authStatus) {
case AuthStatus.NOT_LOGGED_IN:
...
case AuthStatus.LOGGED_IN:
return MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<UserModel>(
create: (BuildContext context) => UserModel(),
lazy: false,
),
...
],
child: const HomeScreen(),
);
break;
default:
...
}
}
Пользователь инициализируется с главной страницы над домашним экраном.
MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<UserProvider>(
create: (BuildContext context) => UserProvider(),
lazy: false,
),
...
],
child: const HomeScreen(),
);
Изнутри рабочего стола я нажимаю на страницу настроек, отображающую существующее значение UserProvider и AuthProvider.
final AuthProvider authProvider = Provider.of<AuthProvider>(context);
final UserProvider userModel = Provider.of<UserProvider>(context);
Navigator.push(
context,
PageTransition<SettingsPage>(
child: MultiProvider(
providers: <ChangeNotifierProvider<dynamic>>[
ChangeNotifierProvider<AuthProvider>.value(
value: authProvider,
),
ChangeNotifierProvider<UserProvider>.value(
value: userProvider,
),
],
child: SettingsPage(),
),
type: PageTransitionType.leftToRightWithFade,
),
);
И, наконец, у меня есть простой обратный вызов logoutCallback внутри SettingsPage, который наряду с другой логикой изменяет значение статуса авторизации, таким образом (ожидаемо) вызывая повторное отображение страницы входа.
SettingsButton(
title: 'Logout',
callback: () {
Provider.of<AuthProvider>(ctx, listen: false).logoutCallback();
Navigator.of(ctx).pop();
}),
Ожидаемое поведение заключается в том, что значение authStatus изменяется в logoutCallback(), и при появлении страницы настроек рабочий стол будет заменен страницей входа в систему из-за оператора switch.
Хотя это происходит, я получаю длинную ошибку:
[Пользовательский поставщик был использован после того, как он был открыт][1]
[1]: https://i.stack.imgur.com/oeyor.png
Любая помощь будет оценена, поскольку я больше не уверен, что может быть причиной этой ошибки. Если я должен включить дополнительную информацию или конкретные примеры из кода, пожалуйста, дайте мне знать.
Комментарии:
1. Я видел такого рода проблемы в приложении, где stck не управлялся должным образом. Следовательно, экземпляр a
Provider
, основанный наcontext
другом экране, все еще существовал. Как только старые экраны были удалены, ошибка была исправлена
Ответ №1:
В следующем фрагменте кода, который вы предоставили, возможно, что logoutcallback()
это асинхронная функция, и для ее выполнения требуется некоторое время. Даже до завершения выполнения виджет удаляется из дерева.
SettingsButton(
title: 'Logout',
callback: () {
Provider.of<AuthProvider>(ctx, listen: false).logoutCallback();
Navigator.of(ctx).pop();
}),
Что вы могли бы сделать, так это связать два метода с использованием then()
или использованием async await
.
Комментарии:
1. Спасибо за предложение. Однако обратный вызов logoutCallback не является будущим.
2. Вызывает ли он API? Или удаляет токены из локальной памяти?
3. Он вызывает выход FirebaseAuth (который является будущим), но независимо от этого он утверждает значение статуса авторизации. Я совершенно уверен, что сбой не связан с функцией выхода из системы, потому что она работает так, как ожидалось. Но, как упоминалось @FloydWatson, вероятно, это связано с плохим управлением стеком. UserProvider удаляется преждевременно, что вызывает ошибку.