列表在 App 中是最多見的形式了,在 Flutter 中提供了 ListView 這個組件來實現列表,本篇將經過 ListView 實現一個圖文並茂的列表。數組
本篇要實現的列表如上圖所示。咱們拿到界面設計稿以後,在 UI 開發工做第一件事就是考慮界面的元素和佈局。以上面的界面爲例,實際的界面元素包括了列表和列表元素,而列表元素是關鍵,列表元素包括了左邊的一張圖片,圖片右側的標題和查看次數(帶前置圖標)。列表的元素的佈局以下圖所示。 縱向上,列表元素的佈局高度由圖片決定。圖標和瀏覽數的高度固定,剩餘的空間由標題佔據。考慮界面的美觀,標題最大行數爲2行,超出部分使用...替代。 橫向上,爲保持圖片的固定長寬比,圖片寬度固定。寬度在圖片固定後,剩餘的空間(除了間距留白外)即標題的空間。 基於上述的描述,能夠獲得大體的佈局:markdown
ListView 用於生成列表,,一般使用 builder靜態方法構建一個列表,其中關鍵的參數爲:網絡
若是要使用分隔組件的列表,也可使用 ListView.seperate 方法構建列表,這個方法多了一個 seperateBuilder 參數,用於返回列表元素間對應的分隔組件。app
所以,列表的關鍵是列表元素組件的實現。less
咱們仍是基於上一個工程,在 dynamic.dart 中實現列表。在源代碼目錄新增兩個文件,分別是 dynamic_item.dart 用於構建列表元素,dynamic_mock_data .dart用於模擬後臺接口數據。其中 dynamic_mock_data 的數據比較簡單,用一個list 靜態方法返回分頁數據,以下所示:ide
class DynamicMockData {
static List<Map<String, Object>> list(int page, int size) {
return List<Map<String, Object>>.generate(size, (index) {
return {
'title': '標題${index + (page - 1) * size + 1}:這是一個列表標題,最多兩行,多處部分將會被截取',
'imageUrl':
'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3331308357,177638268&fm=26&gp=0.jpg',
'viewCount': 180,
};
});
}
}
複製代碼
其中 page 和 size 用於模擬分頁狀況,方便後續實現上拉和下拉刷新。 注意這裏使用了 List 的 generate 方法來構建數組,該方法用於構建指定大小的數組, 能夠經過帶index輸入的回調函數構建對飲 index 下標的數組元素。函數
dynamic_item.dart的實現代碼以下所示:佈局
import 'package:flutter/material.dart';
class DynamicItem extends StatelessWidget {
final String title;
final String imageUrl;
final int viewCount;
static const double ITEM_HEIGHT = 100;
static const double TITLE_HEIGHT = 80;
static const double MARGIN_SIZE = 10;
const DynamicItem(this.title, this.imageUrl, this.viewCount, {Key key})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(MARGIN_SIZE),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_imageWrapper(this.imageUrl),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_titleWrapper(context, this.title),
_viewCountWrapper(this.viewCount.toString()),
],
),
)
],
),
);
}
Widget _titleWrapper(BuildContext context, String text) {
return Container(
height: TITLE_HEIGHT,
margin: EdgeInsets.fromLTRB(MARGIN_SIZE, 0, 0, 0),
child: Text(
this.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.headline6,
),
);
}
Widget _viewCountWrapper(String text) {
return Container(
margin: EdgeInsets.fromLTRB(MARGIN_SIZE, 0, 0, 0),
height: ITEM_HEIGHT - TITLE_HEIGHT,
child: Row(children: [
Icon(
Icons.remove_red_eye_outlined,
size: 14.0,
color: Colors.grey,
),
SizedBox(width: 5),
Text(
this.viewCount.toString(),
style: TextStyle(color: Colors.grey, fontSize: 14.0),
),
]),
);
}
Widget _imageWrapper(String imageUrl) {
return SizedBox(
width: 150,
height: ITEM_HEIGHT,
child: Image.network(imageUrl),
);
}
}
複製代碼
首先定義了title、imageUrl和 viewCount 幾個final 類型的成員屬性,使用 final 的約束是方便外部其餘類能夠直接訪問,但不能夠作修改。若是這些屬性自己外部不可訪問,也能夠定義爲私有成員。字體
其次是使用構造函數直接完成成員屬性的初始化,這是 Dart 語言的一種簡寫方法,只要傳參次序一致就能夠不須要函數體自動完成成員的初始化,一般會用在 final 修飾的成員屬性。ui
在 build 方法中完成了整個界面的構建。其中注意這裏使用了 Expanded 包裹右側區域,Expanded組件是表示橫向佈局中,該組件將自動佔據剩餘的空間。若是沒有這個包裹,會致使右側內容過寬時沒法顯示徹底而報警。
圖片、標題和瀏覽數均經過單獨的構建組件方法獲取,這一方面是下降UI嵌套層級,另外一方面若是往後有一樣的組件,則能夠抽離單獨的構建方法提升複用性。下面對關鍵的幾個設置進行解讀:
除了 ListView 以外,本篇涉及到的組件以下:
以上組件在本篇示例中都是基本應用,更多設置能夠在 IDE 中查看源碼或閱讀官方文檔瞭解。
結語:本篇講述了使用 ListView 完成列表的構建,重點講述了列表元素如何佈局,具體的佈局組件和實現方法。界面實現的關鍵工做實際是佈局子元素的拆分。剩下的實現方式存在多種,看各人喜愛。可是,須要注意避免過多嵌套致使代碼可讀性下降,以及提升 UI 組件的可複用性。