本文制作一個(gè)加密貨幣錢包,設(shè)計(jì)來(lái)源于Pinterest。文章分為兩部分,第一部分集中講設(shè)計(jì),后一部分寫如何從真實(shí)后臺(tái)的數(shù)據(jù)流獲取數(shù)據(jù)。

開始
創(chuàng)建項(xiàng)目并切換目錄
flutter create crypto_wallet
cd create crypto_wallet
打開main.dart文件,清空全部?jī)?nèi)容后增加以下代碼:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Crypto Wallet',
home: CryptoWallet(),
);
}
}
創(chuàng)建CryptoWallet組件。設(shè)計(jì)包含有四個(gè)區(qū)域:上部使用gradient漸變背景、文本和圖標(biāo);底部采用灰色背景,一組產(chǎn)品(portfolio)項(xiàng)和四個(gè)按鈕stack棧。CryptoWallet返回Scaffold組件,代碼如下:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Crypto Wallet',
home: CryptoWallet(),
);
}
}
class CryptoWallet extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
使用Stack組件修改代碼如下:
return Scaffold(
body: SingleChildScrollView(
child: Stack(
children: <Widget>[],
),
),
);
SingleChildScrollView組件確保子組件Stack在界面變化(如變?。r(shí)可以滾動(dòng)。
上部區(qū)域

仔細(xì)看設(shè)計(jì)圖,上部分和下部分放入Column組件中的兩個(gè)指定高度的container容器中。
上部分有g(shù)raident漸變色。Flutter中,container容器組件使用decoration屬性來(lái)定義gradient漸變、shape形狀、color顏色、border邊框、blend渲染等。
對(duì)于上面部分的container容器,我們使用linear gradient線性漸變,指定中左開始到中右結(jié)束點(diǎn)的顏色。然后增加必要的外部間距padding:EdgeInsets.only(top:55,left:20,right:20),設(shè)置Container容器高度MediaQuery.of(context).size.height*0.40(屏幕高度的40%)。
底部區(qū)域
底部區(qū)域不能直接設(shè)置合理的高度和顏色,代碼修改如下:
class CryptoWallet extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Stack(
children: [
Column(
children: [
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFF81269D),
const Color(0xFFEE112D)
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
height: MediaQuery.of(context).size.height * .40,
padding: EdgeInsets.only(top: 55, left: 20, right: 20),
),
Container(
height: MediaQuery.of(context).size.height * .75,
color: Colors.grey,
),
],
),
],
),
));
}
}
代碼運(yùn)行如下

在上部代碼中增加新的Column子組件。在此之前導(dǎo)入font awesome用來(lái)引入需要的圖標(biāo)。打開pubsec.yaml導(dǎo)入以下依賴:
font_awesome_flutter: ^8.2.0
在main.dart里導(dǎo)入
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
Column的最上面組件為Row組件,Row的mainAxisAlignment設(shè)置為spaceBetween,children的三個(gè)子組件:兩個(gè)圖標(biāo)和一個(gè)文本。實(shí)現(xiàn)用兩個(gè)SizedBox組件間隔兩個(gè)文件組件,代碼修改如下:
child: Column(children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
Icons.menu,
color: Colors.white,
),
onPressed: () {},
),
Text("投資組合 (24H)",
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w500)),
IconButton(
icon: Icon(
FontAwesomeIcons.bell,
color: Colors.white,
),
onPressed: () {},
),
],
),
SizedBox(height: 40),
Text("43,729.00",
style: TextStyle(
color: Colors.white,
fontSize: 45.0,
fontWeight: FontWeight.bold)),
SizedBox(height: 20),
Text(
r"+ $3,157.67 (23%)",
style: TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.w300),
),
])
保存并刷新應(yīng)用

產(chǎn)品(portfolio)項(xiàng)區(qū)域
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFF81269D),
const Color(0xFFEE112D)
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
height: MediaQuery.of(context).size.height * .40,
padding: EdgeInsets.only(top: 55, left: 20, right: 20),
),
制作方法cryptoPortfolioItem()進(jìn)行封裝

