Flutter系列07: 加密貨幣錢包(第一部分)

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

image.png

開始

創(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ū)域

image.png

仔細(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)行如下


image.png

在上部代碼中增加新的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組件,RowmainAxisAlignment設(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)用


image.png

產(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)行封裝

image.png

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)用


image.png

四個(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)用


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容