Flutter:一小時從零構建一個簡單的 App,以及你如何做到這一點!
概要
這是一個偶然的事件,我正在瀏覽我的 Youtube 的訂閱內容,看到了很多關于 Flutter 的相關視頻,這引起了我的興趣。我很興奮的開始閱讀它的文檔。
我喜歡的學習方式,是邊動手邊學習。閱讀完幾頁文檔并研究如何創(chuàng)建一個新的項目,我對自己嘗試新東西,倍感信心。
如你在 Gif 中看到的那樣,這是一個非?;镜膽贸绦颍匀挥幸恍┳屛矣X得有趣的地方。
它使用 Google Book Api 從數(shù)據(jù)庫中讀取書籍數(shù)據(jù)。
在數(shù)據(jù)展示前,顯示一個加載指示器。
將書名和圖像,都加載顯示出來。
在傳統(tǒng)的 Android App 中,這將需要幾個類和不少代碼。
而使用 Flutter,這個 App 有一個類和 129 行代碼。包括一些引入語句和布局。
這一切,是不是聽起來很棒?讓我們開始吧!
開始
在我開始之前,我并不會詳細的介紹這個 App 的所有細節(jié),請務必查看示例代碼以及文檔,進行參考。
另外,整個 App 的代碼(所有 129 行代碼),都在 Github 上開源,我推薦你看看。
https://github.com/Norbert515/BookSearch
- List<Book> _items = new List();
- final subject = new PublishSubject<String>();
- bool _isLoading = false;
在這個例子中,我們需要 3 個變量。
一個書本的列表,其中一般被定義成長這樣:
- class Book {
- String title,url;
- Book(this.title,this.url);
- }
一個 Public Subject,它是 RxDart 的一部分,而 RxDart 又是 Rx 的一個實現(xiàn),我這里主要用它來監(jiān)聽 textChanged 事件。
還有一個變量,表示我們目前是否在等待服務端響應。
此外,用戶界面包含三個主要元素。
- 列表
- 加載器。
- 文本輸入框。
我們將他們放在一起。
- new TextField(
- decoration: new InputDecoration(
- hintText: 'Choose a book',
- ),
- onChanged: (string) => (subject.add(string)),
- ),
關于這個地方的一個有趣的部分是 onChanged,在這里我們傳遞了一個 Lambda,它講當前輸入的文本添加到 subject 上,這使得我們可以在其他地方監(jiān)聽回調。
- _isLoading? new CircularProgressIndicator(): new Container(),
如果當前出于加載狀態(tài),則顯示進度條,否則顯示一個空的容器。
- new Expanded(
- child: new ListView.builder(
- padding: new EdgeInsets.all(8.0),
- itemCount: _items.length,
- itemBuilder: (BuildContext context, int index) {
- return new Card(
- child: new Padding(
- padding: new EdgeInsets.all(8.0),
- child: new Row(
- children: <Widget>[
- _items[index].url != null? new Image.network(_items[index].url): new Container(),
- new Flexible(
- child: new Text(_items[index].title, maxLines: 10),
- ),
- ],
- )
- )
- );
- },
- ),
- ),
該列表是一個基于索引的 ListView。而在其內部,我們布局了一個用于顯示網(wǎng)絡圖片的 Image 以及一個用于顯示書籍描述的 Text。
邏輯代碼
- @override
- void initState() {
- super.initState();
- subject.stream.debounce(new Duration(milliseconds: 600)).listen(_textChanged);
- }
在這個 initState 方法中,我們使用流式編碼來處理它的事件并消費它們。這樣做是為了每次點鍵入文本的時候,不會立即向 Api 服務器發(fā)送請求,而是會在***一次鍵入文本之后,等待 600ms,再將輸入的字符串發(fā)送到 _textChange() 方法,這也是我們使用 Rx 的唯一原因。
- void _textChanged(String text) {
- if(text.isEmpty) {
- setState((){_isLoading = false;});
- _clearList();
- return;
- }
- setState((){_isLoading = true;});
- _clearList();
- http.get("https://www.googleapis.com/books/v1/volumes?q=$text")
- .then((response) => response.body)
- .then(JSON.decode)
- .then((map) => map["items"])
- .then((list) {list.forEach(_addBook);})
- .catchError(_onError)
- .then((e){setState((){_isLoading = false;});});
- }
- void _onError(dynamic d) {
- setState(() {
- _isLoading = false;
- });
- }
- void _clearList() {
- setState(() {
- _items.clear();
- });
- }
所有的邏輯都在這里了。
首先說幾點,如果 Text 是空的,我們不再加載并消除列表。
另外,如果我們獲取到 Text 的內容,我們將開始加載并清除列表。
之后,我們向 Google Api 發(fā)出請求 volumes?q=$text ,其中 text 包含當前輸入的字符串。
當結果返回的時候:
- 獲得返回的內容。
- 解析 JSON 數(shù)據(jù),飯后返回一個 Map。
- "items" 包含大量的書籍信息。
- 循環(huán)迭代之后,使用 _addBook() 方法添加到 "items" 中。
- 這些 "items" 就是我們需要的標題和書籍封面圖。
- void _addBook(dynamic book) {
- setState(() {
- _items.add(new Book(book["volumeInfo"]["title"], book["volumeInfo"]["imageLinks"]["smallThumbnail"]));
- });
- }
小結
到這里,該 App 是一個完整的小應用。
這是***步,接下來將使用一個數(shù)據(jù)庫來存儲數(shù)據(jù)。
【本文為51CTO專欄作者“張旸”的原創(chuàng)稿件,轉載請通過微信公眾號聯(lián)系作者獲取授權】