Ошибка, связанная с провайдером флаттера: setState () или markNeedsBuild () вызывается во время сборки - PullRequest
0 голосов
/ 23 апреля 2020

У меня есть страница экрана, как показано ниже. Я продолжаю получать следующую ошибку на консоли.

Exception caught by foundation library ═══════════════════════════════════════════════
setState() or markNeedsBuild() called during build.

Если я закомментировал Provider.of<AppData>(context, listen: false).setAllStoreData(allStoreData); в следующем файле, ошибка исчезнет.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../share/style.dart';
import '../share/backend.dart';
import '../share/app_data.dart';


class ChooseStorePage extends StatefulWidget {
  ChooseStorePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _ChooseStorePageState createState() => _ChooseStorePageState();
}

class _ChooseStorePageState extends State<ChooseStorePage> {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: ChooseStoreInnerWidget(),
      ),
    );
  }
}

class ChooseStoreInnerWidget extends StatefulWidget {
  ChooseStoreInnerWidget({Key key}) : super(key: key);

  @override
  _ChooseStoreInnerWidgetState createState() => _ChooseStoreInnerWidgetState();
}


class _ChooseStoreInnerWidgetState extends State<ChooseStoreInnerWidget> {
  Future _allStoreDataFuture = getFile('stores.json');

  Widget build(BuildContext context) {
    return FutureBuilder<dynamic>(
      future: _allStoreDataFuture,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        List<Widget> children;

        if (snapshot.hasData) {
          var allStoreData = snapshot.data;
          Provider.of<AppData>(context, listen: false).setAllStoreData(allStoreData);

          print(snapshot.data.toString());

          children = [
            Text('Choose Store',
                textAlign: TextAlign.left,
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: largeTextSize,
                  color: blackColor,
                ))
          ];

        } else {
          children = <Widget>[
            Container(
              padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
              child: Center(
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(),
                    Text("  Loading"),
                  ],
                ),
              ),
            ),
          ];
        }
        return ListView(
          children: children,
        );
      },
    );
  }
}

Кроме того, мой класс данных провайдера выглядит следующим образом:

import 'package:uuid/uuid.dart';
import 'package:flutter/material.dart';

class AppData extends ChangeNotifier {
  String uid;
  String phoneNumber;
  String selectedStoreId;
  String selectedStoreLabel;

  dynamic cart;
  dynamic storeData;
  dynamic allStoreData;

  AppData() {
    resetCart();
  }

  void resetCart() {
    cart = {
      "uid": uid,
      "phoneNumber": phoneNumber,
      "lines": [],
      "coupon": null,
      "discount": [],
      "tips_percent": 0,
      "tips": 0.00,
      "totals": {'subtotal': 0.00,
                 'tax': 0.00,
                 'total': 0.00,
                 'total_qty': 0,},
      "dinning_option": null,
      "arrival_time": null,
      "special_request": null,
    };
    notifyListeners();
  }

  void setUID(val) {
    uid = val;
    notifyListeners();
  }

  void setPhoneNumber(val) {
    phoneNumber = val;
    notifyListeners();
  }

  void setSelectedStoreId(val) {
    selectedStoreId = val;
    notifyListeners();
  }

  void setSelectedStoreLabel(val) {
    selectedStoreLabel = val;
    notifyListeners();
  }

  void setStoreData(val) {
    storeData = val;
    notifyListeners();
  }

  void setAllStoreData(val) {
    allStoreData = val;
    notifyListeners();
  }

  String generateOrderId() {
    String storeId = storeData['store_id'];
    var ms = (new DateTime.now()).millisecondsSinceEpoch;
    String epoch = (ms / 1000).round().toString();
    String phonePostfix = phoneNumber.substring(phoneNumber.length - 5);

    String orderId = "$storeId-$epoch-$phonePostfix";
    cart['order_id'] = orderId;

    // notifyListeners();
    return orderId;
  }

  void prepareCheckout() {
    double epoch =
        ((new DateTime.now()).millisecondsSinceEpoch / 1000).round().toDouble();

    DateTime today = new DateTime.now();
    String dateTimeSlug =
        "${today.year.toString()}-${today.month.toString().padLeft(2, '0')}-${today.day.toString().padLeft(2, '0')} ${today.hour.toString().padLeft(2, '0')}:${today.minute.toString().padLeft(2, '0')}:${today.second.toString().padLeft(2, '0')}";

    cart['phone_number'] = phoneNumber;
    cart['uid'] = uid;
    cart['negative_epoch'] = epoch * -1;
    cart['date_slug'] = dateTimeSlug;
    // notifyListeners();
  }

  void addCartItem(val) {
    dynamic line = new Map.from(val);
    cart['lines'].add(line);
    _recalculateCart();
  }

  void removeCartItem(index) {
    cart['lines'].removeAt(index);
    _recalculateCart();
  }

