【问题标题】:Flutter - updating state and THEN navigating awayFlutter - 更新状态然后导航离开
【发布时间】:2022-12-31 21:45:23
【问题描述】:
我有一个名为 AuthenticatingScreen 的有状态小部件,我正在尝试执行以下流程...
- 让用户知道我们正在登录的输出消息
- 获取用户 oAuth 令牌(调用服务文件)
- 更新消息让用户知道我们正在加载他们的详细信息
- 获取用户详细信息并将其重定向
问题是,在第三步,我正在重建状态,这又导致再次触发 build 方法并再次调用该服务,从而触发异常。
import 'package:flutter/material.dart';
import 'package:testing/services/auth_service.dart';
class AuthenticatingScreen extends StatefulWidget {
final String token;
AuthenticatingScreen(this.token);
@override
State<AuthenticatingScreen> createState() => _AuthenticatingScreenState();
}
class _AuthenticatingScreenState extends State<AuthenticatingScreen> {
// step 1) our default message
String _message = 'Please wait while we log you in...';
Future<void> _fetchUserDetails() {
return Future.delayed(const Duration(seconds: 3), () {
// ToDo: fetch user details from the server
});
}
@override
Widget build(BuildContext context) {
// step 2) get our oAuth token
AuthService.handleCallback(widget.token).then((accessCode) async {
// step 3) update our message
setState(() => _message = 'We\'re just getting your details');
// step 4) retrieve our user details and redirect away
_fetchUserDetails().then((_) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/home',
(Route<dynamic> route) => false,
);
});
});
/// output our authenticating screen.
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(bottom: 20.0),
child: CircularProgressIndicator(),
),
Text(_message),
],
),
),
);
}
}
我的问题是:我如何解决这个问题/提取这个逻辑以仅在创建小部件时触发,同时仍然可以访问构建上下文以进行导航?
我试过使小部件本身无状态并将消息和微调器提取到一个单独的小部件中,但单独更改输入参数仍然不会强制重建。
【问题讨论】:
标签:
flutter
state
flutter-state
【解决方案1】:
好的,所以我已经找到了解决方案。在 build() 方法中进行服务调用似乎不是一个好主意。
将我的服务调用移动到一个 void 函数中,然后可以在 initState() 方法中调用它似乎是可行的方法。
import 'package:flutter/material.dart';
import 'package:testing/screens/home.dart';
import 'package:testing/services/auth_service.dart';
class AuthenticatingScreen extends StatefulWidget {
final String token;
AuthenticatingScreen(this.token);
@override
State<AuthenticatingScreen> createState() => _AuthenticatingScreenState();
}
class _AuthenticatingScreenState extends State<AuthenticatingScreen> {
/// the default message to display to the user.
String _message = 'Please wait while we log you in...';
void _authenticateUser(String token) {
AuthService.handleCallback(widget.token).then((accessCode) async {
// we've got the users token, now we need to fetch the user details
setState(() => _message = 'We're just getting your details');
// after fetching the user details, push them to the home screen
_fetchUserDetails().then((_) {
Navigator.of(context).pushNamedAndRemoveUntil(
HomeScreen.name,
(Route<dynamic> route) => false,
);
});
});
}
Future<void> _fetchUserDetails() {
return Future.delayed(const Duration(seconds: 3), () {
// ToDo: fetch user details from the server
});
}
@override
void initState() {
super.initState();
_authenticateUser(widget.token);
}
@override
Widget build(BuildContext context) {
/// output our authenticating screen.
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(bottom: 20.0),
child: CircularProgressIndicator(),
),
Text(_message),
],
),
),
);
}
}
这样,当再次调用build() 方法进行重建时,只需重绘很少的细节。
【解决方案2】:
你可以这样做,我通常使用 getx 和控制器来实现这一点。
- 最好在控制器中分离 UI 类和服务类
- 使 UI 类有状态
- 在onInit()方法中调用API,只调用一次就会触发
服务等级
- 在API方法中得到结果200时,启动UI转换