为啥 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题?

Posted

技术标签:

【中文标题】为啥 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题?【英文标题】:Why is BloCListener in Flutter not saving state, and there's a setState() issue during authentication?为什么 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题? 【发布时间】:2021-01-02 05:59:54 【问题描述】:

我正在使用 Chopper 和 BLoC 系统在我的 Flutter 应用程序上实现登录身份验证。我的完整代码是here。 LoginForm 中的 BlocListner 效果很好,认证后可以导航到 HomePage,但是有两个问题:

    当我在模拟器上终止应用并重新启动时,它不会记住登录状态,因此用户必须重新登录。 在用户通过身份验证后,在导航过程中会出现错误 2 秒。

问题 1: 这是我对 main.dart 的内容:

void main() async 
  WidgetsFlutterBinding.ensureInitialized();
  setupLocator();
  runApp(MultiBlocProvider(providers: [
    BlocProvider(create: (BuildContext context) 
      return DispatchBloc();
    ),
    BlocProvider(create: (BuildContext context) 
      return HistoryBloc();
    ),
    BlocProvider(create: (BuildContext context) 
      return LoginBloc();
    ),
  ], child: MyApp()));


class MyApp extends StatefulWidget 
  @override
  _MyAppState createState() => _MyAppState();


class _MyAppState extends State<MyApp> 
  LoginBloc loginBloc;
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Dispatch Executive App',
      theme: ThemeData(
          primarySwatch: Colors.green,
          textTheme: Theme.of(context).textTheme.apply(
                fontSizeDelta: 1.5,
              )),
      //         home: LoginPage(),
      routes: 
        '/': (context) 
          return BlocListener<LoginBloc, LoginState>(
            cubit: loginBloc,
            listener: (BuildContext context, state) ,
            child: BlocBuilder<LoginBloc, LoginState>(
              builder: (BuildContext context, LoginState state) 
                if (state is LoadedLoginState) 
                  print("loaded log in from home!");
                  return Home(token: state.login.token);
                 else 
                  print(state);
                  return LoginPage();
                
                //return Home();
              ,
            ),
          );
        ,
        '/home': (context) => Home(),
      ,
      //     home: LoginPage(), //change this for testing
    );
  

问题 2: This issue 认证成功后出现 2 秒,导航器指向首页 这就是我的登录表单。 BlocListener 和 BlocBuilder 在这里工作正常!

BlocBuilder(
                  cubit: loginBloc,
                  builder: (context, state) 
                    if (state is LoadingLoginState) 
                      return Center(
                        child: CircularProgressIndicator(),
                      );
                    
                    if (state is LoadedLoginState) 
                      print(state);
                      print("in login form");
                      print(state.login.token);
                      Navigator.pushReplacementNamed(context, '/home');

                      return Text("Success " + state.login.token);
                    
                    if (state is ErrorLoginState) 
                      return Text("Error" + state.error);
                     else 
                      return SizedBox(width: 20.0);
                    
                  ,
                ),

请帮忙!非常感谢。

【问题讨论】:

您想第二次自动进入,无需再次登录?这是你的需要吗? 第二点,我导航了几次,但没有收到任何错误。当您收到错误消息时? 【参考方案1】:

对于第一个问题,您应该将密钥保存在手机内存中。为此,您可以使用来自 Flutter 的 shared_preferences 包。

这是一个例子。

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'SharedPreferences Demo',
      home: SharedPreferencesDemo(),
    );
  


class SharedPreferencesDemo extends StatefulWidget 
  SharedPreferencesDemo(Key key) : super(key: key);

  @override
  SharedPreferencesDemoState createState() => SharedPreferencesDemoState();


class SharedPreferencesDemoState extends State<SharedPreferencesDemo> 
  Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
  Future<int> _counter;

  Future<void> _incrementCounter() async 
    final SharedPreferences prefs = await _prefs;
    final int counter = (prefs.getInt('counter') ?? 0) + 1;

    setState(() 
      _counter = prefs.setInt("counter", counter).then((bool success) 
        return counter;
      );
    );
  

  @override
  void initState() 
    super.initState();
    _counter = _prefs.then((SharedPreferences prefs) 
      return (prefs.getInt('counter') ?? 0);
    );
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: const Text("SharedPreferences Demo"),
      ),
      body: Center(
          child: FutureBuilder<int>(
              future: _counter,
              builder: (BuildContext context, AsyncSnapshot<int> snapshot) 
                switch (snapshot.connectionState) 
                  case ConnectionState.waiting:
                    return const CircularProgressIndicator();
                  default:
                    if (snapshot.hasError) 
                      return Text('Error: $snapshot.error');
                     else 
                      return Text(
                        'Button tapped $snapshot.data time$snapshot.data == 1 ? '' : 's'.\n\n'
                        'This should persist across restarts.',
                      );
                    
                
              )),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  

关于第二期,

【讨论】:

以上是关于为啥 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 Flutter 应用中的 FAB 很大?

为啥 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题?

为啥我们在 Flutter/Dart 中的 catch 参数中加上“e”?

Flutter - DEBUG 中的 Webview 正在工作,但在 RELEASE apk 上显示为空白,为啥?

Dart/Flutter:当作为参数传递时,List 中的元素字符串变为空(为啥??)

为啥我在 Flutter 中使用 Firestore 中的 foreach 方法后得到空值?