I have a Future function that accepts currencies. This function is in a separate file. On the main page, I accept it through the Future builder and expand the data. How can I update them dynamically with Stream? I need to update this data every 5 seconds. My Future func:
//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');
}
}
My 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),
),
);
}
CodePudding user response:
Use streamBuilder
with 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}');
}
},
),
CodePudding user response:
Here's a simple example of how to use a Stream and a
StreamBuilderwhile
polling` data from an API:
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');
}
},
);
}
}