Flutter新人實(shí)戰(zhàn)—從0開始開發(fā)一個(gè)DIY活動(dòng)記錄應(yīng)用(三)基礎(chǔ)UI控件繪制頁面

版權(quán)聲明:本文為本人原創(chuàng)文章,未經(jīng)本人允許不得轉(zhuǎn)載。
上篇文章我們基本介紹了如何分析布局一個(gè)應(yīng)用的整體框架,以及介紹了不帶數(shù)據(jù)傳遞的不同頁面之間的導(dǎo)航。
今天我們開始對(duì)頁面要展示的內(nèi)容進(jìn)行實(shí)現(xiàn),很顯然今天涉及的就是Flutter的各類控件了。

數(shù)據(jù)模型建立

在界面ui之前呢,我們先把活動(dòng)項(xiàng)目的數(shù)據(jù)屬性先建立起來,在后面的內(nèi)容我們會(huì)用到。
在lib目錄下新建文件夾model,用于存放數(shù)據(jù)模型,新建diy_project.dart文件:

image.png

完成數(shù)據(jù)屬性的說明:
diy_project.dart

/*
diy項(xiàng)目類,說明diy項(xiàng)目的對(duì)象屬性
*/

class DiyProject {
  int _id; //項(xiàng)目id
  String _name; //項(xiàng)目名字
  String _date; //項(xiàng)目時(shí)間
  String _place; //項(xiàng)目地點(diǎn)
  String _contact; //項(xiàng)目聯(lián)系人
  String _imagePath; //項(xiàng)目照片地址

  int _singlePrice; //項(xiàng)目單價(jià)
  int _nums; //項(xiàng)目份數(shù)
  int _totalAmount; //項(xiàng)目總價(jià)
  int _itemCost; //物料成本
  int _laborCost; //人員成本
  int _profit; //項(xiàng)目利潤

  bool _isCheckOut; //錢款是否結(jié)清

  //活動(dòng)項(xiàng)目構(gòu)造函數(shù)
  DiyProject(
    this._name,
    this._date,
    this._place,
    this._contact,
    this._imagePath,
    this._singlePrice,
    this._nums,
    this._totalAmount,
    this._itemCost,
    this._laborCost,
    this._profit,
    this._isCheckOut,
  );

  /*
  下面是獲取活動(dòng)項(xiàng)目各屬性值得方法
  */
  String get name => _name;
  String get date => _date;
  String get place => _place;
  String get contact => _contact;
  String get imagePath => _imagePath;

  int get id => _id;
  int get singlePrcie => _singlePrice;
  int get nums => _nums;
  int get totalAmount => _totalAmount;
  int get itemCost => _itemCost;
  int get laborCost => _laborCost;
  int get profit => _profit;
  bool get isCheckOut => _isCheckOut;
}

然后我們?cè)趆ome_page文件中初始化一組項(xiàng)目數(shù)據(jù),用于后面的數(shù)據(jù)展示,代碼如下:
home_page.dart

//初始化三個(gè)項(xiàng)目數(shù)據(jù)
  List<DiyProject> _diyProjects = [
    new DiyProject('多肉種植', '2018-11-2', '萬達(dá)廣場', '蘇蘇', 'images/4.jpg', 30, 50,
        1500, 500, 300, 700, false),
    new DiyProject('彩繪尤克里里', '2018-10-22', '寰宇城', '盼盼', 'images/2.jpg', 20, 30,
        600, 500, 500, 1500, false),
    new DiyProject('小餅干制作', '2018-9-15', '濱江新城', '磊磊', 'images/5.jpg', 40, 50,
        2000, 600, 200, 800, false),
  ];

UI繪制

數(shù)據(jù)模型屬性我們已經(jīng)準(zhǔn)備完畢,下面我們開始一步步實(shí)現(xiàn)首頁的展示效果
我們要實(shí)現(xiàn)的首頁展示效果如下:


首頁.PNG

