gpt4 book ai didi

dart - 发生更改时如何在 Flutter 中重建小部件

转载 作者:IT王子 更新时间:2023-10-29 06:35:53 24 4
gpt4 key购买 nike

编辑:我编辑了下面的代码,以突出获取数据的方法以及构建火车估计的小部件(用 "API_URL 替换沿途的任何 API 信息““API_STOP_ID”)。我希望这能更好地帮助我们解决问题!我真的很感激任何人可以提供的任何信息——我一直在为这个项目努力工作!再次感谢大家!

原帖:我有一个 ListTiles 的 ListView,每个 ListView 都有一个尾随小部件,它在一个新的文本小部件中构建火车到达估计。这些尾随的小部件每五秒更新一次(由 print 语句证明)。当应用程序从火车的 API 获取数据时,它会显示一个“无数据”文本小部件,该小部件由 _buildEstimatesNull() 构建。

但是,问题是即使应用程序已完成获取数据并且 _isLoading = false 仍显示“无数据”(由 print 语句证明)。尽管如此,即使解决了这个问题,列车估计也会很快过时,因为尾随的小部件每五秒自行更新一次,但这不会反射(reflect)在实际的应用程序中,因为小部件是在页面加载时构建的。因此,我需要一种方法来在获取新信息时重建那些尾随的小部件。

有没有办法让 Flutter 也每五秒自动重建一次 ListTile 的尾随小部件(或者每当 _buildEstimatesS1 更新/尾随小部件的内部更新时)?

class ShuttleApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new ShuttleState();
}
}

class ShuttleState extends State<ShuttleApp> {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomeScreen(),
);
}
}

class HomeScreen extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new HomeState();
}
}

class HomeState extends State<HomeScreen> {

var _isLoading = true;

void initState() {
super.initState();
_fetchData();
const fiveSec = const Duration(seconds: 5);
new Timer.periodic(fiveSec, (Timer t) {
_fetchData();
});
}

var arrivalsList = new List<ArrivalEstimates>();

_fetchData() async {
arrivalsList.clear();
stopsList.clear();
final url = "API_URL";
print("Fetching: " + url);
final response = await http.get(url);
final busesJson = json.decode(response.body);
if (busesJson["service_id"] == null) {
globals.serviceActive = false;
} else {
busesJson["ResultSet"]["Result"].forEach((busJson) {
if (busJson["arrival_estimates"] != null) {
busJson["arrival_estimates"].forEach((arrivalJson) {
globals.serviceActive = true;
final arrivalEstimate = new ArrivalEstimates(
arrivalJson["route_id"], arrivalJson["arrival_at"], arrivalJson["stop_id"]
);
arrivalsList.add(arrivalEstimate);
});
}
});
}
setState(() {
_isLoading = false;
});
}

Widget _buildEstimateNull() {
return new Container(
child: new Center(
child: new Text("..."),
),
);
}

Widget _buildEstimateS1() {
if (globals.serviceActive == false) {
print('serviceNotActive');
_buildEstimateNull();
} else {
final String translocStopId = "API_STOP_ID";
final estimateMatches = new List<String>();
arrivalsList.forEach((arrival) {
if (arrival.stopId == translocStopId) {
estimateMatches.add(arrival.arrivalAt);
}
});
estimateMatches.sort();
if (estimateMatches.length == 0) {
print("zero");
return _buildEstimateNull();
} else {
return new Container(
child: new Center(
child: new Text(estimateMatches[0]),
),
);
}
}
}

@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: const Color(0xFF171717),
appBar: new AppBar(),
body: new DefaultTextStyle(
style: new TextStyle(color: const Color(0xFFaaaaaa),),
child: new ListView(
children: <Widget>[
new ListTile(
title: new Text('S1: Forest Hills',
style: new TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0)),
subtitle: new Text('Orange Line'),
contentPadding: new EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
trailing: _isLoading ? _buildEstimateNull() : _buildEstimateS1(),
),
],
),
)
);
}

class ArrivalEstimates {
final String routeId;
final String arrivalAt;
final String stopId;
ArrivalEstimates(this.routeId, this.arrivalAt, this.stopId);
}

非常感谢您提供的任何帮助!我真的 super 感谢! :)

最佳答案

有几种方法可以解决这个问题。但是,如果不查看更多代码,就很难判断发生了什么——特别是您如何获取数据以及您正在使用它做什么。但我想无论如何我都能给你一个足够的答案。

这样做的简单方法是:

  1. 有一个 StatefulWidget,它跟踪列表中所有项目的构建估计。它应该从您的 API 请求数据,获取结果,然后调用 setState(() => this.listData = data);。对 setState 的调用告诉小部件它需要重建。
  2. 为列表中的每一项设置一个 StatefulWidget。他们都会每 5 秒执行一次 API 请求,获取结果,然后每个人都会调用 setState(() => this.itemData = data);。这意味着多次调用 API 等。

