Flutter:如何为 StreamBuilder 制作 http 流
Posted
技术标签:
【中文标题】Flutter:如何为 StreamBuilder 制作 http 流【英文标题】:Flutter : How to make an http stream for StreamBuilder 【发布时间】:2020-10-07 13:28:33 【问题描述】:你好
我正在尝试使用 Flutter 制作我的第一个社交应用,但遇到了困难。 我想从我的 api 获取我的消息(在两个用户之间的对话中)。 当我使用 Future 和 Future Builder 时没有问题,但我希望在发送新消息时更新消息列表!
我发现我们可以使用流实现它,但是每次我尝试在流中转换我的 Future 时,它仍然有效,但就像它是一个 Future(它永远不会更新新消息)。
这里是我的代码的简化部分:
class Test extends StatelessWidget
final Conv conv;
final User otherUser;
const Test(Key key, this.conv, this.otherUser) : super(key: key);
Stream<List<Message>> messageFlow(String convId)
return Stream.fromFuture(getMessages(convId));
Future<List<Message>> getMessages(String convId) async
var data = await http
.post(MyApiUrl, headers: <String, String>, body: <String, String>
"someParam": "param",
"id": convId,
);
var jsonData = json.decode(data.body);
List<Message> messages = [];
for (var m in jsonData)
Message message = Message.fromJson(m);
messages.add(message);
return messages;
@override
Widget build(BuildContext context)
return StreamBuilder(
stream: messageFlow(conv.id),
builder: (BuildContext context, AsyncSnapshot snapshot)
if (snapshot.data == null)
return Container(
child: Center(
child: Text('Loading'),
),
);
return ListView.builder(
reverse: true,
controller: _messagesListController,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index)
Message message = snapshot.data[index];
var isMe = message.owner == otherUser.id ? false : true;
return _buildMessage(message, isMe);
);
);
如果你能帮助我,那就太好了!
【问题讨论】:
将Future
转换为Stream
与您发现的仅使用Future
基本相同。您需要创建自己的Stream
,它会定期执行您现有的Future
。查看this 的“从头开始创建流”部分。
【参考方案1】:
这对我有用
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as HTTP;
class PeriodicRequester extends StatelessWidget
Stream<http.Response> getRandomNumberFact() async*
yield* Stream.periodic(Duration(seconds: 5), (_)
return http.get("http://numbersapi.com/random/");
).asyncMap((event) async => await event);
@override
Widget build(BuildContext context)
return StreamBuilder<http.Response>(
stream: getRandomNumberFact(),
builder: (context, snapshot) => snapshot.hasData
? Center(child: Text(snapshot.data.body))
: CircularProgressIndicator(),
);
【讨论】:
【参考方案2】:我无法复制您的示例代码,但在这里我是如何理解您的问题的。
我们先来定义一下Future
和Streams
的区别:
来自this SO post
Future 就像带有数字的令牌,当他们给你 你点外卖;你提出了请求,但结果还没有 准备好了,但你有一个占位符。当结果准备好时,你 获得回电(外卖柜台上方的数字板显示您的 号码或他们喊出来) - 你现在可以进去拿你的食物 (结果)取出。
溪流就像那条背着小寿司碗的腰带。通过坐 在那张桌子上,您已经“订阅”了该流。你不知道 下一艘寿司船何时到达 - 但当厨师(消息 source) 将其放在流(带)中,然后订阅者将 收到它。需要注意的重要一点是他们到达 异步(你不知道下一条船/消息什么时候来) 但它们将按顺序到达(即,如果厨师放置三种类型 腰带上的寿司,按顺序排列——你会看到它们从你身边走过 以相同的顺序)
下面是一个例子,告诉你如何create your own stream from scratch:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
class MyHomePage extends StatefulWidget
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
// 1st approach
final StreamController _streamController = StreamController();
addData()async
for(int i = 1; i<= 10; i++)
await Future.delayed(Duration(seconds: 1));
_streamController.sink.add(i);
// 2nd approach
// This approach will prevent some approach of memory leaks
Stream<int> numberStream() async*
for(int i = 1; i<= 10; i++)
await Future.delayed(Duration(seconds: 1));
yield i;
@override
void dispose()
// TODO: implement dispose
super.dispose();
_streamController.close();
@override
void initState()
// TODO: implement initState
super.initState();
addData();
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("Stream"),
),
body: Center(
child: StreamBuilder(
stream: numberStream().map((number) => "number $number"),
builder: (context, snapshot)
if(snapshot.hasError)
return Text("hey there is some error");
else if (snapshot.connectionState == ConnectionState.waiting)
return CircularProgressIndicator();
return Text("$snapshot.data", style: Theme.of(context).textTheme.display1,);
,
)
),
);
您也可以查看this SO post 以获取一些参考资料。
在这里,我调整了上面 SO 帖子中的示例,以创建一个迷你简单的聊天服务器来显示消息如何更新。
import 'dart:async';
import 'package:flutter/material.dart';
class Server
StreamController<String> _controller = new StreamController.broadcast();
void simulateMessage(String message)
_controller.add(message);
Stream get messages => _controller.stream;
final server = new Server();
class HomeScreen extends StatefulWidget
@override
_HomeScreenState createState() => new _HomeScreenState();
class _HomeScreenState extends State<HomeScreen>
List<String> _messages = <String>[];
StreamSubscription<String> _subscription;
@override
void initState()
_subscription = server.messages.listen((message) async => setState(()
_messages.add(message);
));
super.initState();
@override
void dispose()
_subscription.cancel();
super.dispose();
@override
Widget build(BuildContext context)
TextStyle textStyle = Theme.of(context).textTheme.display2;
return new Scaffold(
appBar: new AppBar(
title: new Text('Sample App'),
),
body: new ListView(
children: _messages.map((String message)
return new Card(
child: new Container(
height: 100.0,
child: new Center(
child: new Text(message, style: textStyle),
),
),
);
).toList(),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
new FloatingActionButton(
child: new Icon(Icons.account_circle_outlined),
onPressed: ()
// simulate a message arriving
server.simulateMessage('Hello World');
,
),
SizedBox(
height: 20.0,
),
new FloatingActionButton(
child: new Icon(Icons.account_circle_rounded),
onPressed: ()
// simulate a message arriving
server.simulateMessage('Hi Flutter');
,
),
],
),
);
class SampleApp extends StatelessWidget
@override
Widget build(BuildContext context)
return new MaterialApp(
home: new HomeScreen(),
);
void main()
runApp(new SampleApp());
这里有一些教程可以更好地参考:
https://www.youtube.com/watch?v=nQBpOIHE4eE https://www.youtube.com/watch?v=OTS-ap9_aXc【讨论】:
以上是关于Flutter:如何为 StreamBuilder 制作 http 流的主要内容,如果未能解决你的问题,请参考以下文章
FirebaseStorage + Flutter,streamBuilder?