iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 23
0
自我挑戰組

使用flutter建構Android和iOs APP系列 第 25

將商品清單串到firebase資料庫

目標
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;
        });
    }
  1. pages/product_edit.dart
    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,
                );
        },
    );
    }
}


上一篇
Scoped Model,產生商品類別與使用者類別
系列文
使用flutter建構Android和iOs APP25

尚未有邦友留言

立即登入留言