本文由
玉剛說寫做平臺
提供寫做贊助java原做者:
楊哲
git版權聲明:本文版權歸微信公衆號 玉剛說 全部,未經許可,不得以任何形式轉載github
2018年2月27日,在2018世界移動大會上,Google發佈了Flutter的第一個Beta版本。Flutter是Google用以幫助開發者在 Android/IOS 兩個平臺開發高質量原生應用的全新移動UI框架。編程
這段介紹是直接抄下來的,雖然我並不知道什麼叫可移植的GPU加速的渲染引擎,可是最終結果就是利用Flutter構建的應用在運行效率上會和原生應用差很少,那麼咱們開始走進 Flutter 的世界吧。swift
本文章的結構以下:api
tips: 本文在蘋果筆記上開發,由於須要調試 IOS 和 Android數組
這裏我用 Android Studio 開發 Flutter ,下邊咱們來看一下開發步驟。安全
我這裏也是大概描述一下大概流程,爲了節省篇幅,官網有更加詳細的步驟:bash
我首先推薦官網: Flutter 官網 若是你想快速入門,這裏有 中文官網微信
當時我學習 Dart 語言的時候,一直思考 Dart 有什麼優點?只有 Google 這個親爹的緣由嗎?我帶着這個思考查了很多資料,發現 Dart 的優點有以下幾點:
JIT編譯在開發過程當中使用,編譯器速度特別快。而後,當一個應用程序準備發佈時,它被AOT編譯。所以,藉助先進的工具和編譯器,Dart具備一箭雙鵰的優點:極快的開發週期、快速的執行速度和極短啓動時間。
咱們討論過一個有助於保持順暢的特性,那就是Dart能AOT編譯爲本地機器碼。預編譯的AOT代碼比JIT更具可預測性,由於在運行時不須要暫停執行JIT分析或編譯。
然而,AOT編譯代碼還有一個更大的優點,那就是避免了「JavaScript橋樑」。當動態語言(如JavaScript)須要與平臺上的本地代碼互操做時,它們必須經過橋進行通訊,這會致使上下文切換,從而必須保存特別多的狀態(可能會存儲到輔助存儲)。這些上下文切換具備雙重打擊,由於它們不只會減慢速度,還會致使嚴重的卡頓。
若是你有 java 語言的基礎,發現dart裏邊的 API 幾乎 90% 以上相同, 幾乎很快能上手,這裏我就特別指出他們的不一樣點。這裏我想最快的入門方法,應該是查看官網的 quick start, 快速正版放心,並且不會過期,一直在更新。 這裏我簡單的介紹一下:
因爲篇幅有限,我這裏列舉一下我認爲特別的地方,剩下的能夠仔細閱讀 官方文檔。
咱們萬年老套路 Hello World,熟悉他語言的運行機制。
// 定義一個函數
printNumber(num aNumber) {
print('The number is $aNumber.'); //控制檯打印
}
// 啓動方法,相似於 java 的main函數
main() {
var number = 42;
printNumber(number);
}
複製代碼
咱們能夠以看到一下幾點:
沒有初始化的變量都會被賦予默認值 null
var name = 'Bob';
var unInitializeValue1; //未給初值的變量,默認值爲 null
Int unInitializeValue2; //即便是Int 型,默認值也是 null
//相似於 Kotlin, 能夠推導出 name 爲字符串類型
var name = 'Bob';
// 若是不想 推導出類型,下邊兩種寫法
dynamic name = 'Bob';
Object name = 'Bob';
複製代碼
程序中只當數據類型是爲了指出本身的使用意圖,並幫助語言進行語法檢查。可是,指定類型不是必須的,相似於Kotlin 會進行類型推導。
number 取值範圍:-2^53 to 2^53
// String -> int
var one = int.parse('1');
// String -> double
var onePointOne = double.parse('1.1');
// int -> String
String oneAsString = 1.toString();
// double -> String 注意括號中要有小數點位數,不然報錯
String piAsString = 3.14159.toStringAsFixed(2);
複製代碼
string
示例代碼:
var s = 'Android Developer';
print ('A Commpany has a $s, which is good idea.' ==
'A Commpany has a Android Developer,' +
' which is good idea.');
print('I am a ' +
'${s.toUpperCase()} is very hornor!' ==
'I am a ' +
'ANDROID DEVELOPER is very hornor!');
複製代碼
bool 布爾類型
Dart 是強 bool 類型檢查,只有bool 類型的值是true 才被認爲是true
list 列表
var vegetables = new List();
// 或者簡單的用List來賦值
var fruits = ['apples', 'oranges'];
// 添加元素
fruits.add('kiwis');
// 添加多個元素
fruits.addAll(['grapes', 'bananas']);
// 獲取第一個元素
fruits.first;
// 獲取元素最後一個元素
fruits.last;
// 查找某個元素的索引號
assert(fruits.indexOf('apples') == 0);
// 刪除指定位置的元素,返回刪除的元素
fruits.removeAt(index);
// 刪除指定元素,成功返回true,失敗返回false
fruits.remove('apples');
// 刪除最後一個元素,返回刪除的元素
fruits.removeLast();
// 刪除指定範圍元素,含頭不含尾,成功返回null
fruits.removeRange(start,end);
// 刪除指定條件的元素,成功返回null
fruits.removeWhere((item) => item.length >6);
// 刪除全部的元素
fruits.clear();
// sort()對元素進行排序,傳入一個函數做爲參數,return <0表示由小到大, >0表示由大到小
fruits.sort((a, b) => a.compareTo(b));
複製代碼
map 散列表
// Map的聲明
var hawaiianBeaches = {
'oahu' : ['waikiki', 'kailua', 'waimanalo'],
'big island' : ['wailea bay', 'pololu beach'],
'kauai' : ['hanalei', 'poipu']
};
var searchTerms = new Map();
// 指定鍵值對的參數類型
var nobleGases = new Map<int, String>();
// Map的賦值,中括號中是Key,這裏可不是數組
nobleGase[54] = 'dart';
//Map中的鍵值對是惟一的
//同Set不一樣,第二次輸入的Key若是存在,Value會覆蓋以前的數據
nobleGases[54] = 'xenon';
assert(nobleGases[54] == 'xenon');
// 檢索Map是否含有某Key
assert(nobleGases.containsKey(54));
//刪除某個鍵值對
nobleGases.remove(54);
assert(!nobleGases.containsKey(54));
複製代碼
這裏我介紹了一下基本語法,還有函數、異常、單線程的操做,因爲篇幅有限,並且咱們也是一個入門教程,我這裏就介紹到這裏,若是想具體查看,能夠點擊我推薦的官網教程,用的 dart2 的方式。
Flutter使用了一個靈活的系統,容許您調用特定平臺的API,不管在Android上的Java或Kotlin代碼中,仍是iOS上的ObjectiveC或Swift代碼中都可用。
Flutter平臺特定的API支持不依賴於代碼生成,而是依賴於靈活的消息傳遞的方式:
應用的Flutter部分經過平臺通道(platform channel)將消息發送到其應用程序的所在的宿主(iOS或Android)。
宿主監聽的平臺通道,並接收該消息。而後它會調用特定於該平臺的API(使用原生編程語言)並將響應發送回客戶端,即應用程序的Flutter部分。
調用流程以下:
首先建立一個新的應用程序: 方式一: 在終端運行中:
flutter create batterylevel
複製代碼
默認狀況下,模板支持使用Java編寫Android代碼,或使用Objective-C編寫iOS代碼。要使用Kotlin或Swift,請使用-i和/或-a標誌:
在終端中運行:
flutter create -i swift -a kotlin batterylevel
複製代碼
方式二: 也能夠經過項目new Flutter Project 來創造項目
該應用的State類擁有當前的應用狀態。咱們須要延長這一點以保持當前的電量
首先,咱們構建通道。咱們使用MethodChannel調用一個方法來返回電池電量。
通道的客戶端和宿主經過通道構造函數中傳遞的通道名稱進行鏈接。單個應用中使用的全部通道名稱必須是惟一的;
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.io/battery');
// Get battery level.
}
複製代碼
接下來,咱們調用通道上的方法,指定經過字符串標識符調用方法getBatteryLevel。 該調用可能失敗。
例如,若是平臺不支持平臺API(例如在模擬器中運行時),因此咱們將invokeMethod調用包裝在try-catch語句中。
咱們使用返回的結果,在setState中來更新用戶界面狀態batteryLevel。
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<Null> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
複製代碼
最後,咱們在build建立包含一個小字體顯示電池狀態和一個用於刷新值的按鈕的用戶界面。
override
Widget build(BuildContext context) {
return new Material(
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new RaisedButton(
child: new Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
new Text(_batteryLevel),
],
),
),
);
}
複製代碼
接下來,在 ManActivity 中 的 onCreate裏建立MethodChannel並設置一個MethodCallHandler。確保使用與在Flutter客戶端使用的通道名稱相同。
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.io/battery";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
});
}
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
}
複製代碼
到此爲止,咱們介紹完如何使用兩個平臺的特殊 API ,若是你在使用flutter 開發的時候,碰到官方沒有支持的api,你能夠本身去實現兩個平臺的代碼,來實現你想要的效果。
若是您但願在多個Flutter應用程序中使用特定於平臺的代碼,將代碼分離爲位於主應用程序以外的目錄中,作一個平臺插件會頗有用。這樣就能夠把 UI 部分刨除掉,複用代碼部分。 咱們能夠開發插件來來實現咱們要的通用的部分,如何開發一個插件呢?這裏我就不班門弄斧了,你能夠直接查看官網提升的如何開發一個插件
經過上邊的介紹,你們對於Flutter 有必定的理解,下面咱們實現一個demo項目,如何咱們開始進入實戰階段,咱們具體實現的效果如圖下: 主頁(Tab欄+Banner輪播圖+ViewPaper):
抽屜:經過Android Studio new 一個flutter Project 項目,刪除lib/main.dart代碼,咱們開始本身實現代碼。
MaterialAPP 是一個方便的widget,它封裝了應用程序實現Material Design所須要的一些widget。Material 風格是咱們一直想實現的風格,這裏放到最外層就能實現咱們想要的效果是否是很 Happy?
// 導入用的依賴
import 'package:flutter/material.dart';
// main函數使用了(=>)符號, 這是Dart中單行函數或方法的簡寫
void main() => runApp(new MyApp());
// 該應用程序繼承了 StatelessWidget,這將會使應用自己也成爲一個widget。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'home', // 默認標題
home: new HomePage(), // 返回的界面
);
}
}
複製代碼
細心的同窗已經發現,咱們用到的 widget 發現有,StatelessWidget 和 StatefulWidget , 他們的區別以下:
Stateless widgets 是不可變的, 這意味着它們的屬性不能改變 - 全部的值都是最終的.
Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少須要兩個類:
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
}
複製代碼
Scaffold 是 Material library 中提供的一個widget, 它提供了默認的導航欄、標題和包含主屏幕widget樹的body屬性
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return new DefaultTabController(
child: new Scaffold(
appBar: , // 標題
body: ,//主屏幕
drawer: ,//抽屜
bottomNavigationBar: ,// Tab欄
),
);
}
}
複製代碼
添加標題比較簡單,他的屬性很少,我這裏只添加 appbar 的名稱屬性,由於咱們後邊須要添加導航欄,標題名稱會發生改變,我這裏實現代碼以下:
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return new DefaultTabController(
child: new Scaffold(
appBar: new AppBar(
title: _getTitle(),// 抽取成方法
), // 標題
body: ,//主屏幕
drawer: ,//抽屜
bottomNavigationBar: ,// Tab欄
),
);
}
// 方法
_getTitle() {
switch (index) {
case 0:
return _forMatchTitle('電影');
case 1:
return _forMatchTitle('圖書');
case 2:
return _forMatchTitle('音樂');
}
}
//獲取標題的樣式
_forMatchTitle(String data) {
return new Text(data);
}
}
複製代碼
添加抽屜,就直接在 中對應的屬相添加組件便可。
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return new DefaultTabController(
child: new Scaffold(
appBar: new AppBar(
title: _getTitle(),// 抽取成方法
), // 標題
body: ,//主屏幕
drawer: ,//抽屜
bottomNavigationBar: bottomNavigationBar: new BottomNavigationBar(
onTap: _selectPosition,
currentIndex: index,
type: BottomNavigationBarType.fixed,
iconSize: 24.0,
items: new List<BottomNavigationBarItem>.generate(3, (index) {
switch (index) {
case 0:
return new BottomNavigationBarItem(
icon: new Icon(Icons.movie), title: new Text('電影'));
case 1:
return new BottomNavigationBarItem(
icon: new Icon(Icons.book), title: new Text('圖書'));
case 2:
return new BottomNavigationBarItem(
icon: new Icon(Icons.music_note), title: new Text('音樂'));
}
})),// Tab欄
),
);
}
//獲取選中的tab 索引
_selectPosition(int index) {
if (this.index == index) return;
setState(() {
this.index = index;
});
}
}
複製代碼
因爲篇幅,還剩下主內容、抽屜、輪播圖的實現,我就不一一說明了,具體內容我放到了github上,搜索 studylifetime/flutter_demo 就不往文章上貼代碼了。具體實現詳情能夠查看源碼,裏邊註釋比較清楚。
對於 Android 開發人員來講,入門比較簡單,dart 與java 很是相似,語言這一關很好過,熟悉一下界面開發,即可快速上手開發了。可是Flutter 如今還不適合商業項目的開發, 平時使用的微信支付、登陸,推送消息,bugly 錯誤上報,這些都須要國內的廠商來適配,推送、錯誤上報、分享若是從頭作一遍的話,會牽扯公司很大精力。