現在手機主要功能就是拿來上網,所以任何 App 基本上都會需要透過網路存取資料,此時 Flutter 可以怎麼操作呢?使用內建 io package 就可搞定!🙃
我特地寫一個近年來很夯的比特幣查價系統,可以隨時查看多家交易平台上比特幣的實價,也許還可以進一步變成「搬磚」工具!
只要點擊藍色按鈕,就能即時取得多家交易所比特幣的實價。
剛好看到大神介紹開源項目,裡頭使用到 CryptoWatch 的開放 API,有些 API 就算不註冊也能使用,若註冊後會得到 API Key,便可無限量呼叫 API。目前我僅需要查詢交易所比特幣實價,先以這個簡單例子來實作。
/*
Author: HappyMan
Date: 2022/02/24
Topic: HttpClient
*/
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> marketPriceList = [];
List<double> priceList = [];
dynamic apiResponse;
void callGetPriceAPI() {
marketPriceList = [];
priceList = [];
// https://docs.cryptowat.ch/rest-api/
_getPrice('https://api.cryptowat.ch/markets/coinbase-pro/btcusdt/price', 'Coinbase');
_getPrice('https://api.cryptowat.ch/markets/binance/btcusdt/price', 'Binance');
_getPrice('https://api.cryptowat.ch/markets/binance-us/btcusdt/price', 'Binance.US');
_getPrice('https://api.cryptowat.ch/markets/huobi/btcusdt/price', 'Huobi');
_getPrice('https://api.cryptowat.ch/markets/gateio/btcusdt/price', 'GateIO');
_getPrice('https://api.cryptowat.ch/markets/okcoin/btcusdt/price', 'Okcoin');
_getPrice('https://api.cryptowat.ch/markets/ftx/btcusdt/price', 'FTX');
_getPrice('https://api.cryptowat.ch/markets/liquid/btcusdt/price', 'Liquid');
_getPrice('https://api.cryptowat.ch/markets/okex/btcusdt/price', 'Okex');
_getPrice('https://api.cryptowat.ch/markets/kraken/btcusdt/price', 'Kraken');
_getPrice('https://api.cryptowat.ch/markets/cexio/btcusdt/price', 'CEX.IO');
_getPrice('https://api.cryptowat.ch/markets/bitfinex/btcusdt/price', 'Bitfinex');
}
_getPrice(String url, String market) async {
var httpClient = HttpClient();
String result;
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
var data = jsonDecode(json);
result = market + ': ' + data['result']['price'].toString();
apiResponse = data;
print('apiResponse');
print(apiResponse);
} else {
result = 'Error getting Bitcoin price:\nHttp status ${response.statusCode}';
}
} catch (exception) {
result = 'Failed getting Bitcoin price';
print('exception');
print(exception);
}
setState(() {
marketPriceList.add(result);
// 解析取得價格
var parts = result.split(' ');// Ex: "Binance: 37777.1"
if (parts.length > 1) {
priceList.add(double.parse(parts[1]));
}
print('priceList');
print(priceList);
});
}
@override
Widget build(BuildContext context) {
SizedBox spacer = SizedBox(height: 10.0);
DateTime dateTime = DateTime.now();
// 取得幣價最小值
double _getMin(){
if (priceList.length == 0)
return 0;
double min = priceList.first;
for (int i = 1; i < priceList.length; i++) {
if (min > priceList[i]) {
min = priceList[i];
}
}
return min;
}
// 取得幣價最大值
double _getMax(){
if (priceList.length == 0)
return 0;
double max = priceList.first;
for (int i = 1; i < priceList.length; i++) {
if (max < priceList[i]) {
max = priceList[i];
}
}
return max;
}
// 取得幣價最小值的市場
String _getMinMarket(){
double min = _getMin();
for (int i = 1; i < marketPriceList.length; i++) {
if (marketPriceList[i].contains(min.toString())) {
// 解析取得市場
var parts = marketPriceList[i].split(':');// Ex: "Binance: 37777.1"
if (parts.length > 1) {
return parts[0];
}
}
}
return 'None';
}
// 取得幣價最大值的市場
String _getMaxMarket(){
double max = _getMax();
for (int i = 1; i < marketPriceList.length; i++) {
if (marketPriceList[i].contains(max.toString())) {
// 解析取得市場
var parts = marketPriceList[i].split(':');// Ex: "Binance: 37777.1"
if (parts.length > 1) {
return parts[0];
}
}
}
return 'None';
}
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Your current Bitcoin price is:'),
spacer,
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
border: Border.all(
color: Colors.grey,
width: 3,
),
color: Colors.black12,
),
margin: EdgeInsets.all(50),
height: 250,
width: 200,
child: Column(
children: [
for (int i = 0; i < marketPriceList.length; i++)
Text(marketPriceList[i]),
],
),
),
Text('Max: ' + _getMax().toString() + ' ' + _getMaxMarket(),
style: TextStyle(
color: Colors.redAccent,
),
),
Text('Min: ' + _getMin().toString() + ' ' + _getMinMarket(),
style: TextStyle(
color: Colors.blueAccent,
),
),
spacer,
Text('Time: ' + dateTime.toString().substring(0,19)),
spacer,
ElevatedButton(
onPressed: callGetPriceAPI,
child: Text('Get Bitcoin price'),
),
spacer,
Container(
margin: EdgeInsets.all(50),
child: Text(
apiResponse!=null?apiResponse.toString():'',
style: TextStyle(
fontSize: 12,
color: Colors.black45,
),
),
),
],
),
),
);
}
}
目前程式碼中我使用 12 家交易平台的數據,每次呼叫都會扣「零用錢」,若切換不同的 IP,則零用錢會再復原,現階段免費夠用就好~
以 Binance 為例:
https://api.cryptowat.ch/markets/binance/btcusdt/price
得到回傳:
// 20220224142639
// https://api.cryptowat.ch/markets/binance/btcusdt/price
{
"result": {
"price": 34859.04
},
"allowance": {
"cost": 0.005,
"remaining": 9.66,
"upgrade": "For unlimited API access, create an account at https://cryptowat.ch"
}
}
再查一下各平台買賣手續費 % 數(通常 0.05%-0.2%),一買一賣後扣掉手續費還能賺錢的話,就可以考慮用程式來「搬磚」囉~🤑
寫此專案的時刻,發生俄羅斯大戰烏克蘭事件,比特幣下跌至 35000 美金以下,好奇「買點」在哪裡呢?
有興趣的人,可以參考原始碼,然後請不吝給我星星,謝謝~🤠
https://github.com/happymanx/Exchange
參考:
隨意留個言吧:)~