I'm using a StreamBuilder with a StreamController, however nothing shows up, even though I'm periodically calling an API with a timer but the widget doesn't build. Adding await to the getOrders() in the timer didn't fix it. I'm following the documentation, and this is what I could come up with. Is there something I'm missing? Thanks in advance!
file:
import 'dart:async';
import 'dart:math';
import 'package:dineos_tools/api/api_service.dart';
import 'package:dineos_tools/models/serving_station/order.dart';
import 'package:dineos_tools/screens/serving_station_history_screen.dart';
import 'package:dineos_tools/widgets/navigation.dart';
import 'package:dineos_tools/widgets/order_stack.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:sizer/sizer.dart';
class ServingStationScreenStreamed extends StatefulWidget {
const ServingStationScreenStreamed({Key? key}) : super(key: key);
@override
ServingStationScreenState createState() => ServingStationScreenState();
}
class ServingStationScreenState extends State<ServingStationScreenStreamed> {
StreamController<Map<String, List<Order>>> streamController = StreamController();
Future<void> getOrders() async {
try {
var orders = (await APIService().getOrders())!;
streamController.sink.add({
'ordered': orders.where((order) {
return order.itemsOrdered!.isNotEmpty;
}).toList(),
'preparingAndAwaitingCancellation': orders.where((order) {
return order.itemsPreparingAndAwaitingCancellation!.isNotEmpty;
}).toList(),
});
} catch (e) {
setState(() {
streamController.sink.addError(e);
});
}
}
@override
void dispose() {
streamController.close();
super.dispose();
}
@override
void initState() {
super.initState();
Timer.periodic(const Duration(seconds: 1), (timer) {
getOrders();
});
}
final List<String> emptyImages = [
'assets/svg/barbecue.svg',
'assets/svg/beer.svg',
'assets/svg/breakfast.svg',
'assets/svg/cooking.svg',
'assets/svg/fresh-drink.svg',
'assets/svg/ice-cream.svg',
'assets/svg/mint-tea.svg',
'assets/svg/pizza.svg',
'assets/svg/special-event.svg',
'assets/svg/tea.svg',
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: Navigation(
allowBack: false,
activePage: 'Serving Station',
restaurantName: 'Test Restaurant',
actions: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: IconButton(
onPressed: () {
Get.to(() => const ServingStationHistoryScreen());
},
icon: const Icon(
Icons.history_rounded,
color: Colors.white,
size: 26.0,
),
),
),
],
),
extendBodyBehindAppBar: true,
backgroundColor: const Color.fromRGBO(21, 21, 32, 1),
body: StreamBuilder<Map<String, List<Order>>>(
stream: streamController.stream,
builder: (context, snapshot) {
switch(snapshot.connectionState) {
case ConnectionState.none:
print(snapshot.data);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.only(bottom: 100),
child: Opacity(
opacity: 0.5,
child: SvgPicture.asset(
emptyImages.elementAt(Random().nextInt(emptyImages.length)),
height: SizerUtil.deviceType == DeviceType.tablet ? 500 : 200,
theme: const SvgTheme(
currentColor: Color.fromRGBO(93, 194, 188, 1),
),
),
),
),
Text(
'There are currently no pending orders.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizerUtil.deviceType == DeviceType.tablet ? 30 : 18, color: Colors.white),
),
],
),
);
case ConnectionState.waiting:
case ConnectionState.active:
return const CircularProgressIndicator(
color: Color.fromRGBO(93, 194, 188, 1),
);
case ConnectionState.done:
if(snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.only(bottom: 100),
child: Opacity(
opacity: 0.5,
child: SvgPicture.asset(
'assets/svg/server.svg',
height: SizerUtil.deviceType == DeviceType.tablet ? 500 : 200,
theme: const SvgTheme(
currentColor: Color.fromRGBO(93, 194, 188, 1),
),
),
),
),
Text(
'Service Unreachable',
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizerUtil.deviceType == DeviceType.tablet ? 30 : 18, color: Colors.white),
),
Text(
'Error: ${snapshot.error}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizerUtil.deviceType == DeviceType.tablet ? 15 : 10, color: Colors.red),
),
],
),
);
} else {
final data = snapshot.data as Map<String, List<Order>>;
return SafeArea(
bottom: false,
child: ListView(
shrinkWrap: false,
children: [
data['preparingAndAwaitingCancellation']!.isNotEmpty ? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(top: 20, left: 20, bottom: 20),
child: Row(
children: [
const Text('Current Items',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
Container(
margin: const EdgeInsets.only(left: 10, right: 10),
padding: const EdgeInsets.only(left: 5, right: 5, top: 2, bottom: 2),
decoration: BoxDecoration(
color: Colors.yellow[200],
border: Border.all(
color: Colors.transparent,
),
borderRadius: const BorderRadius.all(Radius.circular(20))
),
child: const Text(
'Preparing',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Container(
padding: const EdgeInsets.only(left: 5, right: 5, top: 2, bottom: 2),
decoration: BoxDecoration(
color: Colors.red[200],
border: Border.all(
color: Colors.transparent,
),
borderRadius: const BorderRadius.all(Radius.circular(20))
),
child: const Text(
'Awaiting Cancellation',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
),
SingleChildScrollView(
restorationId: 'firstScroll',
scrollDirection: Axis.horizontal,
child: SizedBox(
width: data['preparingAndAwaitingCancellation']!.length * 500 < 2500 ? data['preparingAndAwaitingCancellation']!.length * 500 : 2500,
child: MasonryGridView.count(
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: data['preparingAndAwaitingCancellation']!.length < 5 ? data['preparingAndAwaitingCancellation']!.length : 5,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
shrinkWrap: true,
itemCount: data['preparingAndAwaitingCancellation']!.length,
padding: const EdgeInsets.only(left: 20, right: 20),
itemBuilder: (context, index) {
return OrderStack(
order: data['preparingAndAwaitingCancellation']![index],
filter: OrderStatusFilter.preparingAndAwaitingCancellation,
);
},
),
),
),
],
) : Container(),
data['ordered']!.isNotEmpty ? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(top: 20, left: 20, bottom: 20),
child: Row(
children: [
const Text('New Items',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
Container(
margin: const EdgeInsets.only(left: 10, right: 10),
padding: const EdgeInsets.only(left: 5, right: 5, top: 2, bottom: 2),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.transparent,
),
borderRadius: const BorderRadius.all(Radius.circular(20))
),
child: const Text(
'Ordered',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
),
SingleChildScrollView(
restorationId: 'secondScroll',
scrollDirection: Axis.horizontal,
child: SizedBox(
width: data['ordered']!.length * 500 < 2500 ? data['ordered']!.length * 500 : 2500,
child: MasonryGridView.count(
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: data['ordered']!.length < 5 ? data['ordered']!.length : 5,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
shrinkWrap: true,
itemCount: data['ordered']!.length,
padding: const EdgeInsets.only(left: 20, right: 20),
itemBuilder: (context, index) {
return OrderStack(
order: data['ordered']![index],
filter: OrderStatusFilter.ordered,
);
},
),
),
),
],
) : Container(),
],
),
);
}
}
},
),
);
}
}