  void addCartTip(val) {
    cart['tips_percent'] = val['rate'];
    _recalculateCart();
  }

  void updateQty(index, qty) {
    cart['lines'][index]['_user']['_qty'] = qty;
    _recalculateCart();
  }

  void addCartCoupon(val) {
    val = val.toUpperCase();
    cart['coupon'] = val;
    _recalculateCart();
  }

  List<dynamic> __processCoupon() {
    String couponCode = cart['coupon'];
    dynamic couponData;

    if (storeData['coupon'].containsKey(couponCode)) {
      couponData = storeData['coupon'][couponCode];
    } else {
      return null;
    }

    // check valid date
    DateTime untilDate = DateTime.parse(couponData['until']);
    DateTime today = DateTime.now();

    if (today.compareTo(untilDate) == 1) {
      return null;
    }

    // check criteria
    String criteriaUnit = couponData['criteria_unit'];
    double criteriaMetric = couponData['criteria_metric'];
    List<dynamic> criteriaItems = couponData['criteria_items'];

    double userMetric = 0;
    List<dynamic> discountableLines = [];

    if (criteriaUnit == 'item') {
      for (dynamic line in cart['lines']) {
        if ((criteriaItems != null && criteriaItems.contains(line['course'])) ||
            (criteriaItems == null)) {
          userMetric = userMetric + line['_user']['_qty'];
          discountableLines.add(new Map.from(line));
        }
      }


    } else if (criteriaUnit == 'dollar') {
      for (dynamic line in cart['lines']) {
        if ((criteriaItems != null && criteriaItems.contains(line['course'])) ||
            (criteriaItems == null)) {
          userMetric = userMetric + line['_user']['_qty'] * line['price'];
          discountableLines.add(new Map.from(line));
        }
      }
    }

    if (!(userMetric >= criteriaMetric)) {
      return null;
    }

    // redeem
    String discountUnit = couponData['discount_unit'];
    double discountMetric = couponData['discount_metric'];
    List<dynamic> results = [];

    if (discountUnit == 'item') {
      // choose the lowest course from item
      int discountMulti = 1;
      if (couponData['discount_multi'] == true) {
        discountMulti = userMetric ~/ criteriaMetric;
        discountMulti = (discountMetric * discountMulti).toInt();
      }
      List<dynamic> flatDiscountableLines =
          __flatDiscountableLines(discountableLines);

      for (int i = 1; i <= discountMulti; i++) {
        dynamic discountLine = flatDiscountableLines[i];
        dynamic finalLine = new Map.from(discountLine);
        finalLine['price'] = finalLine['price'] * -1;
        results.add(finalLine);
      }
    } else if (discountUnit == 'dollar') {
      dynamic finalLine = {'item': 'Discount', 'course': discountMetric * -1};
      results.add(finalLine);
    } else if (discountUnit == 'percent') {
      dynamic finalLine = {
        'item': 'Discount',
        'course': discountMetric * userMetric * -1
      };
      results.add(finalLine);
    }
    return results;
  }

  List<dynamic> __flatDiscountableLines(discountableLines) {
    List<dynamic> flatList = [];
    for (var line in discountableLines) {
      for (int i = 0; i <= line['_user']['_qty']; i++) {
        dynamic flat = {'course': line['course'], 'price': line['price']};
        flatList.add(flat);
      }
    }

    flatList.sort((a, b) {
      if (a['price'] > b['price']) {
        return 1;
      }
      return -1;
    });
    return flatList;
  }

  void _recalculateCart() {
    // coupon
    if (cart['coupon'] != null) {
      cart['discount'] = __processCoupon();
    }

    if (cart['discount'] == null){
      cart['discount'] = [];
    }


    //line total
    double subTotal = 0.00;
    int totalQty = 0;
    for (dynamic item in cart['lines']) {
      int _qty = item['_user']['_qty'];
      double _price = item['price'];
      double _lineTotal = _price * _qty;
      item['_user']['_line_total'] = _lineTotal;
      subTotal = subTotal + _lineTotal;

      totalQty = totalQty + _qty;
    }

    //tax
    double taxRate = storeData['tax']['rate'];
    double tax = taxRate * subTotal;
    tax = double.parse(tax.toStringAsFixed(2));

    //subtotal
    for (dynamic row in cart['discount']) {
      subTotal = subTotal + row['price'];
    }
    subTotal = double.parse(subTotal.toStringAsFixed(2));

    //tips
    double tips = cart['tips_percent'] * subTotal;
    tips = double.parse(tips.toStringAsFixed(2));

    //total
    double total = subTotal + tax + cart['tips'];
    total = double.parse(total.toStringAsFixed(2));

    cart['tips'] = tips;
    cart['totals'] = {
      'subtotal': subTotal,
      'tax': tax,
      'total': total,
      'total_qty': totalQty,
    };

    notifyListeners();
  }
}

...