首页 > 解决方案 > 不确定如何正确实现 ChangeNotifierProvider 以记住 Flutter 中的登录用户

问题描述

我有一个具有工作用户注册的应用程序,并且使用用户提供程序可以在注册期间设置用户并在以下屏幕上检索它而不会出现问题。我遇到的问题是尝试正确实现在应用程序启动时设置存储的当前登录用户并进入仪表板的能力,而不是在不抛出以下断言的情况下完成注册过程。

我可以毫无问题地获取用户,SharedPreferences但是我不确定如何在我的提供程序中正确设置此用户,以便在仪表板屏幕加载时可以访问它。下面是我的main()函数和 MyApp 类。

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (context) => AuthProvider()),
      ChangeNotifierProvider(create: (context) => UserProvider()),
    ],
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Future<User> getUserData() => UserPreferences().getUser();

    return MaterialApp(
        title: 'My App',
        theme: ThemeData(
          primarySwatch: Colors.red,
          accentColor: Colors.black,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: FutureBuilder(
            future: getUserData().then((value) => {
                  Provider.of<UserProvider>(context, listen: false)
                      .setUser(value)
                }),
            builder: (context, snapshot) {
              switch (snapshot.connectionState) {
                case ConnectionState.none:
                case ConnectionState.waiting:
                  return CircularProgressIndicator();
                default:
                  if (snapshot.hasError)
                    return Text('Error: ${snapshot.error}');
                  else if (snapshot.data.token == null)
                    return Login();
                  else if (snapshot.data.token != null) {
                    return Dashboard();
                  } else {
                    UserPreferences().removeUser();
                    return Login();
                  }
              }
            }),
        routes: {
          '/dashboard': (context) => Dashboard(),
          '/login': (context) => Login(),
          '/register': (context) => Register(),
          '/username': (context) => Username(),
        });
  }
}

在 FutureBuilder 中,我尝试在getUserData()调用上链接一个 then 来设置用户,但这当然会返回以下断言:

════════ Exception caught by foundation library ════════════════════════════════
The following assertion was thrown while dispatching notifications for UserProvider:
setState() or markNeedsBuild() called during build.

This _InheritedProviderScope<UserProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.

从我读过的内容来看,这是因为notifyListeners()在用户提供程序中有一个调用,它将调用markNeedsBuild(). 也就是说,我已经尝试在网上移动一些东西和一些建议,但无法弄清楚实现这一点的最佳实践和最佳方法是什么。

用户提供者:

class UserProvider with ChangeNotifier {
  User _user = new User();

  User get user => _user;

  void setUser(User user) {
    _user = user;
    notifyListeners();
  }
}

如果有人已经登录而没有遇到上述断言,我如何在应用程序加载时正确设置用户?谢谢!

标签: flutterregistrationflutter-providerflutter-change-notifierchangenotifier

解决方案


共享偏好可按如下方式使用:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      body: Center(
      child: RaisedButton(
        onPressed: _incrementCounter,
        child: Text('Increment Counter'),
        ),
      ),
    ),
  ));
}

_incrementCounter() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  int counter = (prefs.getInt('counter') ?? 0) + 1;
  print('Pressed $counter times.');
  await prefs.setInt('counter', counter);
}

推荐阅读