Flutter:一小時從零構建一個簡單的 App,以及你如何做到這一點!
概要
這是一個偶然的事件,我正在瀏覽我的 Youtube 的訂閱內容,看到了很多關于 Flutter 的相關視頻,這引起了我的興趣。我很興奮的開始閱讀它的文檔。
我喜歡的學習方式,是邊動手邊學習。閱讀完幾頁文檔并研究如何創建一個新的項目,我對自己嘗試新東西,倍感信心。
如你在 Gif 中看到的那樣,這是一個非常基本的應用程序,但它仍然有一些讓我覺得有趣的地方。
它使用 Google Book Api 從數據庫中讀取書籍數據。
在數據展示前,顯示一個加載指示器。
將書名和圖像,都加載顯示出來。
在傳統的 Android App 中,這將需要幾個類和不少代碼。
而使用 Flutter,這個 App 有一個類和 129 行代碼。包括一些引入語句和布局。
這一切,是不是聽起來很棒?讓我們開始吧!
開始
在我開始之前,我并不會詳細的介紹這個 App 的所有細節,請務必查看示例代碼以及文檔,進行參考。
另外,整個 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 的一個實現,我這里主要用它來監聽 textChanged 事件。
還有一個變量,表示我們目前是否在等待服務端響應。
此外,用戶界面包含三個主要元素。
- 列表
- 加載器。
- 文本輸入框。
我們將他們放在一起。
- new TextField(
- decoration: new InputDecoration(
- hintText: 'Choose a book',
- ),
- onChanged: (string) => (subject.add(string)),
- ),
關于這個地方的一個有趣的部分是 onChanged,在這里我們傳遞了一個 Lambda,它講當前輸入的文本添加到 subject 上,這使得我們可以在其他地方監聽回調。
- _isLoading? new CircularProgressIndicator(): new Container(),
如果當前出于加載狀態,則顯示進度條,否則顯示一個空的容器。
- 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。而在其內部,我們布局了一個用于顯示網絡圖片的 Image 以及一個用于顯示書籍描述的 Text。
邏輯代碼
- @override
- void initState() {
- super.initState();
- subject.stream.debounce(new Duration(milliseconds: 600)).listen(_textChanged);
- }
在這個 initState 方法中,我們使用流式編碼來處理它的事件并消費它們。這樣做是為了每次點鍵入文本的時候,不會立即向 Api 服務器發送請求,而是會在***一次鍵入文本之后,等待 600ms,再將輸入的字符串發送到 _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 發出請求 volumes?q=$text ,其中 text 包含當前輸入的字符串。
當結果返回的時候:
- 獲得返回的內容。
- 解析 JSON 數據,飯后返回一個 Map。
- "items" 包含大量的書籍信息。
- 循環迭代之后,使用 _addBook() 方法添加到 "items" 中。
- 這些 "items" 就是我們需要的標題和書籍封面圖。
- void _addBook(dynamic book) {
- setState(() {
- _items.add(new Book(book["volumeInfo"]["title"], book["volumeInfo"]["imageLinks"]["smallThumbnail"]));
- });
- }
小結
到這里,該 App 是一個完整的小應用。
這是***步,接下來將使用一個數據庫來存儲數據。
【本文為51CTO專欄作者“張旸”的原創稿件,轉載請通過微信公眾號聯系作者獲取授權】