initState() for list item is not called when new item in list is added in list view dynamically

Viewed 379

I want to show orders list with all details in each list item including amount, cart items and timer. Each list view item is a StatefulWidget. The issue I am facing right now is that when new a order is placed it displays in listview but initState() for newly added item is not called and because of that issue my timer also not starts.

This is my code for list view items.

class CardWidget extends StatefulWidget {
  String tabText;
  Order order;
  int index;
  List<Map<String, dynamic>> orderStatus;
  Function(Order) removeOrder;
  Function(Order) updateOrder;
  int listIndex;
  Function(String) rejectOrder;

  CardWidget({
    this.tabText,
    this.order,
    this.index,
    this.orderStatus,
    this.removeOrder,
    this.updateOrder,
    this.listIndex,
    this.rejectOrder,
  });

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return CardWidgetState();
  }
}

class CardWidgetState extends State<CardWidget> {
  Duration countdownDuration;

  TextEditingController min = new TextEditingController();
  Duration duration = Duration();
  Timer timer;

  Color textColor;
  String minutes = '00';
  String seconds = '00';

  bool docPrinted = false;
  bool noPrinterFound = false;
  Color sectionColors;
  String rejectionMsg = '';
  bool statusUpdated = true;
  bool orderCompleted = false;

  @override
  void dispose() {
    super.dispose();
    min.dispose();
    if (timer != null) timer.cancel();
  }

  @override
  void initState() {
    super.initState();

    if (widget.order.currentStatusID == widget.order.nextStatusId) {
      orderCompleted = true;
    } else if (widget.order.orderFrom == 'mobile_app' ||
        widget.order.orderFrom == 'web') {
      if (widget.order.currentStatusTxt.toLowerCase() == 'placed') {
        DateTime currentTime = DateTime.now().subtract(Duration(hours: 5));

        DateTime orderTime = DateFormat("yyyy-MM-ddTHH:mm:ss")
            .parse(widget.order.dateTime)
            .add(Duration(minutes: 5));

        print(
            'ORDER CARD ${widget.order.id}       ${currentTime.difference(orderTime).inSeconds}');

        int minDiff = -currentTime.difference(orderTime).inSeconds;
        if (minDiff <= 300 && !minDiff.isNegative) {
          countdownDuration = Duration(seconds: minDiff);
          duration = countdownDuration;
          startTimer();
        }
      } else {
        if (timer != null) timer.cancel();
      }
    }
  }

  void startTimer() {
    timer = Timer.periodic(Duration(seconds: 1), (_) => addTime());
  }

  void addTime() {
    final sec = duration.inSeconds - 1;
    if (sec == 0) {
      timer?.cancel();
      rejectOrder();
    } else {
      duration = Duration(seconds: sec);
      String twoDigits(int n) => n.toString().padLeft(2, '0');

      setState(() {
        minutes = twoDigits(duration.inMinutes.remainder(60));
        seconds = twoDigits(duration.inSeconds.remainder(60));
      });
    }
  }
}

This is my code for listview.

updateOrderList(List<Order> newList)
  {
    setState(() {
      newordersList=List.from(newList);
    });
  }


  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      child: newordersList.length != 0
          ? Stack(
              fit: StackFit.expand,
              children: [
                Positioned(
                  right: 12.7,
                  top: 0,
                  bottom: 0,
                  child: Container(
                    width: 10,
                    decoration: BoxDecoration(
                      color: Colors.grey[300],
                      borderRadius: BorderRadius.circular(20),
                      border: Border.all(color: Colors.grey[300], width: 2),
                    ),
                  ),
                ),
                Positioned(
                  right: 7.5,
                  top: 5,
                  bottom: 5,
                  left: 0,
                  child: DraggableScrollbar.arrows(
                    controller: scrollController,
                    alwaysVisibleScrollThumb: true,
                    backgroundColor: Theme.of(context).focusColor,
                    heightScrollThumb: 150,
                    child: StaggeredGridView.countBuilder(
                      crossAxisCount: 3,
                      padding: EdgeInsets.only(
                          left: 20, top: 15, bottom: 15, right: 30),
                      itemCount: newordersList.length,
                      controller: scrollController,
                      itemBuilder: (BuildContext context, int index) =>
                          CardWidget(
                        tabText: 'new_orders',
                        order: newordersList[index],
                        index: widget.tabIndex,
                        removeOrder: (order) {
                          setState(() {
                            newordersList.remove(order);
                          });
                        },
                        updateOrder: (order) {
                          updateOrder(order, index);
                        },
                        listIndex: index,
                        rejectOrder: (orderID) {
                          rejectOrder(orderID);
                        },
                      ),
                      staggeredTileBuilder: (int index) =>
                          new StaggeredTile.fit(1),
                      mainAxisSpacing: 15.0,
                      crossAxisSpacing: 15.0,
                    ),
                  ),
                ),
              ],
            )
          : Center(
              child: Text(
                'No Record found',
                style: GoogleFonts.roboto(
                  textStyle: Theme.of(context).textTheme.subhead.merge(
                        TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 15,
                        ),
                      ),
                ),
              ),
            ),
    );
  }
  

And this is my code in which I am adding new item to list.I am using GlobalKey() for list view and calling the function:

orderListKey.currentState.updateOrderList(mobileOrders)

of listview widget to update list.

updateForNewOrder(Order order) {
    if (order.orderFrom == 'mobile_app' || order.orderFrom == 'web') {
      mobileOrders.add(order);
      mobileOrders.sort((a, b) => b.id.compareTo(a.id));
      setState(() {
        mobileOrderCount = mobileOrders.length;
        tabs[2]['count'] = mobileOrderCount;
        if (currentTab != 2) {
          tabs[2]['animate'] = true;
          globals.animatedTabs[2] = true;
        }
      });

      globals.mobileOrders = mobileOrders;
      orderListKey.currentState.updateOrderList(mobileOrders);
    }
}


Widget orderView() {
    return Container(
      child: TabBarView(
        controller: _tabController,
        children: <Widget>[
          KioskTabView(
            key: orderListKey,
            neworders: mobileOrders,
            completedorders: [],
            tabIndex: 2,
            rejectOrder: (orderid) {
              rejectOrder(orderid);
            },
          ), ////////////MOBILE ORDERS
        ],
      ),
    );
  }

Anyone please help me out to solve this issue. Thanks

1 Answers

I have the same similar problem. I have listviewbuilder and the item has Statefulwidget with a checkbox. Inside that component has initstate() and setstate() when checkbox changed. I have solved this problem by changing stateful to stateless and when the checkbox has changed. The item will do a callback to the parent (where listviewbuilder exists).

Related