• 作者:老汪软件技巧
  • 发表时间: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();

线程实现异步__线程future同步异步

} 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的状态显示加载动画或列表视图。


上一条查看详情 +不可错过的JSON验证神器JSON Schema Validator
下一条 查看详情 +没有了