颤振:使用 Provider 时状态在热重载时丢失

flutter: State is lost on Hot Reload when using Provider

我正在使用 Provider 来管理我的应用程序的状态。这是我的实现方式。

hypnose.dart

class _HypnoseAppState extends State<HypnoseApp> {
  @override
  Widget build(BuildContext context) {
    UserService userService = UserService();
    AudioUtilService audioUtilService = AudioUtilService();

    return MultiProvider(
      providers: [
        ChangeNotifierProvider<UserService>.value(
          value: userService,
        ),
        ChangeNotifierProvider<AudioUtilService>.value(
          value: audioUtilService,
        )
      ],
      child: MaterialApp(
          debugShowCheckedModeBanner: false,
          title: Globals.title,
          theme: ThemeData(primarySwatch: Colors.cyan),
          darkTheme: ThemeData.dark(),
          initialRoute: '/',
          routes: {
            '/': (BuildContext context) => WelcomeScreen(userService),
            '/home': (BuildContext context) => HomePageSwitcher(),
            '/audiocreate': (BuildContext context) => AudioCreateScreen()
          }),
    );
  }
}

home_switcher.dart

class HomePageSwitcher extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<UserService>(
      builder: (BuildContext context, UserService userService, Widget child) {
        return Scaffold(
            appBar: AppBar(),
            drawer: Drawer(
              child: Column(
                children: <Widget>[
                  UserAccountsDrawerHeader(
                    accountEmail: Text(userService.loggedInUser.email),
                    accountName: Text(userService.loggedInUser.name),
                    currentAccountPicture:
                        Image.network(userService.loggedInUser.avatar),
                  )
                ],
              ),
            ),
            body: Center(
              child: RaisedButton(
                child: Text('Sign out'),
                onPressed: () async {
                  await userService.signOut();
                  Navigator.pushNamed(context, '/');
                },
              ),
            ));
      },
    );
  }
}

user_service.dart

class UserService extends ChangeNotifier {
  // Get auth instances
  final GoogleSignIn _googleSignIn = GoogleSignIn();
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Store reference of user collection
  final CollectionReference userDb = Firestore.instance.collection('user');

  // Master instance of logged in user
  User _loggedInUser;

  // Getter to access loggedInUser
  User get loggedInUser {
    return _loggedInUser;
  }

  PublishSubject<AuthState> _authStateSubject = PublishSubject();

.... other code

现在的问题是,每次我热重载时,在主页上,我开始收到 NoSuchMethodError,因为它说电子邮件、姓名等属性在 null 时被调用,我认为这意味着状态丢失。我怎样才能克服同样的问题?我做错了什么吗?

你不应该使用 ChangeNotifierProvider.value。而是使用默认构造函数:

ChangeNotifierProvider(
  builder: (_) => UserService(),
)

否则,您的构建方法不纯,您将遇到

中描述的问题

构建方法的设计方式应该是 pure/without 副作用。这是因为许多外部因素可以触发新的小部件构建,例如:

Route pop/push
Screen resize, usually due to keyboard appearance or orientation change
Parent widget recreated its child
An InheritedWidget the widget depends on (Class.of(context) pattern) change

这意味着构建方法不应触发 http 调用或修改任何状态。

这与问题有什么关系?

您面临的问题是您的构建方法 side-effects/is 不纯,使得无关的构建调用很麻烦。

与其阻止构建调用,不如让构建方法更纯净,这样它就可以随时调用而不会受到影响。

对于您的示例,您需要将小部件转换为 StatefulWidget,然后提取对 State 的 initState 的 HTTP 调用:

ChangeNotifierProvider(
    create: (_) => UserService(),
),

保留密钥

class _HypnoseAppState extends State<HypnoseApp> {
    Key key = UniqueKey();
    ...
}

并构建:

return MultiProvider(
    key: key, //<<<<<<<<<<<<<<<<<<<<<<Here
    providers: ChangeNotifierProvider<UserService>.value(
        value: userService,
    ),
    ChangeNotifierProvider<AudioUtilService>.value(
         value: audioUtilService,
     )
],