card的子組件使用InkWell利用onTap屬性進(jìn)行產(chǎn)品的觸發(fā)事件。InkWell子組件使用container容器設(shè)置padding外間距,color顏色和border邊框的radius弧度后加入child子組件。
class cryptoPortfolioItem extends StatelessWidget {
IconData icon;
String name;
double amount;
double rate;
String percentage;
cryptoPortfolioItem(
this.icon, this.name, this.amount, this.rate, this.percentage);
@override
Widget build(BuildContext context) {
return Card(
elevation: 1.0,
child: InkWell(
onTap: () => print("tapped"),
child: Container(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0, right: 15.0),
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22.0)),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 10.0, right: 15.0),
child: Icon(icon, color: Colors.grey, size:40)),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(name, style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),),
Text("\$$amount", style: TextStyle(fontSize: 18.0,fontWeight: FontWeight.bold))
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("$rate BTC", style: TextStyle(fontSize: 15.0,
fontWeight: FontWeight.normal)),
Text("+ $percentage", style: TextStyle(
fontSize: 16.0,
color: Colors.red[500],
))
],
)
],
),
flex: 3,
)
],
),
)));
}
}
調(diào)用該方法
cryptoPortfolioItem(FontAwesomeIcons.btc, "BTC", 410.80,0.0036, "82.19(92%)"),
在container容器中增加ListView組件顯示cryptoPortfolioItem()。
Container(
alignment: Alignment.topCenter,
padding: EdgeInsets.only(
top: MediaQuery.of(context).size.height * .30,
right: 10.0,
left: 10.0),
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: ListView(
children: [
cryptoPortfolioItem(FontAwesomeIcons.btc, "BTC", 410.80,
0.0036, "82.19(92%)"),
cryptoPortfolioItem(FontAwesomeIcons.ethereum, "ETH", 1089.86,
126.0, "13.10(2.3%)"),
cryptoPortfolioItem(FontAwesomeIcons.xRay, "XRP", 22998.13,
23000, "120(3.6%)"),
cryptoPortfolioItem(FontAwesomeIcons.btc, "BTC", 410.80,
0.0036, "82.19(92%)"),
cryptoPortfolioItem(FontAwesomeIcons.ethereum, "ETH", 1089.86,
126.0, "13.10(2.3%)"),
cryptoPortfolioItem(FontAwesomeIcons.xRay, "XRP", 22998.13,
23000, "120(3.6%)"),
cryptoPortfolioItem(FontAwesomeIcons.btc, "BTC", 410.80,
0.0036, "82.19(92%)"),
cryptoPortfolioItem(FontAwesomeIcons.ethereum, "ETH", 1089.86,
126.0, "13.10(2.3%)"),
cryptoPortfolioItem(FontAwesomeIcons.xRay, "XRP", 22998.13,
23000, "120(3.6%)"),
],
),
),
),
保存并刷新應(yīng)用

四個(gè)按鈕區(qū)域
使用Positioned組件制作四個(gè)按鈕,代碼如下:
Positioned(
bottom: MediaQuery.of(context).size.height * .37,
left: MediaQuery.of(context).size.width * .05,
child: RaisedButton(
padding: EdgeInsets.symmetric(
vertical: 18.0,
horizontal: 38.0,
),
color: Color(0xFFEE112D),
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0)
),
child: Text(
"發(fā)送",
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
fontWeight: FontWeight.bold,
)
)
),
),
Positioned(
bottom: MediaQuery.of(context).size.height * .37,
right: MediaQuery.of(context).size.width * .05,
child: RaisedButton(
padding: EdgeInsets.symmetric(
vertical: 18.0,
horizontal: 30.0,
),
color: Color(0xFFEE112D),
onPressed: () {},
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(25.0)),
child: Text(
"接收",
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
)),
),
Positioned(
bottom: MediaQuery.of(context).size.height * .43,
left: MediaQuery.of(context).size.width * .30,
child: RaisedButton(
padding: EdgeInsets.symmetric(
vertical: 18.0,
horizontal: 38.0,
),
color: Color(0xFFEE112D),
onPressed: () {},
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(25.0)),
child: Text(
"交易",
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
)),
),
Positioned(
bottom: MediaQuery.of(context).size.height * .33,
left: MediaQuery.of(context).size.width * .40,
child: FloatingActionButton(
backgroundColor: Colors.white,
onPressed: null,
child: Icon(
Icons.close,
color: Colors.black,
),
),
),
保存并刷新應(yīng)用
