目標
1.讓商品清單是從資料庫拉出來的
2.拉的時候會loading
3.新增編輯刪除都可以影響資料庫
4.使用者在清單由上往下拉時,會重新抓資料庫
1.先去firebase新增一個專案
2.再新增一個realtime database
3.把規則改成
"rules": {
".read": true,
".write": true
}
4.複製網址
https://xxxxxxxxx.firebaseio.com/
5.pubsepc.yaml
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.0
scoped_model: "^0.3.0"
http: "^0.11.3+16"
scoped_model要升級才不會報錯
另外發現ensure_visible的github已經宣佈廢棄?!
所以先把focusNode相關的組件都先拿掉以免報錯
6.pages/products.dart
Widget _buildProductsList() {
return ScopedModelDescendant(
builder: (BuildContext context, Widget child, MainModel model) {
Widget content = Center(child: Text('No Products Found!'));
if (model.displayedProducts.length > 0 && !model.isLoading) {
//全域中已經存有資料,並且沒在loading
content = Products();
} else if (model.isLoading) {
//loading狀態時,顯示loading
content = Center(child: CircularProgressIndicator());
}
return RefreshIndicator(onRefresh: model.fetchProducts, child: content,) ;
//使用者可以往下拉進行重新抓資料
},
);
}
7.scoed-models/connected_products.dart
import 'dart:convert';
import 'dart:async';
import 'package:scoped_model/scoped_model.dart';
import 'package:http/http.dart' as http;
import '../models/product.dart';
import '../models/user.dart';
class ConnectedProductsModel extends Model {
List<Product> _products = [];
String _selProductId;
User _authenticatedUser;
bool _isLoading = false;
}
class ProductsModel extends ConnectedProductsModel {
...
Future<bool> addProduct(String title, String description, String image, double price) async {
//未來會是一個布林值
_isLoading = true;
notifyListeners();
final Map<String, dynamic> productData = {
'title': title,
'description': description,
'image':'某張巧克力圖片的URL',
'price': price,
'userEmail': _authenticatedUser.email,
'userId': _authenticatedUser.id
};
try {
//中間有失敗就跳到catch
final http.Response response = await http.post(
'https://xxxxxxxxx.firebaseio.com/products.json',
body: json.encode(productData)
);
//送你要新增的資料到firebase
if (response.statusCode != 200 && response.statusCode != 201) {
_isLoading = false;
notifyListeners();
return false;
}
final Map<String, dynamic> responseData = json.decode(response.body);
final Product newProduct = Product(
id: responseData['name'],
//把回傳的id拿來用
title: title,
description: description,
image: image,
price: price,
userEmail: _authenticatedUser.email,
userId: _authenticatedUser.id
);
_products.add(newProduct);
_isLoading = false;
notifyListeners();
return true;
} catch (error) {
_isLoading = false;
notifyListeners();
return false;
}
// .catchError((error) {
// _isLoading = false;
// notifyListeners();
// return false;
// });
}
Future<bool> updateProduct(String title, String description, String image, double price) {
_isLoading = true;
notifyListeners();
final Map<String, dynamic> updateData = {
'title': title,
'description': description,
'image':'某張巧克力圖片的URL',
'price': price,
'userEmail': selectedProduct.userEmail,
'userId': selectedProduct.userId
};
return http
.put(
'https://xxxxxxxxxx.firebaseio.com/products/${selectedProduct.id}.json',
//更新這個id的資料
body: json.encode(updateData)
).then((http.Response reponse) {
_isLoading = false;
final Product updatedProduct = Product(
id: selectedProduct.id,
title: title,
description: description,
image: image,
price: price,
userEmail: selectedProduct.userEmail,
userId: selectedProduct.userId);
_products[selectedProductIndex] = updatedProduct;
//更新到全域的變數中
notifyListeners();
return true;
}).catchError((error) {
_isLoading = false;
notifyListeners();
return false;
});
}
Future<bool> deleteProduct() {
_isLoading = true;
final deletedProductId = selectedProduct.id;
_products.removeAt(selectedProductIndex);
_selProductId = null;
notifyListeners();
return http
.delete('https://xxxxxxxxxx.firebaseio.com/products/${deletedProductId}.json')
//根據ID刪商品
.then((http.Response response) {
_isLoading = false;
notifyListeners();
return true;
}).catchError((error) {
_isLoading = false;
notifyListeners();
return false;
});
}
Future<Null> fetchProducts() {
_isLoading = true;
notifyListeners();
return http
.get('https://xxxxxxxxxx.firebaseio.com/products.json')
//抓到資料
.then<Null>((http.Response response) {
final List<Product> fetchedProductList = [];
final Map<String, dynamic> productListData = json.decode(response.body);
if (productListData == null) {
_isLoading = false;
notifyListeners();
return;
}
productListData.forEach((String productId, dynamic productData) {
//跑迴圈
final Product product = Product(
id: productId,
//索引值
title: productData['title'],
description: productData['description'],
image: productData['image'],
price: productData['price'],
userEmail: productData['userEmail'],
userId: productData['userId']);
fetchedProductList.add(product);
//加進去陣列中
});
_products = fetchedProductList;
_isLoading = false;
notifyListeners();
_selProductId = null;
}).catchError((error) {
_isLoading = false;
notifyListeners();
return;
});
}
Widget _buildSubmitButton() {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return model.isLoading
? Center(child: CircularProgressIndicator())
: RaisedButton(
child: Text('Save'),
textColor: Colors.white,
onPressed: () => _submitForm(
model.addProduct,
model.updateProduct,
model.selectProduct,
model.selectedProductIndex),
);
},
);
}
void _submitForm(Function addProduct, Function updateProduct, Function setSelectedProduct,[int selectedProductIndex]) {
...
if (selectedProductIndex == -1) {
//代表_products.indexWhere回傳一個-1
//也就是選到的商品不在變數product裡面
addProduct(
_formData['title'],
_formData['description'],
_formData['image'],
_formData['price'],
).then((bool success) {
if (success) {
Navigator
.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null));
//新增完後跳回列表
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Something went wrong'),
content: Text('Please try again!'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
//光箱消失
child: Text('Okay'),
)
],
);
});
}
});
} else {
updateProduct(
_formData['title'],
_formData['description'],
_formData['image'],
_formData['price'],
).then((_) => Navigator
.pushReplacementNamed(context, '/products')
//編輯完後跳回商品列表
.then((_) => setSelectedProduct(null))
//把選擇商品編號設回null
);
}
}
@override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
final Widget pageContent = _buildPageContent(context, model.selectedProduct);
//把product物件傳進去
return model.selectedProductIndex == -1
? pageContent
: Scaffold(
appBar: AppBar(
title: Text('Edit Product'),
),
body: pageContent,
);
},
);
}
}