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 CircularProgressIndicator
或 ListView.builder
一旦 isLoading
更新为 false。
您仍会在 initState
中调用该函数,但 class 中的局部布尔值将消失。
我正在尝试在进行 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 CircularProgressIndicator
或 ListView.builder
一旦 isLoading
更新为 false。
您仍会在 initState
中调用该函数,但 class 中的局部布尔值将消失。