为啥 notifyListeners() 不更新消费者?
Posted
技术标签:
【中文标题】为啥 notifyListeners() 不更新消费者?【英文标题】:Why does notifyListeners() not update consumer?为什么 notifyListeners() 不更新消费者? 【发布时间】:2021-12-02 22:30:29 【问题描述】:在我使用 Flutter 构建的应用程序中,我使用 provider package 向我的应用程序添加状态管理。此外,我使用shared preferences package 来跟踪我的用户的登录状态(基于令牌)。该应用使用了一个使用 Sanctum 的 Laravel API。
一切都按预期工作。但是,在注销用户并使用其他用户重新登录后,会导致显示前一个用户的数据。我注意到旧用户的令牌一直保留在提供程序中,这导致旧数据加载。
main.dart
Future main() async
await dotenv.load(fileName: ".env");
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return ChangeNotifierProvider(
create: (context) => AuthProvider(),
child: Consumer<AuthProvider>(builder: (context, authProvider, child)
return MultiProvider(
providers: [
ChangeNotifierProvider<CategoryProvider>(
create: (context) => CategoryProvider(authProvider)),
ChangeNotifierProvider<TransactionProvider>(
create: (context) => TransactionProvider(authProvider)),
ChangeNotifierProvider<ProfileProvider>(
create: (context) => ProfileProvider(authProvider))
],
child: MaterialApp(
title: 'Flutter App',
routes:
'/': (context)
final authProvider = Provider.of<AuthProvider>(context);
return authProvider.isAuthenticated ? Home() : Login();
,
'/login': (context) => Login(),
'/register': (context) => Register(),
'/profile': (context) => Profile(),
'/categories': (context) => Categories(),
,
));
));
鉴于上面的示例,我希望对我的 AuthProvider 进行任何更改,以重建 Consumer 小部件中列出的 Provider。
auth_provider.dart
class AuthProvider extends ChangeNotifier
bool isAuthenticated = false;
late String token;
AuthProvider()
init();
Future<void> init() async
this.token = await getToken();
if (this.token.isNotEmpty)
this.isAuthenticated = true;
ApiService apiService = ApiService(this.token);
notifyListeners();
ApiService apiService = ApiService('');
Future<void> register(String name, String email, String password,
String passwordConfirm, String deviceName) async
this.token = await apiService.register(name, email, password, passwordConfirm, deviceName);
setToken(this.token);
this.isAuthenticated = true;
notifyListeners();
Future<void> login(String email, String password, String deviceName) async
this.token = await apiService.login(email, password, deviceName);
setToken(this.token);
this.isAuthenticated = true;
notifyListeners();
Future<void> logout() async
this.token = '';
this.isAuthenticated = false;
setToken(this.token);
final prefs = await SharedPreferences.getInstance();
prefs.clear();
notifyListeners();
Future<void> setToken(token) async
final prefs = await SharedPreferences.getInstance();
prefs.setString('token', token);
Future<String> getToken() async
final prefs = await SharedPreferences.getInstance();
return prefs.getString('token') ?? '';
在logout()
函数中,我正在清除令牌。
category_provider.dart
class CategoryProvider extends ChangeNotifier
List<Category> categories = [];
late ApiService apiService;
late AuthProvider authProvider;
CategoryProvider(AuthProvider authProvider)
this.authProvider = authProvider;
this.apiService = ApiService(authProvider.token);
init();
Future init() async
categories = await apiService.fetchCategories();
notifyListeners();
Future<void> addCategory(String name) async
try
Category addedCategory = await apiService.addCategory(name);
categories.add(addedCategory);
notifyListeners();
catch (Exception)
print(Exception);
// omitted functions
ApiService 是一个接收传递的令牌并为提供者执行 API 调用的类。
api.dart
class ApiService
late String token;
ApiService(String token)
this.token = token;
final String baseUrl = dotenv.env['APP_URL'].toString() + '/api/';
Future<List<Category>> fetchCategories() async
http.Response response =
await http.get(Uri.parse(baseUrl + 'categories'), headers:
HttpHeaders.contentTypeHeader: 'application/json',
HttpHeaders.acceptHeader: 'application/json',
HttpHeaders.authorizationHeader: 'Bearer $token',
);
List categories = jsonDecode(response.body)['data'];
return categories.map((category) => Category.fromJson(category)).toList();
// omitted functions
为什么auth_provider.dart
的注销功能中的notifiyListeners()
不会触发消费者重建?我是否遗漏了可能导致此问题的其他内容?
回答后更新
在main.dart
的providers数组中,我将ChangeNotifierProvider改为ChangeNotifierProxyProvider。不同之处在于 ChangeNotifierProxyProvider 允许 update()
回调,因此如果 AuthProvider
更新,则提供程序可以得到更新。
代码示例:
ChangeNotifierProxyProvider<AuthProvider, CategoryProvider>(
create: (context) => CategoryProvider(authProvider),
update: (context, authProvider, categoryProvider) => CategoryProvider(authProvider)
),
【问题讨论】:
【参考方案1】:Consumer
正在更新。你的Provider
s 没有重新创造他们的价值观。
Provider.create
只调用一次,第一次需要该值。在一个用户注销和另一个用户登录后,相同的CategoryProvider
实例仍然存在,因此据Provider
所知,没有理由再创建一个。存储在CategoryProvider
中的ApiService
实例仍然使用旧令牌,这会导致加载旧数据。
要更新令牌,您需要使用新令牌更新或重新创建 CategoryProvider
。一个选项是ChangeNotifierProxyProvider
,它提供了一个update
回调参数。
【讨论】:
【参考方案2】:您在单个提供程序中使用了多个提供程序 尝试改变它
【讨论】:
我不完全确定您的意思,您能否提供一个示例来回答您的问题? 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。以上是关于为啥 notifyListeners() 不更新消费者?的主要内容,如果未能解决你的问题,请参考以下文章
Provider 调用 notifyListeners() 时 Flutter View 不更新视图