使用 Flutter Provider 将数据传递到另一个屏幕
Posted
技术标签:
【中文标题】使用 Flutter Provider 将数据传递到另一个屏幕【英文标题】:Passing data to another screen with Flutter Provider 【发布时间】:2021-07-14 10:52:38 【问题描述】:我正在尝试使用 Provider
将数据传递到另一个屏幕,但似乎我总是传递相同的数据,除非我对列表进行排序然后传递不同的数据(这意味着我可能正在切换通过对列表进行排序来索引,这就是它现在传递不同数据的原因)。简而言之,我调用 API,填充列表,也为下一页设置提供程序,然后单击我列出上一个屏幕中的信息,但问题是我总是显示相同的项目,除非我对列表。代码如下:
调用 API 并显示列表:
var posts = <RideData>[];
var streamController = StreamController<List<RideData>>();
@override
void initState()
_getRideStreamList();
super.initState();
_getRideStreamList() async
await Future.delayed(Duration(seconds: 3));
var _vehicleStreamData = await APICalls.instance.getRides();
var provider = Provider.of<RideStore>(context, listen: false);
posts = await _vehicleStreamData
.map<RideData>((e) => RideData.fromJson(e))
.toList();
streamController.add(posts);
provider.setRideList(posts, notify: false);
bool isSwitched = true;
void toggleSwitch(bool value)
if (isSwitched == false)
posts.sort((k1, k2) => k1.rideId.compareTo(k2.rideId));
else
posts.sort((k1, k2) => k2.rideId.compareTo(k1.rideId));
@override
Widget build(BuildContext context)
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
TextButton(
child: Text('sort ascending'),
onPressed: ()
setState(()
toggleSwitch(isSwitched = !isSwitched);
);
),
Container(
height: 1000,
child: StreamBuilder<List<RideData>>(
initialData: posts,
stream: streamController.stream,
builder: (context, snapshot)
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index)
return Column(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Text(
'Ride #$snapshot.data[index].rideId',
),
),
FlatButton(
textColor: Colors.blue[700],
minWidth: 0,
child: Text('View'),
onPressed: ()
// here is where I pass the data to the RideInfo screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RideInfo(
rideId: snapshot
.data[index].rideId,
)));
,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'$snapshot.data[index].pickupTime',
),
Text(
'$snapshot.data[index].jobArrived',
),
],
),
],
);
,
);
),
),
],
),
),
),
);
按下查看按钮并将数据传递到另一个屏幕 (RideInfo) 后:
class RideInfo extends StatefulWidget
static const String id = 'ride_info_screen';
String rideId;
RideInfo(@required this.rideId);
@override
_RideInfoState createState() => _RideInfoState();
class _RideInfoState extends State<RideInfo>
String rideID = '';
@override
void initState()
super.initState();
setState(()
rideID = widget.rideId;
);
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Ride #$rideID',
),
),
body: SafeArea(
child: SingleChildScrollView(
child: Consumer<RideStore>(
builder: (context, rideStore, child)
return Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index)
RideData rides = rideStore.getRideByIndex(index);
return Column(
children: [
Expanded(
flex: 2,
child: Column(
children: [
Text(
"PICK UP",
),
// here I display the pickUpTime but it is always the same and I wanted to display the time based on the ID
Text(
'$rides.pickupTimeAM',
),
],
),
),
],
);
),
],
);
,
),
),
),
);
当我按下查看单个项目的视图时,数据(在这种情况下为pickUpTime
)并没有改变,但是就像我说的,当我用排序方法改变列表的顺序时,我得到了不同的数据。
这是提供者模型:
class RideStore extends ChangeNotifier
List<RideData> _rideList = [];
List<RideData> get rideList => _rideList;
setRideList(List<RideData> list, bool notify = true)
_rideList = list;
if (notify) notifyListeners();
RideData getRideByIndex(int index) => _rideList[index];
int get rideListLength => _rideList.length;
如何根据我在骑行信息屏幕中按下并传递的列表中的 ID 显示正确的信息,这样它不会总是返回相同的数据?提前感谢您的帮助!
【问题讨论】:
【参考方案1】:违规代码在RideInfo
:
ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index)
RideData rides = rideStore.getRideByIndex(index);
index
始终为1
,因此您始终显示第一个RideData
。有多种选项可以修复它,例如将index
,甚至将RideData
传递给RideInfo
构造函数:
class RideInfo extends StatefulWidget
static const String id = 'ride_info_screen';
String rideId;
final int index;
RideInfo(@required this.rideId, @required this.index, Key key)
: super(key: key)
和:
RideData rides = rideStore.getRideByIndex(widget.index);
我在代码上有一些额外的 cmets。首先,ListView
在RideInfo
中毫无用处,因此请将其删除。
其次,无需构造streamController
并在父表单中使用StreamBuilder
。您的列表可在RideStore
中找到。所以你的父表单可能有:
Widget build(BuildContext context)
var data = Provider.of<RideStore>(context).rideList;
...
Container(
height: 1000,
child:
// StreamBuilder<List<RideData>>(
// initialData: posts,
// stream: streamController.stream,
// builder: (context, snapshot)
// return
ListView.builder(
shrinkWrap: true,
itemCount: data.length,
我希望这些 cmets 有所帮助。
编辑:
编辑您的代码以使用FutureBuilder
非常简单。首先,让_getRideStreamList
返回它读取的数据:
_getRideStreamList() async
...
return posts;
删除在initState
中对_getRideStreamList
的调用,并将ListView
包装在调用FutureBuilder
的_getRideStreamList
中:
Container(
height: 1000,
child: FutureBuilder(
future: _getRideStreamList(),
builder: (ctx, snapshot)
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
else
var data = snapshot.data;
return ListView.builder(
...
);
,
),
),
这会在等待数据时显示CircularProgressIndicator
。
请注意,这是一个快速的技巧 - 您不希望每次重新构建小部件时都读取数据。所以_getRideStreamList
可以检查数据是否已经被读取并直接返回而不是重新读取。
【讨论】:
首先,谢谢你的详细回答,非常感谢!在这种情况下,我通过了index
,我不确定如何成功地将完整的Ride Data
传递给Ride Info
。其次,我使用了Stream Builder
没有Provider
,所以我没有机会在发布之前对其进行重构,但再次感谢您的提醒,我真的很感激。干杯!
不用担心。您可以将RideData
传递给构造函数上的RideInfo
,就像任何值一样:RideData ride; RideInfo(@required RideData this.ride);
根据您的建议,我删除了不必要的代码,现在的问题是我需要刷新我的小部件,因为 ListView.builder
在从 API 获取数据之前呈现小部件,我尝试使用FutureBuilder
并且我使用了“Provider”,因为我正在调用 API,但我遇到了同样的问题......以上是关于使用 Flutter Provider 将数据传递到另一个屏幕的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Flutter 中使用 Provider 正确获取 API
Flutter - 了解 Provider、Bloc 的生命周期以及何时处理流 [重复]