为什么 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 回调参数。