#1 的优点是你可以批处理 API 调用,而#2 的优点是你的构建整体变化较小(尽管 flutter 的工作方式,这将是非常小的)......所以我可能会如果可能的话,选择#1。

但是,还有更好的方法!

更好的方法是使用某种 API 管理器(或任何您想要的名称)来处理与您的 API 的通信。它可能会在您的小部件树中位于更高的位置,并且会以您想要的任何逻辑启动/停止。根据小部件树在多远的位置,您可以将它传递给每个 child ,或者更可能将它保存在一个 InheritedWidget 中,然后可以使用它从每个列表元素或整个列表中检索它。

API 管理器会提供各种 streams - 根据您的 API,使用一堆命名字段/方法或使用 getStream(id) 类型的结构。

然后,在您的各种列表元素中,您将使用 StreamBuilder基于数据构建每个元素的小部件 - 通过使用 StreamBuilder,您可以获得一个 ConnectionState 对象,该对象让您知道流是否已接收到任何数据,因此您可以选择显示 isLoading 类型的小部件而不是显示数据的小部件.

通过使用这种更高级的方法,您可以获得:

  • 可维护性
    • 如果您的 API 发生变化,您只需更改 API 管理器
    • 您可以编写更好的测试,因为 API 交互和 UI 交互是分开的
  • 可扩展性
    • 如果您稍后使用推送通知进行更新,而不是每 5 秒对服务器执行一次 ping 操作,则可以将其合并到 API 管理器中,这样它就可以在不接触 UI 的情况下简单地更新流

编辑:根据 OP 的评论,他们已经或多或少地实现了第一个建议。但是,代码存在一些问题。我将在下面列出它们,并且我已经发布了一些更改的代码。

  1. arrivalsList 应该在每次完成新构建时被替换,而不是简单地被更改。这是因为 dart 会比较列表,如果找到相同的列表,它不一定会比较所有元素。此外,虽然在函数中间更改它不一定会导致问题,但通常最好使用局部变量然后在最后更改值。请注意,该成员实际上是在 setState 中设置的。
  2. 如果 serviceActive == false,则 return _buildEstimateNull(); 错过了返回。

代码如下:

class HomeState extends State<HomeScreen> {

var _isLoading = true;

void initState() {
super.initState();
_fetchData();
const fiveSec = const Duration(seconds: 5);
new Timer.periodic(fiveSec, (Timer t) {
_fetchData();
});
}

var arrivalsList = new List<ArrivalEstimates>();

_fetchData() async {
var arrivalsList = new List<ArrivalEstimates>(); // *********** #1
stopsList.clear();
final url = "API_URL";
print("Fetching: " + url);
final response = await http.get(url);
final busesJson = json.decode(response.body);
if (busesJson["service_id"] == null) {
print("no service id");
globals.serviceActive = false;
} else {
busesJson["ResultSet"]["Result"].forEach((busJson) {
if (busJson["arrival_estimates"] != null) {
busJson["arrival_estimates"].forEach((arrivalJson) {
globals.serviceActive = true;
final arrivalEstimate = new ArrivalEstimates(
arrivalJson["route_id"], arrivalJson["arrival_at"], arrivalJson["stop_id"]
);
arrivalsList.add(arrivalEstimate);
});
}
});
}
setState(() {
_isLoading = false;
this.arrivalsList = arrivalsList; // *********** #1
});
}

Widget _buildEstimateNull() {
return new Container(
child: new Center(
child: new Text("..."),
),
);
}

Widget _buildEstimateS1() {
if (globals.serviceActive == false) {
print('serviceNotActive');
return _buildEstimateNull(); // ************ #2
} else {
final String translocStopId = "API_STOP_ID";
final estimateMatches = new List<String>();
print("arrivalsList length: ${arrivalsList.length}");
arrivalsList.forEach((arrival) {
if (arrival.stopId == translocStopId) {
print("Estimate match found: ${arrival.stopId}");
estimateMatches.add(arrival.arrivalAt);
}
});
estimateMatches.sort();
if (estimateMatches.length == 0) {
print("zero");
return _buildEstimateNull();
} else {
return new Container(
child: new Center(
child: new Text(estimateMatches[0]),
),
);
}
}
}

@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: const Color(0xFF171717),
appBar: new AppBar(),
body: new DefaultTextStyle(
style: new TextStyle(color: const Color(0xFFaaaaaa),),
child: new ListView(
children: <Widget>[
new ListTile(
title: new Text('S1: Forest Hills',
style: new TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0)),
subtitle: new Text('Orange Line'),
contentPadding: new EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
trailing: _isLoading ? _buildEstimateNull() : _buildEstimateS1(),
),
],
),
)
);
}

关于dart - 发生更改时如何在 Flutter 中重建小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51033222/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com