首页 > 解决方案 > 如何将 ChangeNotifier 与 Navigator 一起使用

问题描述

在我的应用程序中,我有一个模型来存储登录我的应用程序的用户。

class AuthenticationModel extends ChangeNotifier {
  User _user;
  User get user => _user;

  void authenticate(LoginData loginData) async {
    // _user = // get user from http call
    notifyListeners();
  }

  void restoreUser() async {
    //_user = // get user from shared prefs
    notifyListeners();
  }
}

该模型注册在小部件树的顶部:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => AuthenticationModel(),
      child: MaterialApp(
        title: 'My App',
        initialRoute: '/',
        routes: {
          '/': (context) => PrehomeScreen(),
          '/home': (context) => HomeScreen()
        },
      ),
    );
  }
}

在小部件树的某个地方,我有一个调用 Model 的按钮:

          child: Consumer<AuthenticationModel>(
            builder: (context, authModel, child) {
              return MyCustomButton(
                text: 'Connect',
                onPressed: () {
                  authModel.authenticate(...)
                },
              );
            },
          ),

现在,我想在某个地方收听 上的更改以在模型中用户不为空时AuthenticationModel触发 a 。Navigator.pushReplacmentNamed('/home')

我试图在 Prehome 的构建器中做到这一点:

class PrehomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<AuthenticationModel>(
      builder: (context, authModel, child) {
        if (authModel.user != null) {
          Navigator.of(context).pushReplacementNamed("/home")
        }
        return Container(
          child: // Prehome UI
        );
      },
    );
  }
}

但是这样做时我有一个错误:

════════ (2) Exception caught by widgets library ═══════════════════════════════════════════════════
setState() or markNeedsBuild() called during build.
The relevant error-causing widget was:
  Consumer<AuthenticationModel> file:///Users/pierre.degand/Projects/cdc/course_du_coeur/lib/Prehome.dart:13:12
═══════════════════════════════════════════════════════════════════════════════

如何设置这样的侦听器?在这样的模型更改上触发导航是一种好习惯吗?

谢谢

编辑:我找到了一种方法来完成这项工作。Consumer我没有在 PrehomeScreen 构建器中使用,而是使用了以下代码:

class PrehomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    Provider.of<AuthenticationModel>(context).addListener(() {
      Navigator.of(context).pushReplacementNamed("/home");
    });

    return Container(
      child: // UI
    );
  }
}

它工作正常,导航在模型更改时执行。但是控制台有错误信息(打印3次):

════════ (4) Exception caught by foundation library ════════════════════════════════════════════════
Looking up a deactivated widget's ancestor is unsafe.
════════════════════════════════════════════════════════════════════════════════════════════════════

该应用程序不会崩溃,所以现在,我可以接受。我仍然想知道这是否是一个好方法。

标签: flutterdart

解决方案


void main() {
  Provider.debugCheckInvalidValueType = null;

  return runApp(
    Provider(
      create: (_) => AuthenticationModel(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final navigatorKey = GlobalKey<NavigatorState>();

    Provider.of<AuthenticationModel>(context).addListener(() {
      final authModel = Provider.of<AuthenticationModel>(context);

      if (authModel.user != null) {
        navigatorKey.currentState.pushReplacementNamed("/home");
      }
    });

    return MaterialApp(
      navigatorKey: navigatorKey,
      title: 'My App',
      initialRoute: '/',
      routes: {
        '/': (context) => PrehomeScreen(),
        '/home': (context) => HomeScreen()
      },
    );
  }
}

推荐阅读