我用于我的应用程序 jwt 身份验证。我有带有 bloc 模式的主页,它选择页面将其加载到屏幕上(LoginScreen 或 AppScreen)。LoginScreen 有自己的 bloc 模式,也是 AppScreen。当我输入登录名和密码并单击登录时,会调度 LoginEvent,它将请求发送到 Web 服务并使用 jwt 令牌获取响应,该令牌将存储在共享首选项中,但同时 AppScreen 用他的 bloc 呈现,在这个AppScreen 的时间块正在发送数据请求(需要 jwt 令牌)但为空,因为尚未存储 jwt 令牌。我发现一种解决方法不是优雅的方式,我也可以使用延迟,但这不是正确的方式......
AppScreen 的解决方法:
@override
void initState() {
super.initState();
_bloc = BottomNavigationBloc(workoutTypeRepository: WorkoutTypeRepository());
_bloc.add(AppScreenLunched());
_bloc.add(AppScreenLunched());
}
如果我留下_bloc.add(AppScreenLunched());
数据无法加载到主页,因为它会从 Web 服务接收 null
如果我离开
_bloc.add(AppScreenLunched());
_bloc.add(AppScreenLunched());
按第一个add
数据为空
到第二个add
我终于得到了与块一起提供给主页的数据
有什么办法吗?
开始画面:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
AuthBloc _authBloc;
LoginRepository _loginRepository;
@override
void initState() {
super.initState();
_loginRepository = LoginRepository();
_authBloc = AuthBloc(loginRepository: _loginRepository);
_authBloc.add(AppStarted());
}
@override
void dispose() {
super.dispose();
_authBloc.close();
}
@override
Widget build(BuildContext context) {
return BlocProvider<AuthBloc>(
create: (_) => _authBloc,
child: MaterialApp(
title: 'TEST',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocBuilder(
bloc: _authBloc,
builder: (BuildContext context, AuthState state) {
if (state is AuthUninitialized) {
return LoginPage(loginRepository: _loginRepository);
} else if (state is AuthAuthenticated) {
return AppScreen();
} else if (state is AuthUnauthenticated) {
return LoginPage(loginRepository: _loginRepository);
} else if (state is AuthLoading) {
return LoadingIndicator();
}
return LoginPage(loginRepository: _loginRepository);
}
)
)
);
}
}
登录屏幕(部分):
class LoginForm extends StatefulWidget {
@override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final registerRepository = RegisterRepository();
LoginBloc _loginBloc;
@override
void initState() {
super.initState();
_loginBloc = BlocProvider.of<LoginBloc>(context);
}
@override
void dispose() {
super.dispose();
_loginBloc.close();
}
@override
Widget build(BuildContext context) {
_onLoginButtonPressed() {
_loginBloc.add(LoginButtonPressed(
email: _emailController.text,
password: _passwordController.text,
));
}
应用屏幕:
class AppScreen extends StatefulWidget {
@override
_AppScreenState createState() => _AppScreenState();
}
class _AppScreenState extends State<AppScreen> {
BottomNavigationBloc _bloc;
@override
void initState() {
super.initState();
_bloc = BottomNavigationBloc(workoutTypeRepository: WorkoutTypeRepository());
_bloc.add(AppScreenLunched());
_bloc.add(AppScreenLunched());
}
@override
void dispose() {
super.dispose();
_bloc.close();
}
@override
Widget build(BuildContext context) {
return BlocProvider<BottomNavigationBloc>(
create: (_) => _bloc,
child: BlocBuilder(
bloc: _bloc,
builder: (BuildContext context, BottomNavigationState state) {
return Scaffold(
appBar: AppBar(
title: Text('TEST'),
),
body: _blocBuilder(context, state),
bottomNavigationBar: BottomActionBar(),
);
},
),
);
}
Widget _blocBuilder(BuildContext context, BottomNavigationState state) {
if (state is PageLoading) {
return Center(child: CircularProgressIndicator());
} else if (state is HomePageLoaded) {
return HomePage(workoutTypes: state.workoutTypes);
} else if (state is SearchPageLoaded) {
return Center(child: Text("SearchPage"));
// return SearchPage();
} else if (state is WorkoutPageLoaded) {
// return Center(child: Text("WorkoutPage"));
return WorkoutPage();
} else if (state is FavoritePageLoaded) {
return Center(child: Text("FavoritePage"));
// return SearchPage();
} else if (state is ProfilePageLoaded) {
return Center(child: Text("ProfilePage"));
// return ProfilePage();
}
return Container();
}
}
授权块:
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final LoginRepository loginRepository;
AuthBloc({@required this.loginRepository}) : assert(loginRepository != null);
@override
AuthState get initialState => AuthUninitialized();
@override
Stream<AuthState> mapEventToState(
AuthEvent event,
) async* {
if (event is AppStarted) {
final bool hasToken = await loginRepository.hasToken();
if (hasToken) {
yield AuthAuthenticated();
} else {
yield AuthUnauthenticated();
}
}
if (event is LoggedIn) {
final bool hasToken = await loginRepository.hasToken();
if (hasToken) {
yield AuthAuthenticated();
} else {
yield AuthUnauthenticated();
}
}
if (event is LoggedOut) {
yield AuthLoading();
await loginRepository.deleteToken();
yield AuthUnauthenticated();
}
}
}
登录块:
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final LoginRepository loginRepository;
final AuthBloc authBloc;
LoginBloc({
@required this.loginRepository,
@required this.authBloc,
}) : assert(loginRepository != null),
assert(authBloc != null);
LoginState get initialState => LoginInitial();
@override
Stream<LoginState> mapEventToState(LoginEvent event) async* {
if (event is LoginButtonPressed) {
yield LoginLoading();
try {
await loginRepository.authenticate(
email: event.email,
password: event.password,
);
authBloc.add(LoggedIn());
yield LoginInitial();
} catch (error) {
yield LoginFailure(error: error.toString());
}
}
}
}
底部导航栏块:
class BottomNavigationBloc extends Bloc<BottomNavigationEvent, BottomNavigationState> {
int currentIndex = 0;
final WorkoutTypeRepository workoutTypeRepository;
BottomNavigationBloc({this.workoutTypeRepository}) : assert(workoutTypeRepository != null);
@override
BottomNavigationState get initialState => PageLoading();
@override
Stream<BottomNavigationState> mapEventToState(BottomNavigationEvent event) async* {
if (event is AppScreenLunched) {
this.add(PageTapped(index: this.currentIndex));
}
if (event is PageTapped) {
this.currentIndex = event.index;
yield CurrentIndexChanged(currentIndex: this.currentIndex);
yield PageLoading();
if (this.currentIndex == 0) {
final workoutTypes = await getWorkoutType();
yield HomePageLoaded(workoutTypes: workoutTypes);
}
if (this.currentIndex == 1) {
yield SearchPageLoaded();
}
if (this.currentIndex == 2) {
yield WorkoutPageLoaded();
}
if (this.currentIndex == 3) {
yield FavoritePageLoaded();
}
if (this.currentIndex == 4) {
yield ProfilePageLoaded();
}
}
}
Future<List<WorkoutType>> getWorkoutType() async {
List<WorkoutType> workoutType = await workoutTypeRepository.getWorkoutType();
return workoutType;
}
}