Stream如何定时刷新数据?
How to refresh data periodically with Stream?
我有一个接受货币的 Future 函数。
这个函数在一个单独的文件中。在主页上,我通过 Future 构建器接受它并扩展数据。如何使用 Stream 动态更新它们?我需要每 5 秒更新一次这些数据。
我的未来功能:
//fetch data from API
Future<List<CurrencyModel>?> _fetchCurrency() async {
currencyList = [];
final response = await http.get(
Uri.parse(
'https:...'),
);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.isNotEmpty) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
currencyList.add(
CurrencyModel.fromJson(map),
);
}
}
}
return currencyList;
} else {
throw Exception('Failed to load currencies');
}
}
我的body:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Currencies'),
centerTitle: true,
),
body: FutureBuilder(
future: client.fetchCurrency(),
builder: (BuildContext context,
AsyncSnapshot<List<CurrencyModel>?> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: currencyList.length,
itemBuilder: (context, index) => CurrencyCard(
currencyList[index],
),
);
} else if (snapshot.hasError) {
return Text(
'${snapshot.error}',
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: startTimer,
child: const Icon(Icons.update_sharp),
),
);
}
将 streamBuilder
与 stream
结合使用
StreamController<List<CurrencyModel>> controller = StreamController<List<CurrencyModel>();
Timer? timer;
@override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 5), (Timer t) => fetchCurrency());
}
void fetchCurrency(){
//do your api call and add data to stream
currencyList = [];
final response = await http.get(
Uri.parse(
'https:...'),
);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.isNotEmpty) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
currencyList.add(
CurrencyModel.fromJson(map),
);
}
}
}
controller.add(currencyList);
} else {
throw Exception('Failed to load currencies');
}
}
@override
void dispose() {
timer?.cancel();
super.dispose();
}
// here is your stream builder
StreamBuilder<List<CurrencyModel>>(
stream: controller.stream,
builder: (
context,
AsyncSnapshot<List<CurrencyModel>> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.active
|| snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
// return your listview here
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) => CurrencyCard(
snapshot.data[index],
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
这是一个简单示例,说明如何使用来自 API:
的 Stream and a
StreamBuilderwhile
polling` 数据
class Issue72171944 extends StatefulWidget {
const Issue72171944({Key? key}) : super(key: key);
@override
State<Issue72171944> createState() => _Issue72171944State();
}
class _Issue72171944State extends State<Issue72171944> {
Uri url = Uri.parse('https://catfact.ninja/fact');
final StreamController<String> _streamController = StreamController<String>();
late Stream stream;
@override
void initState() {
super.initState();
stream = _streamController.stream;
startPolling();
}
void startPolling(){
Timer.periodic(const Duration(seconds: 5), (timer){
http.get(url).then((response){
if (response.statusCode == 200) {
String catFact = jsonDecode(response.body)['fact'];
_streamController.add(catFact);
}
});
});
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: stream,
builder: (context, snapshot){
if(snapshot.hasData){
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text(snapshot.data.toString(),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
);
} else{
return const Text('No data');
}
},
);
}
}
简单创建每五秒更新一次的流属性。
Stream<List<String>> get stream =>
Stream.periodic(
const Duration(seconds: 5),
(_) => http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'))
).asyncMap((r) async => await r)
.map((r) => json.decode(r.body))
.map((list) => list.map((item) => item['title']))
.map((result) => result.toList().cast<String>());
@override
Widget build(BuildContext context) {
return StreamBuilder<List<String>>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('${snapshot.error}');
} else if (snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data![index]);
}
);
} else {
return const CircularProgressIndicator();
}
},
);
}
我有一个接受货币的 Future 函数。 这个函数在一个单独的文件中。在主页上,我通过 Future 构建器接受它并扩展数据。如何使用 Stream 动态更新它们?我需要每 5 秒更新一次这些数据。 我的未来功能:
//fetch data from API
Future<List<CurrencyModel>?> _fetchCurrency() async {
currencyList = [];
final response = await http.get(
Uri.parse(
'https:...'),
);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.isNotEmpty) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
currencyList.add(
CurrencyModel.fromJson(map),
);
}
}
}
return currencyList;
} else {
throw Exception('Failed to load currencies');
}
}
我的body:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Currencies'),
centerTitle: true,
),
body: FutureBuilder(
future: client.fetchCurrency(),
builder: (BuildContext context,
AsyncSnapshot<List<CurrencyModel>?> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: currencyList.length,
itemBuilder: (context, index) => CurrencyCard(
currencyList[index],
),
);
} else if (snapshot.hasError) {
return Text(
'${snapshot.error}',
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: startTimer,
child: const Icon(Icons.update_sharp),
),
);
}
将 streamBuilder
与 stream
StreamController<List<CurrencyModel>> controller = StreamController<List<CurrencyModel>();
Timer? timer;
@override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 5), (Timer t) => fetchCurrency());
}
void fetchCurrency(){
//do your api call and add data to stream
currencyList = [];
final response = await http.get(
Uri.parse(
'https:...'),
);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.isNotEmpty) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
currencyList.add(
CurrencyModel.fromJson(map),
);
}
}
}
controller.add(currencyList);
} else {
throw Exception('Failed to load currencies');
}
}
@override
void dispose() {
timer?.cancel();
super.dispose();
}
// here is your stream builder
StreamBuilder<List<CurrencyModel>>(
stream: controller.stream,
builder: (
context,
AsyncSnapshot<List<CurrencyModel>> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.active
|| snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
// return your listview here
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) => CurrencyCard(
snapshot.data[index],
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
这是一个简单示例,说明如何使用来自 API:
的Stream and a
StreamBuilderwhile
polling` 数据
class Issue72171944 extends StatefulWidget {
const Issue72171944({Key? key}) : super(key: key);
@override
State<Issue72171944> createState() => _Issue72171944State();
}
class _Issue72171944State extends State<Issue72171944> {
Uri url = Uri.parse('https://catfact.ninja/fact');
final StreamController<String> _streamController = StreamController<String>();
late Stream stream;
@override
void initState() {
super.initState();
stream = _streamController.stream;
startPolling();
}
void startPolling(){
Timer.periodic(const Duration(seconds: 5), (timer){
http.get(url).then((response){
if (response.statusCode == 200) {
String catFact = jsonDecode(response.body)['fact'];
_streamController.add(catFact);
}
});
});
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: stream,
builder: (context, snapshot){
if(snapshot.hasData){
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text(snapshot.data.toString(),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
);
} else{
return const Text('No data');
}
},
);
}
}
简单创建每五秒更新一次的流属性。
Stream<List<String>> get stream =>
Stream.periodic(
const Duration(seconds: 5),
(_) => http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'))
).asyncMap((r) async => await r)
.map((r) => json.decode(r.body))
.map((list) => list.map((item) => item['title']))
.map((result) => result.toList().cast<String>());
@override
Widget build(BuildContext context) {
return StreamBuilder<List<String>>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('${snapshot.error}');
} else if (snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data![index]);
}
);
} else {
return const CircularProgressIndicator();
}
},
);
}