簡單分析以后我們可以看得出首先這是一個(gè)可以滾動(dòng)的ListView,里面包含的是一個(gè)個(gè)的Card,Card里對(duì)應(yīng)的是項(xiàng)目活動(dòng)的屬性(展示照片、名稱、時(shí)間、地點(diǎn)等等)。這些屬性又是按照一定的方向進(jìn)行排列的,其實(shí)很多教程和文章里都有說到如何分析一個(gè)頁面的布局,那要點(diǎn)就是一個(gè)字:拆

我們對(duì)這個(gè)card進(jìn)行拆解:

1、card里是一個(gè)Colum的垂直排列
2、從上往下分別是圖片、名稱日期組合、聯(lián)系人地點(diǎn)組合、金額
3、其中名稱日期和聯(lián)系人地點(diǎn)是一個(gè)Row控件實(shí)現(xiàn)的橫向排列

下面我們邊擼碼邊看效果:
首先為了整體代碼文件的清晰整潔,我們?cè)趌ib下新建ui文件夾,在ui文件夾里新建diy_list_show.dart

image.png

然后在文件中根據(jù)上面拆解的結(jié)構(gòu)繪制ui控件:
diy_list_show.dart

import 'package:activity_record/model/diy_project.dart';
import 'package:flutter/material.dart';

class DiyListShow extends StatefulWidget {
  //將項(xiàng)目對(duì)象作為參數(shù)配置給DiyListShow的構(gòu)造函數(shù)
  DiyListShow({Key key, this.diyItem}) : super(key: key);
  DiyProject diyItem;
  @override
  State<StatefulWidget> createState() => new DiyListShowState();
}

class DiyListShowState extends State<DiyListShow> {
  @override
  Widget build(BuildContext context) {
    //card第一行時(shí)間和預(yù)留按鈕菜單
    Widget _rowTime() {
      return new Container(
        //設(shè)置距離左邊8.0的內(nèi)間距
        padding: const EdgeInsets.only(left: 8.0),
        child: new Row(
          //表示兩個(gè)子空間頭尾分布
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            new Text(widget.diyItem.date),
            new IconButton(
              icon: new Icon(Icons.more_horiz),
              onPressed: () {},
            )
          ],
        ),
      );
    }

    //card第三行名稱和地點(diǎn)
    Widget _rowNameAndPlace() {
      return new Container(
        padding: const EdgeInsets.all(8.0),
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            new Text(widget.diyItem.name,
                style:
                    new TextStyle(fontSize: 17.0, fontWeight: FontWeight.bold)),
            new Text(widget.diyItem.place),
          ],
        ),
      );
    }

    //進(jìn)行card里的內(nèi)容組合
    Widget _diyContentShow() {
      return Container(
        height: 288.0,
        child: new Column(
          children: <Widget>[
            _rowTime(),
            //使用expanded將填充控件的剩余空間
            new Expanded(
                //flex代表這個(gè)控件在父控件里的范圍比例,默認(rèn)是1,這里表示在高度288的容器里,圖片會(huì)填滿剩余的所有空間
                flex: 3,
                child: new Image.asset(
                  widget.diyItem.imagePath,
                  fit: BoxFit.cover,
                  width: 400.0,
                )),
            _rowNameAndPlace(),
            new Padding(
              padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
              child: new Row(
                children: <Widget>[
                  new Text(
                    '${widget.diyItem.singlePrcie.toString()}元',
                    style: new TextStyle(
                        fontSize: 15.0,
                        color: Theme.of(context).primaryColor,
                        fontWeight: FontWeight.bold),
                  ),
                  new SizedBox(
                    width: 20.0,
                  ),
                  new Text(
                    '${widget.diyItem.nums.toString()}份',
                    style: new TextStyle(
                        fontSize: 15.0,
                        color: Theme.of(context).primaryColor,
                        fontWeight: FontWeight.bold),
                  )
                ],
              ),
            )
          ],
        ),
      );
    }

    //將整個(gè)項(xiàng)目展示內(nèi)容包裹在card里
    return new Card(
      margin: const EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 9.0), //設(shè)置外邊距18
      //card形狀設(shè)置頂部圓形弧度12,底部沒有
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(top: Radius.circular(12.0))),
      //inkwell是一個(gè)帶水波紋觸摸效果的控件,預(yù)留點(diǎn)擊回調(diào)作為以后點(diǎn)擊響應(yīng)事件
      child: new InkWell(
        onTap: () {},
        child: _diyContentShow(),
      ),
    );
  }
}

