【问题标题】:Future Provider Stuck In loading state未来提供者陷入加载状态
【发布时间】:2021-06-08 20:58:08
【问题描述】:

我正在使用未来的提供程序在加载时显示登录页面,然后在加载时显示加载指示器。这是我未来的提供者

final loginProvider = FutureProvider.family((ref, UserInput input) =>
ref.read(authRepositoryProvider).doLogin(input.email, input.password));

在我的 UI 中,我有这个....

class LoginScreen extends HookWidget {
   final TextEditingController emailEditingController = TextEditingController();
   final TextEditingController passwordEditingController =
       TextEditingController();

  @override
  Widget build(BuildContext context) {
     var userInput =
     UserInput(emailEditingController.text, passwordEditingController.text);

     final login = useProvider(loginProvider(userInput));

     return login.when(
       data: (user) => Login(emailEditingController, passwordEditingController),
       loading: () => const ProgressIndication(),
       error: (error, stack) {
         if (error is DioError) {
         return Login(emailEditingController, passwordEditingController);
       } else {
          return Login(emailEditingController, passwordEditingController);
       }
     },
  );
 }
}

这是我的 doLogin 函数。

@override
  Future<dynamic> doLogin(String email, String password) async {
    try {
       final response = await _read(dioProvider)
          .post('$baseUrl/login', data: {'email': email, 'password': password});
       final data = Map<String, dynamic>.from(response.data);
     return data;
    } on DioError catch (e) {
       return BadRequestException(e.error);
    } on SocketException {
      return 'No Internet Connection';
   }
 }

我想知道为什么它卡在加载状态。任何帮助将不胜感激。

【问题讨论】:

    标签: flutter riverpod


    【解决方案1】:

    首先,family 在给定输入时创建提供程序的新实例。因此,在您的实现中,每当您的文本字段发生更改时,您都会生成一个新的提供者并观察该新提供者。 这很糟糕。

    在您的情况下,为了访问登录状态而保留 UserInput 并没有多大意义。也就是说,在这种情况下,FamilyProvider 并不理想。

    以下是您可以选择如何编写它的示例。 这不是你编写它的唯一方法。如果没有像 Firebase 这样为你处理大部分内容的 API,它可能比流式传输更容易掌握。

    首先,一个 StateNotifierProvider:

    enum LoginState { loggedOut, loading, loggedIn, error }
    
    class LoginStateNotifier extends StateNotifier<LoginState> {
      LoginStateNotifier(this._read) : super(LoginState.loggedOut);
    
      final Reader _read;
      late final Map<String, dynamic> _user;
    
      static final provider =
          StateNotifierProvider<LoginStateNotifier, LoginState>((ref) => LoginStateNotifier(ref.read));
    
      Future<void> login(String email, String password) async {
        state = LoginState.loading;
        try {
          _user = await _read(authRepositoryProvider).doLogin(email, password);
          state = LoginState.loggedIn;
        } catch (e) {
          state = LoginState.error;
        }
      }
    
      Map<String, dynamic> get user => _user;
    }
    

    这使我们可以手动控制登录过程的状态。它不是最优雅的,但实际上,它可以工作。

    接下来是登录屏幕。这是他们得到的准系统。暂时忽略error 参数 - 稍后会清除。

    class LoginScreen extends HookWidget {
      const LoginScreen({Key? key, this.error = false}) : super(key: key);
    
      final bool error;
    
      @override
      Widget build(BuildContext context) {
        final emailController = useTextEditingController();
        final passwordController = useTextEditingController();
    
        return Column(
          children: [
            TextField(
              controller: emailController,
            ),
            TextField(
              controller: passwordController,
            ),
            ElevatedButton(
              onPressed: () async {
                await context.read(LoginStateNotifier.provider.notifier).login(
                      emailController.text,
                      passwordController.text,
                    );
              },
              child: Text('Login'),
            ),
            if (error) Text('Error signing in'),
          ],
        );
      }
    }
    

    您会注意到我们可以使用 useTextEditingController 钩子来处理这些问题。您还可以通过 StateNotifier 看到对login 的调用。

    最后但并非最不重要的一点是,我们需要对我们花哨的新状态做点什么。

    class AuthPage extends HookWidget {
      const AuthPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final loginState = useProvider(LoginStateNotifier.provider);
    
        switch (loginState) {
          case LoginState.loggedOut:
            return LoginScreen();
          case LoginState.loading:
            return LoadingPage();
          case LoginState.loggedIn:
            return HomePage();
          case LoginState.error:
            return LoginScreen(error: true);
        }
      }
    }
    

    在实践中,您可能希望将其包装在另一个带有 Scaffold 的小部件中。

    我知道这与您所问的不完全一样,但我认为看看解决问题的另一种方法可能会有所帮助。

    【讨论】:

    • 感谢它确实有效。我刚刚得到一个 texteditingcontroller 在被处理后被使用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-02
    • 2016-04-16
    • 2021-09-11
    • 1970-01-01
    • 2020-08-22
    • 1970-01-01
    • 2014-06-23
    相关资源
    最近更新 更多