- 作者:老汪软件技巧
- 发表时间:2024-12-01 04:02
- 浏览量:
Flutter中文网 中Futter for iOS 开发者线程和异步存在问题修改
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
showLoadingDialog() {
return widgets.length == 0;
}
getBody() {
if (showLoadingDialog()) {
return getProgressDialog();
} else {
return getListView();
}
}
getProgressDialog() {
return Center(child: CircularProgressIndicator());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: getBody());
}
ListView getListView() => ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
});
Widget getRow(int i) {
return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}"));
}
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}
上述代码存在问题有:
你的代码中有几个潜在的问题可能导致UI更新不正确。下面我将逐一分析并提供可能的解决方案:
Isolate与UI线程更新:
Flutter的UI更新必须在主线程上进行。由于你在Isolate中进行了网络请求和数据处理,然后尝试通过setState更新UI,这通常是不被允许的,因为setState需要在UI线程(即主线程)上调用。然而,在你的代码中,setState是在_SampleAppPageState类的loadData方法中调用的,这个方法虽然是由Isolate触发的,但它是_SampleAppPageState的一个成员方法,并且由于Flutter的UI线程是单线程的,只要loadData最终是在UI线程上被调用(比如通过事件循环),setState就是合法的。但这里的问题是,你的代码结构使得这一点变得不清晰和容易出错。
解决方案:确保loadData方法(特别是其中的setState调用)是在UI线程上执行的。虽然在这个特定情况下可能不是问题(因为loadData是在initState中直接调用的,而initState是在UI线程上执行的),但更好的做法是使用Flutter提供的异步机制(如compute或直接在UI线程上调用异步函数)来处理这类任务,以避免线程混淆。
showLoadingDialog 方法的逻辑 :
你的showLoadingDialog方法检查widgets列表的长度来决定是否显示加载动画。然而,这个方法是在每次build时被调用的,而widgets列表的更新是在loadData中通过setState完成的。这里的问题是,showLoadingDialog的返回值是基于widgets列表的当前状态,但这个状态可能在setState调用之前就已经被build方法使用了。
解决方案:你应该使用一个布尔状态变量(比如isLoading)来明确指示是否正在加载数据,而不是依赖于widgets列表的长度。在initState中初始化这个变量为true,在loadData的setState调用中将其设置为false。然后,在getBody方法中使用这个变量来决定是否显示加载动画。
示例代码:
class _SampleAppPageState extends State {
List widgets = [];
bool isLoading = true; // 新增状态变量
@override
void initState() {
super.initState();
loadData();
}
getBody() {
if (isLoading) { // 使用isLoading变量
return getProgressDialog();
} else {
return getListView();
}
}
loadData() async {
// ... 其他代码不变 ...
setState(() {
widgets = msg;
isLoading = false; // 更新isLoading状态
});
}
// ... 其他代码不变 ...
}
Isolate中的错误处理:
你的Isolate代码中没有明显的错误处理逻辑。如果网络请求失败或JSON解析出错,你的Isolate可能会崩溃,而不会通知主线程。
解决方案:在dataLoader函数中添加错误处理逻辑,并通过SendPort将错误信息发送回主线程。然后,在主线程中处理这些错误,可能通过显示错误消息或重试请求。
示例代码:
static dataLoader(SendPort sendPort) async {
ReceivePort port = ReceivePort();
sendPort.send(port.sendPort);
await for (var msg in port) {
String dataURL = msg[0];
SendPort replyTo = msg[1];
try {
http.Response response = await http.get(Uri.parse(dataURL));
replyTo.send(jsonDecode(response.body));
} catch (e) {
replyTo.send(null); // 或者发送一个错误对象
}
}
}
并在主线程中检查msg是否为null或错误对象来处理错误。通过以上修改,你的代码应该能够更可靠地更新UI,并在数据加载完成后正确显示列表。
上述代码中,如果请求失败如何在屏幕中间显示text加载失败?
要在请求失败时在屏幕中间显示'加载失败'的文本,你需要对loadData方法进行修改,以便在捕获到异常时更新一个表示错误状态的状态变量。然后,你需要在getBody方法中添加逻辑来检查这个错误状态,并相应地显示错误消息、加载动画或列表视图。
下面是如何实现这一点的步骤:
添加错误状态变量:
在_SampleAppPageState类中添加一个布尔状态变量hasError,用于指示是否发生了错误。修改loadData方法:
在loadData方法中,使用try-catch语句来捕获可能发生的异常,并在捕获到异常时将hasError设置为true。修改getBody方法:
在getBody方法中,添加一个检查hasError的逻辑。如果hasError为true,则显示一个包含'加载失败'文本的Center组件;否则,根据isLoading的状态显示加载动画或列表视图。
下面是修改后的代码示例:
dart
class _SampleAppPageState extends State {
List widgets = [];
bool isLoading = true;
bool hasError = false; // 新增错误状态变量
@override
void initState() {
super.initState();
loadData();
}
getBody() {
if (hasError) { // 检查是否有错误
return Center(child: Text("加载失败")); // 显示错误消息
} else if (isLoading) { // 检查是否正在加载
return getProgressDialog(); // 显示加载动画
} else {
return getListView(); // 显示列表视图
}
}
loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
SendPort sendPort = await receivePort.first;
try {
List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts");
setState(() {
widgets = msg;
isLoading = false;
hasError = false; // 清除错误状态
});
} catch (e) {
print("Error: $e"); // 打印错误信息到控制台(可选)
setState(() {
isLoading = false;
hasError = true; // 设置错误状态
});
}
}
// ... 其他代码不变 ...
}
现在,当请求失败时,hasError将被设置为true,并且getBody方法将显示一个包含'加载失败'文本的Center组件。如果请求成功,hasError将被清除,并且根据isLoading的状态显示加载动画或列表视图。