在我的应用程序中,我有一个模型来存储在我的应用程序中登录的用户。
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
上的更改以触发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
═══════════════════════════════════════════════════════════════════════════════
如何设置这样的监听器?在这样的模型更改上触发导航是一种好习惯吗?
谢谢
编辑:我找到了一种使这项工作有效的方法。我没有在PrehomeScreen构建器中使用Consumer
,而是使用了以下代码:
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.
════════════════════════════════════════════════════════════════════════════════════════════════════
该应用程序不会崩溃,因此,现在我可以接受。 我仍然想知道这是否是一种好方法。
答案 0 :(得分:0)
我更喜欢使用Stream或rxdart PublishSubject BehaviourSubject收听任何活动或管理全局应用程序数据。
我使用集团模式实现它。基本上,区块模式就像redux一样,react意味着创建一个包含所有应用程序数据的中央数据集,而您不必进行道具钻探。
您可以像这样创建Stream。
UnicodeError: encoding with 'idna' codec failed (UnicodeError: label empty or too long)
现在您可以从任何地方访问该abcBloc变量,并像这样收听连接性变量
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);更新连接。 每次执行任何更改时,监听器都会被调用。
请记住,您必须调用一次listenConnectivity()才能将其激活;
答案 1 :(得分:0)
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()
},
);
}
}
答案 2 :(得分:0)
我认为不需要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()
},
);
}
}
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("/"),
);
}
}