为什么 notifyListeners() 不更新消费者?
Why does notifyListeners() not update consumer?
在我用 Flutter 构建的应用程序中,我使用 provider package to add state management to my app. Additionally, I am using the shared preferences package to keep track of the login state of my user (token based). The app consumes a Laravel API that makes use of Sanctum。
一切都按预期进行。但是,在注销用户并使用其他用户重新登录后,会导致显示前一个用户的数据。我注意到旧用户的令牌一直保留在提供程序中,这导致加载旧数据。
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 进行任何更改,以重建消费者小部件中列出的提供者。
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 是一个 class,它接收传递的令牌并 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)
),
您在一个提供商中使用了多个提供商
尝试改变那个
正在更新Consumer
。您的 Provider
没有重新创建它们的值。
Provider.create
只调用一次,即第一次需要该值的时候。在一个用户注销和另一个用户登录后,同一个 CategoryProvider
实例仍然存在,据 Provider
所知,没有理由再创建一个。存储在 CategoryProvider
中的 ApiService
实例仍然使用旧令牌,这会导致加载旧数据。
要更新令牌,您需要使用新令牌更新或重新创建 CategoryProvider
。一个选项是 ChangeNotifierProxyProvider
,它提供了一个 update
回调参数。
在我用 Flutter 构建的应用程序中,我使用 provider package to add state management to my app. Additionally, I am using the shared preferences package to keep track of the login state of my user (token based). The app consumes a Laravel API that makes use of Sanctum。
一切都按预期进行。但是,在注销用户并使用其他用户重新登录后,会导致显示前一个用户的数据。我注意到旧用户的令牌一直保留在提供程序中,这导致加载旧数据。
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 进行任何更改,以重建消费者小部件中列出的提供者。
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 是一个 class,它接收传递的令牌并 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)
),
您在一个提供商中使用了多个提供商 尝试改变那个
正在更新Consumer
。您的 Provider
没有重新创建它们的值。
Provider.create
只调用一次,即第一次需要该值的时候。在一个用户注销和另一个用户登录后,同一个 CategoryProvider
实例仍然存在,据 Provider
所知,没有理由再创建一个。存储在 CategoryProvider
中的 ApiService
实例仍然使用旧令牌,这会导致加载旧数据。
要更新令牌,您需要使用新令牌更新或重新创建 CategoryProvider
。一个选项是 ChangeNotifierProxyProvider
,它提供了一个 update
回调参数。