我正在使用未来的提供程序在加载时显示登录页面,然后在加载时显示加载指示器。这是我未来的提供者
final loginProvider = FutureProvider.family((ref, UserInput input) =>
ref.read(authRepositoryProvider).doLogin(input.email, input.password));
在我的用户界面中,我有这个......
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';
}
}
我想知道为什么它卡在加载状态。任何帮助将不胜感激。
答案 0 :(得分: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 的小部件中。
我知道这与您所问的不完全相同,但我认为查看解决问题的另一种方法可能会有所帮助。