StreamBuilder not reacting to items getting added to StreamController.sink

Viewed 22

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(),
                    ],
                  ),
                );
              }
          }
        },
      ),
    );
  }
}
1 Answers

use snapshot.hasdata and snapshot.hasError to check states.

check below template.

builder: (BuildContext context, AsyncSnapshot<T> snapshot) {
          if (snapshot.hasError) {
            return buildErrorWidget();
          } else {
            switch (snapshot.connectionState) {
              case ConnectionState.waiting:
                return buildLoadingWidget();
              case ConnectionState.active:
              case ConnectionState.done:
                if (!snapshot.hasData || (snapshot.data is List && (snapshot.data as List).isEmpty)) {
                  return buildNoDataWidget();
                } else {
                  return buildContentWidget(context, snapshot.data!);
                }
              case ConnectionState.none:
              default:
                return buildNoDataWidget();
            }
          }
        }



Related