flutter - Flutter:子 block 正在初始化,但是数据尚未存储在shared_preferences中

标签 flutter dart bloc flutter-navigation

我使用我的应用程序jwt认证。我有带有块模式的主页,该页面选择要在屏幕上加载页面的页面(LoginScreen或AppScreen)。 LoginScreen有自己的块模式,也有AppScreen。
当我输入登录名和密码并单击登录时,将调度LoginEvent,该请求将请求发送到Web服务并获得带有jwt token 的响应,该 token 将存储在共享首选项中,但与此同时,AppScreen会用其bloc呈现。 AppScreen的时间块正在发送数据请求(需要jwt token ),但会为null,因为尚未存储jwt token 。我发现一种解决方法不是一种优雅的方法,也可以使用延迟,但这不是正确的方法...

解决AppScreen:

@override
  void initState() {
    super.initState();
    _bloc = BottomNavigationBloc(workoutTypeRepository: WorkoutTypeRepository());
    _bloc.add(AppScreenLunched());
    _bloc.add(AppScreenLunched());
  }

如果我离开_bloc.add(AppScreenLunched());数据无法加载到Homepage,因为它从Web服务接收到null

如果我离开
_bloc.add(AppScreenLunched()); 
_bloc.add(AppScreenLunched()); 

通过第一个add数据为空

通过第二个add我最终获得了随bloc提供给主页的数据

有什么办法吗?

开始屏幕:
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);
                }
            )
        )
    );
  }
}

LoginScreen(的一部分):
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,
      ));
    }

AppScreen:
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();
  }
}

AuthBloc:
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();
    }
  }
}

LoginBloc:
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());
      }
    }
  }
}

BottomNavigationBarBloc:
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;
  }
}

最佳答案

主页:

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: (context) => _authBloc,
        child: MaterialApp(
            title: 'TEST',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: BlocBuilder(
                bloc: _authBloc,
                builder: (BuildContext context, AuthState state) {
                  if (state is AuthUninitialized) {
                    return LoadingIndicator();
//                    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 LoginPage extends StatelessWidget {
  final LoginRepository loginRepository;

  LoginPage({
    Key key,
    @required this.loginRepository
  }) : assert(loginRepository != null), super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: BlocProvider(
          create: (context) {
            return LoginBloc(
              loginRepository: loginRepository,
              authBloc: BlocProvider.of<AuthBloc>(context)
            );
          },
          child: Stack(
            children: <Widget>[
              new Container(
                  decoration: BoxDecoration(
                      image: backgroundImage
                  )
              ),
              new Center(
                child: SingleChildScrollView(
                  child: Column(
                      children: <Widget>[
                        LoginForm()
                      ]
                  ),
                ),
              )
            ],
          ),
        )
    );
  }
}

AuthBloc:
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();
    }
  }
}

LoginBloc:
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 LoginRepository {
  LoginProvider _loginProvider;

  LoginRepository({LoginProvider loginProvider}) {
    _loginProvider = loginProvider ?? LoginProvider();
  }

  Future<String> authenticate({String email, String password}) async {
    if (email == null || password == null) {
      return "Nie podanu loginu lub hasła";
    }

    var loginData =
        await _loginProvider.login(email: email, password: password);
    var token = AuthToken.fromJson(loginData);
    return token.accessToken;
  }

  Future<bool> hasToken() async {
    String token = await LocalStorage.get(Constant.ACCESS_TOKEN);
    if (token != null) {
      return true;
    }
    return false;
  }

  Future<void> persistToken(String token) async {
    if (token != null) {
      await LocalStorage.save(Constant.ACCESS_TOKEN, "${Constant.TOKEN_TYPE_BEARER} $token");
    }
  }

  Future<void> deleteToken() async {
    await LocalStorage.remove(Constant.ACCESS_TOKEN);
  }
}

本地存储:
class LocalStorage {
  static save(String key, String value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(key, value);
  }

  static Future<String> get(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.get(key);
  }

  static remove(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove(key);
  }

  static removeAll() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var keys = prefs.getKeys();
    keys.map((key) {
      remove(key);
    });
  }
}

关于flutter - Flutter:子 block 正在初始化,但是数据尚未存储在shared_preferences中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59111719/

相关文章:

Flutter BLoC `buildWhen` 属性

flutter - 飞镖 flutter : How to run animation from a scoped-model?

image - 如何在 flutter 中使用暗模式和不同分辨率的 Assets 图像?

flutter - 在dart中的列表上执行异步操作

flutter - 如何在Dart中将utf8字符串转换为LATIN1?

flutter - 我们什么时候应该使用 freeze 作为密封类或构造函数?

flutter/Dart : speech to text (offline and continuous) for any language

Flutter:更改ListView.builder中选定按钮的背景颜色

swift - Flutter Google Maps iOS构建失败

flutter - 导航到另一个页面 View 或任何 View 时,Bloc 小部件引用技术数据(重建)