Flutter 入門實例

Dart4Flutter - 01 – 變量、類型和函數git

Dart4Flutter – 02 –控制流 和異常github

Dart4Flutter – 03 – 類和泛型web

Dart4Flutter – 04 – 異步和庫編程

Dart4Flutter - 拾遺01 - flutter-dart環境搭建windows

Dart4Flutter - 不可變性api

Flutter入門 - 狀態管理網絡

Flutter 入門實例1app

Flutter 入門 - Container 屬性詳解框架

Flutter 入門-本地訪問-MethodChannelless

Flutter 實例 - 加載更多的ListView

Flutter 實例 - 從本地到Flutter通訊 - Event Channels

自從10年前Android和iOS出現,在移動開發界,跨平臺開發一直是一個目標。一個跨平臺的應用能夠幫助公司和團隊節省大量的時間和精力。

過去這些年,已經發布了不少跨平臺的開發工具,包括來自adobe基於web的PhoneGap,來自Microsoft的Xamarin,來自Facebook的React Native。

最近進入跨平臺界的工具是來自google的flutter,如今已經發布了第一個預覽版。flutter擁有開發效率高、UI渲染快,更容易的自定義組件、和在兩個平臺上和本地應用同樣的性能。

介紹

flutter應用是用dart語言開發的,Dart和其餘語言,例如Kotlin Swift同樣,擁有不少共同的語言特性,並且能夠轉化爲JavaScript代碼。

做爲一個跨平臺框架,flutter很是像React Native,例如flutter支持響應式和聲明是編程。可是和React Native不同的是,flutter不須要JavaScript橋接,這樣可提高整個應用的性能和應用啓動的時間。Dart是經過AOT技術取得了上述的表現。

另外一方面,擁有JIT的Flutter,支持在開發期間,不須要從頭構建整個應用,就能夠刷新UI,提供開發效率。

正如本教程將要展現的,flutter框架是創建在組件思想之上的。在flutter中,組件不僅僅指應用的視圖,而是整個界面甚至是真個應用。

除了開發跨Android和iOS應用,學習flutter將給你一個在Fuchsia平臺開發的超前開始。Fuchsia是google開發中的實驗性的操做系統。

本教程,咱們構建一個app,首先從github api中獲取一個組織的成員信息,而後將成員信息展現在一個滾動列表中。

當開發app時,但是使用Android模擬器、iOS模擬器或者同時使用。

在構建app時,咱們將學習flutter一下內容:

  • 配置開發環境
  • 建立工程
  • Hot reload
  • 引入文件和庫
  • 使用組件和建立本身的組件
  • 建立網絡請求
  • 在列表中展現列表項
  • 添加應用主題 有也將順便學習一點Dart。

開始

Flutter開發能夠在macOS,Linux或Windows上完成。 雖然您能夠在Flutter工具鏈中使用任何編輯器,但IntelliJ IDEA,Android Studio和Visual Studio Code的IDE插件能夠簡化開發工做。 咱們將在本教程中使用Android Studio。

配置開發環境

您能夠在這找到配置flutter開發環境的說明,具體步驟因平臺而異,但主要步驟是:

  1. 下載flutter SDK,地址
  2. 將flutter的bin目錄添加到path中
  3. 執行flutter doctor命令,他會安裝flutter框架,包括dart,並且提示你任何其餘須要安裝的依賴。
  4. 安裝其餘依賴
  5. 在IDE中安裝flutter插件
  6. 測試驅動應用 Flutter網站上提供的說明作得很是好,並容許您在所選平臺上輕鬆設置開發環境。 本教程的其他部分假設您已經爲Flutter開發設置了Android Studio,而且您已經解決了flutter doctor發現的任何問題。

建立工程

  • 以下所示選擇File->new->new Flutter Project
    image
  • 以下所示選擇Flutter Application,點擊next.
    image
  • 以下如所示填寫項目名稱、Flutter SDK的路徑、項目存儲目錄。
    image

在Android Studio中,您會在左側看到一個顯示項目結構的面板。 有iOS和Android的文件夾,以及包含main.dart的lib文件夾。 您將本教程中的只在lib文件夾中工做。

image
將main.dart中的代碼替換爲如下內容:

import 'package:flutter/material.dart';

void main() => runApp(new GHFlutterApp());


class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'GHFlutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('GHFlutter'),
        ),
        body: new Center(
          child: new Text('GHFlutter'),
        ),
      ),
    );
  }
}
複製代碼

