Flutter 如何将ChangeNotifier与Navigator一起使用

Flutter 如何将ChangeNotifier与Navigator一起使用,flutter,dart,Flutter,Dart,在我的应用程序中,我有一个模型存储登录到我的应用程序的用户 class AuthenticationModel extends ChangeNotifier { User _user; User get user => _user; void authenticate(LoginData loginData) async { // _user = // get user from http call notifyListeners(); } void

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

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()
        },
      ),
    );
  }
}
在小部件树的某个地方,我有一个按钮调用模型:

          child: Consumer<AuthenticationModel>(
            builder: (context, authModel, child) {
              return MyCustomButton(
                text: 'Connect',
                onPressed: () {
                  authModel.authenticate(...)
                },
              );
            },
          ),
但这样做时我有一个错误:

════════ (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
═══════════════════════════════════════════════════════════════════════════════
它工作正常,在模型更改时执行导航。但控制台中有一条错误消息(打印3次):

该应用程序不会崩溃,所以,现在,我同意这一点。
我仍然想知道这是否是一种好方法。

我更喜欢使用Stream或rxdart PublishSubject行为主题收听任何活动或管理全球应用程序数据

我使用bloc模式实现它。基本上,bloc模式就像react的redux,这意味着创建一个包含所有应用程序数据的中心数据集,而您不必进行道具钻取

您可以像这样创建流

import 'package:rxdart/rxdart.dart';

class AbcBloc {
  BehaviorSubject<bool> _connectivity;
  AbcBloc() {
    _connectivity = BehaviorSubject<bool>();
  }
  // stream
  Stream<bool> get connectivity => _connectivity.stream;
  // sink
  Function(bool) get updateConnectivity => _connectivity.sink.add;
dispose(){
    _connectivity.close();
}
}

void createAbcBloc() {
  if (abcBloc != null) {
    abcBloc.dispose();
  }
  abcBloc = AbcBloc();
}

AbcBloc abcBloc = AbcBloc();


您可以从abcBloc.updateConnectivity(false)更新连接; 每次执行任何更改时,侦听器都将被调用


记住,您必须调用ListenConnection()一次才能激活它

我更喜欢使用Stream或rxdart PublishSubject行为主题收听任何活动或管理全球应用程序数据

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()
      },
    );
  }
}
我使用bloc模式实现它。基本上,bloc模式就像react的redux,这意味着创建一个包含所有应用程序数据的中心数据集,而您不必进行道具钻取

您可以像这样创建流

import 'package:rxdart/rxdart.dart';

class AbcBloc {
  BehaviorSubject<bool> _connectivity;
  AbcBloc() {
    _connectivity = BehaviorSubject<bool>();
  }
  // stream
  Stream<bool> get connectivity => _connectivity.stream;
  // sink
  Function(bool) get updateConnectivity => _connectivity.sink.add;
dispose(){
    _connectivity.close();
}
}

void createAbcBloc() {
  if (abcBloc != null) {
    abcBloc.dispose();
  }
  abcBloc = AbcBloc();
}

AbcBloc abcBloc = AbcBloc();


您可以从abcBloc.updateConnectivity(false)更新连接; 每次执行任何更改时,侦听器都将被调用

记住,您必须调用ListenConnection()一次才能激活它

void main(){
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()
      },
    );
  }
}
Provider.debugCheckInvalidValueType=null; 返回runApp( 提供者( 创建:(\u)=>AuthenticationModel(), 子项:MyApp(), ), ); } 类MyApp扩展了无状态小部件{ @凌驾 小部件构建(构建上下文){ 最终navigatorKey=GlobalKey(); Provider.of(context.addListener)(){ 最终authModel=Provider.of(上下文); if(authModel.user!=null){ navigatorKey.currentState.pushReplacementNamed(“/home”); } }); 返回材料PP( 导航工作:导航工作, 标题:“我的应用程序”, initialRoute:“/”, 路线:{ “/”:(上下文)=>PrehomeScreen(), “/home”:(上下文)=>主屏幕() }, ); } }
void main(){
Provider.debugCheckInvalidValueType=null;
返回runApp(
提供者(
创建:(\u)=>AuthenticationModel(),
子项:MyApp(),
),
);
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
最终navigatorKey=GlobalKey();
Provider.of(context.addListener)(){
最终authModel=Provider.of(上下文);
if(authModel.user!=null){
navigatorKey.currentState.pushReplacementNamed(“/home”);
}
});
返回材料PP(
导航工作:导航工作,
标题:“我的应用程序”,
initialRoute:“/”,
路线:{
“/”:(上下文)=>PrehomeScreen(),
“/home”:(上下文)=>主屏幕()
},
);
}
}

我认为不需要
变更通知程序

void main() async {
  final isLoggedIn = await Future.value(true); // get value from shared prefs or your model

  runApp(MyApp(isLoggedIn));
}

class MyApp extends StatelessWidget {
  MyApp(this.isLoggedIn);

  final bool isLoggedIn;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: isLoggedIn ? '/home' : '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/login': (context) => LoginScreen()
      },
    );
  }
}

我认为不需要
ChangeNotifier

void main() async {
  final isLoggedIn = await Future.value(true); // get value from shared prefs or your model

  runApp(MyApp(isLoggedIn));
}

class MyApp extends StatelessWidget {
  MyApp(this.isLoggedIn);

  final bool isLoggedIn;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: isLoggedIn ? '/home' : '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/login': (context) => LoginScreen()
      },
    );
  }
}

它不起作用。我还试着用
消费者
包装
材料应用
,但效果不太好。我找到了一种方法,可以让这部作品在我的第一篇文章中看到编辑。我已经编辑过了。我认为,
PrehomeScreen
是不必要的小部件。它不工作。我还试着用
消费者
包装
材料应用
,但效果不太好。我找到了一种方法,可以让这部作品在我的第一篇文章中看到编辑。我已经编辑过了。我认为
PrehomeScreen
是不必要的小部件。
void main() async {
  final isLoggedIn = await Future.value(true); // get value from shared prefs or your model

  runApp(MyApp(isLoggedIn));
}

class MyApp extends StatelessWidget {
  MyApp(this.isLoggedIn);

  final bool isLoggedIn;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: isLoggedIn ? '/home' : '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/login': (context) => LoginScreen()
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('Logout'),
      onPressed: () => Navigator.of(context).pushReplacementNamed("/login"),
    );
  }
}

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('Login'),
      onPressed: () => Navigator.of(context).pushReplacementNamed("/"),
    );
  }
}