API 调用完成后显示 CircularProgressIndicator() 不会停止

Displaying CircularProgressIndicator() will not stop after API call is completed

我正在尝试在进行 API 调用时显示 CircularProgressIndicator。当导航到 OrdersScreen 时,CircularProgressIndicator 会显示并且不会停止。

当点击错误时,它会将我引导至我的 API 调用中的 try{} catch{} 块中的 catch。

这是我遇到的错误:

I/flutter (22500): Invalid argument(s) (input): Must not be null
E/flutter (22500): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Invalid argument(s) (input): Must not be null
[38;5;248mE/flutter (22500): #0      Orders.getOrders[39;49m
E/flutter (22500): <asynchronous suspension>
[38;5;248mE/flutter (22500): #1      _OrdersScreenState.initState.<anonymous closure> (package:shop_app/screens/order_screen.dart)[39;49m
E/flutter (22500): <asynchronous suspension>
E/flutter (22500):

这是我的 API 电话:

class Orders with ChangeNotifier {
  List<OrderItem> _orders = [];

  List<OrderItem> get orders {
    return [..._orders];
  }
  //make a copy of private class _orders
  //establishing so that we cannot modify the private class

//READ API call
  Future<void> getOrders() async {
    final url = Uri.https(
        'shop-app-flutter-49ad1-default-rtdb.firebaseio.com', '/products.json');
    //note that for the post URL when using this https package we had to remove the special characters (https://) in order to properly post via the API
    //establish the URL where the API call will be made
    try {
      final response = await http.get(url);
      // print(json.decode(response.body));
      final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
      //retrieve the json response data stored in firebase, translate to a Map, and store that map in the jsonResponse variable
      if (jsonResponse == null) {
        return;
      }
      //if there is no data returned in the jsonResponse (the db is empty) then we do nothing, avoiding an app crash on an empty API call
      final List<OrderItem> orderProducts = [];
      //establish an empty list in preparation to store the new Order values retrieved from the API call
      jsonResponse.forEach((orderID, orderData) {
        //forEach will exectue a function on every value that is housed within that Map
        orderProducts.insert(
            0, //insert at index 0 inserts the newest added product at the beginning of the list
            OrderItem(
              id: orderID,
              amount: orderData['amount'],
              dateTime: DateTime.parse(orderData['dateTime']),
              products: (orderData['products'] as List<dynamic>)
                  .map(
                    (item) => CartItem(
                      id: item['id'],
                      title: item['title'],
                      quantity: item['quantity'],
                      price: item['price'],
                    ),
                  )
                  .toList(),
              //since products is stored on the db as a map, we have to retrieve those values and define how the properties of the items stored in the db should be mapped --> recreating our CartItem as it's stored in the db
            ));
        //retrieve the values for each of the given properties and Map them according to the values stored on the server
      });
      _orders = orderProducts;
      notifyListeners();
      //set the value of the _items list - that is the primary data of the ProductsProvider to tell the different areas of the app the data to show - equal to the values retrieved from the API call
    } catch (error) {
      print(error);
      throw (error);
    }
  }
}

带有 CircularProgressIndicator 的代码:

class OrdersScreen extends StatefulWidget {
  static const routeName = '/orders';

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

class _OrdersScreenState extends State<OrdersScreen> {
  bool _isLoading = false;

  @override
  void initState() {
    setState(() {
      _isLoading = true;
    });
    // when the state of the screen is initialized set the value of _isLoading to true
    // by setting _isLoading to true we are establishing another state while the API call is being made
    Provider.of<Orders>(context, listen: false).getOrders().then((_) {
      setState(() {
        _isLoading = false;
      });
    });
    // we are making the API call and then setting the state of _isLoading back to false indicating the change of the _isLoading variable means a completed API call
    // --> by changing the value of _isLoading prior to and after the API call it allows us to put additional functionality while the API call is made --> we established a CircularProgressIndicator which may be found in the body
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final orderData = Provider.of<Orders>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Your Order'),
      ),
      body: _isLoading == true
          ? Center(
              child: CircularProgressIndicator(
                  backgroundColor: Theme.of(context).primaryColor),
            )
          : ListView.builder(
              itemCount: orderData.orders.length,
              itemBuilder: (ctx, index) => OrderCard(
                order: orderData.orders[index],
              ),
              //populate the order card UI element with data provided by the orders method within orders.dart
              //this data is retrieved by calling the provider of type orders
            ),
      drawer: SideDrawer(),
    );
  }
}

供参考:

订单项:

class OrderItem {
  OrderItem({
    @required this.id,
    @required this.amount,
    @required this.products,
    @required this.dateTime,
  });
  final String id;
  final double amount;
  final List<CartItem> products; //CartItem from cart.dart
  final DateTime dateTime;
}

购物车项目:

class CartItem {
  CartItem({
    @required this.id,
    @required this.title,
    @required this.quantity,
    @required this.price,
  });

  final String id;
  final String title;
  final int quantity;
  final double price;
}

要充分利用您已经设置的 Provider,您应该将脚手架的主体设为 Consumer<Orders> 小部件。在内部保留相同的逻辑,但它需要基于 Orders class.

中的布尔值(初始化为真)
Consumer<Orders>(builder: (context, orderData, child) {
      return orderData.isLoading == true
          ? Center(
              child: CircularProgressIndicator(
                  backgroundColor: Theme.of(context).primaryColor),
            )
          : ListView.builder(
              itemCount: orderData.orders.length,
              itemBuilder: (ctx, index) => OrderCard(
                order: orderData.orders[index],
              ),
              //populate the order card UI element with data provided by the orders method within orders.dart
              //this data is retrieved by calling the provider of type orders
            );
    });

在您的 getOrders() 函数中处理 isLoading 的值,这将通知 Consumer<Orders> 小部件 return a CircularProgressIndicatorListView.builder 一旦 isLoading 更新为 false。

您仍会在 initState 中调用该函数,但 class 中的局部布尔值将消失。