頂部附近的main()函數使用=>運算符,爲了單行函數運行應用程序。 您有一個名爲GHFlutterApp的應用程序。 你在這裏看到你的應用程序自己是一個StatelessWidget(無狀態組件)。Flutter應用程序中的大多數實體都是組件,不管是無狀態的仍是有狀態的。 您重寫組件的build()方法來建立您的應用的組件。 您正在使用MaterialApp 組件,該組件提供了大量遵循Material Design規範應用組件。

對於本入門教程,經過選擇它並敲擊Delete鍵,從項目中刪除test文件夾中的測試文件widget_test.dart

你能夠在macOS上,啓動iOS模擬器。 您也能夠在macOS,Linux或Windows上使用Android模擬器。

您看到的慢速模式banner表示應用程序正在以調試模式運行。

您能夠經過單擊Android Studio窗口頂部工具欄右側的中止按鈕來中止正在運行的應用程序:

image

Hot Reload

flutter支持Hot Reload 功能。flutter中的Hot reload和Android Studio的Instant Run相似。 構建並運行應用程序,使其在模擬器或模擬器上運行:

如今,不中止正在運行的應用程序,將應用程序欄字符串更改成其餘內容:

appBar: new AppBar(
  title: new Text('GHFlutter App'),
),
複製代碼

如今單擊工具欄上的熱從新加載按鈕:

image
在一兩秒鐘內,您會看到正在運行的應用程序中反映了變化的內容:

因爲Flutter處於開發階段,熱從新加載功能可能沒法正常工做,但整體而言,在構建UI時節省時間。

引入文件

您不須要將全部的Dart代碼保存在一個main.dart文件中,而是但願可以從您建立的其餘類中導入。 如今您將看到一個用於導入字符串的示例,這將有助於您應用的本地化。 在lib文件夾中右鍵單擊並選擇New File建立一個名爲strings.dart的文件: 在新建的文件中添加以下類:

class Strings {
  static String appTitle = "GHFlutter";
}
複製代碼

在main.dart的頂部添加以下引入:

import 'strings.dart';
複製代碼

以下更新應用的title:

return new MaterialApp(
  title: Strings.appTitle,
複製代碼

更新其餘的字符串,使用新的字符串文件,因此最終GHFlutterApp類以下:

class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
  return new MaterialApp(
    title: Strings.appTitle,
    home: new Scaffold(
      appBar: new AppBar(
        title: new Text(Strings.appTitle),
      ),
      body: new Center(
        child: new Text(Strings.appTitle),
      ),
    ),
  );
}
}
複製代碼

構建並運行應用程序,您應該看不到任何變化,但如今您正在使用字符串文件中的字符串。

組件

幾乎Flutter應用程序的每一個元素都是一個組件。組件被設計成不可變的,由於使用不可變組件有助於保持應用程序UI的輕量級。

您將使用兩種基本類型的組件:

Stateless: 僅依賴於本身的配置信息的組件,例如圖像視圖中的靜態圖像 Stateful: 須要維護動態信息的組件,並經過與狀態對象交互來實現 無狀態和有狀態的組件在每一個幀的Flutter應用程序中重繪,不一樣之處在於有狀態組件將其配置委託給狀態對象。

要開始製做本身的組件,請在main.dart的底部建立一個新類:

class GHFlutter extends StatefulWidget {
  @override
  createState() => new GHFlutterState();
}
複製代碼

你已經建立了一個StatefulWidget子類,而且你重寫了createState()方法來建立它的狀態對象。 如今在GHFlutter上添加一個GHFlutterState類:

class GHFlutterState extends State<GHFlutter> {
}
複製代碼

GHFlutterState使用GHFlutter的參數擴展狀態。

製做新組件時的主要任務是重寫在組件呈如今屏幕上時調用的build()方法。

重寫GHFlutterState 的build()方法:

@override
Widget build(BuildContext context) {
​    
}
複製代碼

最終以下:

@override
Widget build(BuildContext context) {
  return new Scaffold (
    appBar: new AppBar(
      title: new Text(Strings.appTitle),
    ),
    body: new Text(Strings.appTitle),
  );
}
複製代碼

Scaffold 是material design組件的容器。 它充當組件層次結構的根。 您已將AppBar和一個body添加到scaffold中,而且每一個都包含一個Text組件