里面的主要知識(shí)點(diǎn)我進(jìn)行了注釋,細(xì)心的朋友可能發(fā)現(xiàn)了一個(gè)知識(shí)點(diǎn)——image.asset,下面介紹使用方法:

  1. 首先需要在lib同級(jí)新建圖片文件夾images


    image.png
  2. 將想使用的圖片拷貝到目錄下
  3. pubsec.yaml文件里配置圖片的位置引用
    引用方法如下:
    image.png

這樣我們就能使用image.asset('圖片地址')展示圖片了。
Card的UI我們寫完了,下面我們回到home_page.dart,修改body代碼看看card得顯示效果如何:
home_page.dart

body: new ListView.builder(
        itemCount: 3,
        itemBuilder: (context, index) {
          return new DiyListShow(
            diyItem: _diyProjects[index],
          );
        },
      ),

寫完后可能發(fā)現(xiàn)DiyListShow()下面有警告,因?yàn)槲覀冞€沒引用這個(gè)dart文件,所以在開頭我們導(dǎo)入文件:
import 'package:activity_record/ui/diy_list_show.dart';包括以后在引用或使用其他dart文件里的方法內(nèi)容的時(shí)候都要記得先導(dǎo)入需要引用的文件。
以上代碼中ListView是列表滾動(dòng)控件,他有兩個(gè)構(gòu)造函數(shù),分別是ListView()和ListView.builder()
其中ListView()適合內(nèi)容不多的情況使用,因?yàn)閒lutter會(huì)全部渲染完成后展現(xiàn),如果是數(shù)據(jù)量很多或者無限數(shù)據(jù)的話就要使用ListView.builder(),這個(gè)構(gòu)造方法是根據(jù)用戶滾動(dòng)的情況,實(shí)時(shí)渲染需要展現(xiàn)的數(shù)據(jù),當(dāng)滑出屏幕后就會(huì)回收對(duì)應(yīng)的數(shù)據(jù)。

寫了這么多,是時(shí)候看下效果了,UI界面的調(diào)整都是邊看邊改的,對(duì)于新人我們不可能一步到位直接寫好,邊寫邊熱重載邊修改非常關(guān)鍵,好了運(yùn)行后效果如下:


image.png

image.png

感覺是不是還可以,哈哈 自戀下??赡苡腥擞X得奇怪,這和上面開頭的圖的布局不一樣啊,這是因?yàn)榕醮笕私o我提了新的需求,她對(duì)之前的效果提出了修改意見,要知道用戶的需求才是關(guān)鍵嘛,畢竟我們做完是給他們用的,所以我做了調(diào)整和之前的不一樣了,不過只要你掌握了方法,無論怎么改我相信你都可以做出來的啦。

最后總結(jié)

今天我們主要介紹了flutter里的基礎(chǔ)控件,雖然用到的不多,但是都是非?;A(chǔ)和常用的,包括listview、container、row、column、sizedbox、card、text、iconbutton、expanded、padding等等,每個(gè)控件里都有很多屬性,對(duì)于新人來說不要記得那么多,主要記住每個(gè)控件常用的屬性就可以啦,其他的后面再慢慢學(xué)習(xí)就好了。
試試看在每個(gè)內(nèi)容前加上小的示意圖標(biāo)(比如在時(shí)間前加上代表時(shí)間的icon),美化整個(gè)card效果,動(dòng)手試試吧!或者你也可以根據(jù)自己想要的樣子設(shè)計(jì)一個(gè)展示UI,然后自己實(shí)現(xiàn)一下吧。

最后附上項(xiàng)目源碼地址:https://gitee.com/xusujun33/activity_record_jia.git
項(xiàng)目持續(xù)更新中.......

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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