更新GHFlutterApp,以便它使用新的GHFlutter組件做爲其home屬性,而不是構建默認的scaffold:

class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: Strings.appTitle,
      home: new GHFlutter(),
    );
  }
}
複製代碼

構建並運行應用程序,您將看到新的組件正在運行:

如今尚未多少變化,可是如今你已經開始構建新的組件。

網絡請求

以前,您將本身的strings.dart文件導入到項目中。 您能夠一樣導入Flutter框架的其餘庫或者Dart的庫。

例如,您如今將使用框架中的庫來進行HTTP網絡調用,並將生成的JSON響應解析爲Dart對象。 在main.dart的頂部添加兩個新的引入:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'strings.dart';
複製代碼

您會看到當前未使用的指標在引入上。

Dart應用程序是單線程的,但Dart支持在其餘線程上運行代碼以及運行異步代碼,經過使用async/await模式,從而不會阻止UI線程。

您將進行異步網絡調用以檢索GitHub組織成員列表。 在GHFlutterState頂部添加一個空List做爲屬性,並添加一個屬性來保存文本樣式(_biggerFont):

var _members = [];

final _biggerFont = const TextStyle(fontSize: 18.0);
複製代碼

名字如下劃線開頭的變量表示類的私有成員變量。 要進行異步HTTP調用,請在GHFlutterState中添加一個_loadData()方法:

_loadData() async {
  String dataURL = "https://api.github.com/orgs/raywenderlich/members";
  http.Response response = await http.get(dataURL);
  setState(() {
    _members = JSON.decode(response.body);
  });
}
複製代碼

您已將async關鍵字添加到_loadData()中,以告訴Dart它是異步的,可是http.get()的await關鍵字表示阻塞執行。 經過dataUrl設置API的請求地址。

當HTTP調用完成時,您將回調傳遞給在UI線程上同步運行的setState()。 這時,您解碼JSON響應並將其賦值給_members變量。 在GHFlutterState中重寫initState方法,在initState中調用_loadData() 方法。當組件狀態初始化時,_loadData()會被調用,加載數據。

@override
void initState() {
  super.initState();

  _loadData();
}
複製代碼

使用 ListView

如今您已經有了成員列表,您須要一種方法在列表UI中顯示它們。 Dart提供了一個ListView組件,可以讓您在列表中顯示數據。 ListView的做用相似於Android上的RecyclerView和iOS上的UITableView,經過重複回收利用之後的view,使列表滾動更加順滑。 在GHFlutterState中添加_buildRow()方法:

Widget _buildRow(int i) {
  return new ListTile(
    title: new Text("${_members[i]["login"]}", style: _biggerFont)
  );
}
複製代碼

您將返回一個ListTile組件,該組件顯示第iJSON成員的"login"值,還使用您以前建立的文本樣式(_biggerFont)。

更新build方法,使body屬性值爲ListView.builder:

body: new ListView.builder(
  padding: const EdgeInsets.all(16.0),
  itemCount: _members.length,
  itemBuilder: (BuildContext context, int position) {
    return _buildRow(position);
  }),
複製代碼

您已添加padding,將itemCount設置爲成員數量,設置itemBuilder調用_buildRow(),傳遞postion變量。

您能夠嘗試熱從新加載,但可能會收到"Full restart may be required"的消息。 若是是這樣,重啓:

發起網絡請求,解析數據,並在列表中顯示結果是很容易的!

添加分割線

爲了在列表中增長分割線,須要翻倍list的itemCount屬性,而後list中的position爲偶數時返回Divider組件。

body: new ListView.builder(
  itemCount: _members.length * 2,
  itemBuilder: (BuildContext context, int position) {
    if (position.isOdd) return new Divider();

    final index = position ~/ 2;

    return _buildRow(index);
  }),
複製代碼

必定要itemCount翻倍,由於增長了分割線,因此刪除padding。在itemBuilder中,返回Divider組件,或者經過整數除法計算一個新的index,利用_buildRow(index)返回一個組件。

嘗試從新加載,你應該在列表中看到分割線:

要在每行中添加padding,能夠在_buildRow()中使用Padding組件:

Widget _buildRow(int i) {
  return new Padding(
    padding: const EdgeInsets.all(16.0),
    child: new ListTile(
      title: new Text("${_members[i]["login"]}", style: _biggerFont)
    )
  );
}
複製代碼

ListTile如今是Padding的子部件。 熱加載以查看行上的padding而不是分隔符上的Padding。

解析爲自定義類型

在上一節中,JSON解析器將JSON響應中的每一個成員都做爲Dart Map類型實例添加到_members列表中,這至關於Kotlin中的Map或Swift中的Dictionary。 可是,若是你想使用自定義類。 在main.dart文件中添加Member類:

class Member {
  final String login;

  Member(this.login) {
    if (login == null) {
      throw new ArgumentError("login of Member cannot be null. "
          "Received: '$login'");
    }
  }
}
複製代碼

member有一個login屬性,和一個構造函數,在構造函數中當login值爲null時,會拋出一個error。 更新GHFlutterState類中的_members變量聲明,因此如今他就是一個Member對象的list

var _members = <Member>[];
複製代碼

更新_buildRow()方法使用Member對象的login屬性而不是map的"login"key的值。

title: new Text("${_members[i].login}", style: _biggerFont)
複製代碼

如今更新在_loadData()中的傳遞給setState()的回調,將map解析爲Member對象,添加到member對象list中。

setState(() {
  final membersJSON = JSON.decode(response.body);

  for (var memberJSON in membersJSON) {
    final member = new Member(memberJSON["login"]);
    _members.add(member);
  }
});
複製代碼

若是你熱加載,有可能看到一個錯誤,這時你從新啓動。

下載圖片

每一個member都有一個頭像url.你將添加頭像到Member類中,而後在app中顯示頭像。

在Member類中添加avatarUrl屬性,這個屬性不能爲null。

class Member {
  final String login;
  final String avatarUrl;

  Member(this.login, this.avatarUrl) {
    if (login == null) {
      throw new ArgumentError("login of Member cannot be null. "
          "Received: '$login'");
    }
    if (avatarUrl == null) {
      throw new ArgumentError("avatarUrl of Member cannot be null. "
          "Received: '$avatarUrl'");
    }
  }
}
複製代碼

更新_buildRow()方法,經過NetworkImage 和 the CircleAvatar 組件顯示頭像。

Widget _buildRow(int i) {
  return new Padding(
    padding: const EdgeInsets.all(16.0),
    child: new ListTile(
      title: new Text("${_members[i].login}", style: _biggerFont),
      leading: new CircleAvatar(
        backgroundColor: Colors.green,
        backgroundImage: new NetworkImage(_members[i].avatarUrl)
      ),
    )
  );
}
複製代碼

設置頭像爲ListTile的leading屬性,頭像將顯示在title的前面。 更新_loadData()方法,使在建立一個新的Member時使用map的"avatar_url"值。

final member = new Member(memberJSON["login"], memberJSON["avatar_url"]);
複製代碼

構建運行,你將能看到每個行的頭像。

整理代碼

大部分代碼如今位於main.dart文件中。 爲了使代碼更清晰一點,你能夠重構組件和你已經添加到他們本身的文件中的類。 在lib文件夾中建立名爲member.dart和ghflutter.dart的文件。 將Member類移入member.dart,並將GHFlutterState和GHFlutter類轉移到ghflutter.dart。 您不須要member.dart中的任何導入語句,但flutter.dart中的導入應以下所示:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'member.dart';
import 'strings.dart';  
複製代碼

您還須要更新main.dart中的導入,因此整個文件包含如下內容:

import 'package:flutter/material.dart';

import 'ghflutter.dart';
import 'strings.dart';

void main() => runApp(new GHFlutterApp());


class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: Strings.appTitle,
      home: new GHFlutter(),
    );
  }
}  
複製代碼

經過構建並運行應用程序,您應該看不到任何更改,但代碼如今更清晰一些

添加主題

您能夠經過將主題屬性添加到您在main.dart中建立的MaterialApp來輕鬆地將主題添加到應用程序:

lass GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: Strings.appTitle,
      theme: new ThemeData(primaryColor: Colors.green.shade800), 
      home: new GHFlutter(),
    );
  }
}  
複製代碼

您正在使用綠色陰影做爲Material Design主題顏色值。

構建並運行應用程序,以查看新的主題:

https://user-gold-cdn.xitu.io/2018/6/26/1643c2669c39ca8a?w=281&h=500&f=png&s=65613

大部分應用截圖都來自Android模擬器。 您也能夠在iOS模擬器中運行最終的主題應用程序:

這就是我所說的跨平臺

最終代碼地址

參考

www.raywenderlich.com/188257/gett…

相關文章
相關標籤/搜索