上週個人一位微信好友問我有沒有學Flutter
,我回答說還沒真正學,他說應該要接觸一下。對於新技術的誕生,我始終保持敬畏之心,和另外一位大學舍友聊了當時如何入坑Android
的經歷,才發現本身的學習方式和路線有不少的問題,知識點很零亂,知識沒有系統化,很少說了,後面學習新的知識必定要從「碎片化」到「總體化」。2018年2月,在世界移動大會上,Google發佈了Flutter的第一個beta版本,2018年6月11日發佈首個預覽版,在2018年12月05日北京時間凌晨1點45分,在Flutter Live上,谷歌Flutter團隊推出Flutter1.0,Flutter1.0版本是UI工具包的第一個穩定版本,在2019年2月27日世界移動通訊大會上Google推出1.2版本,帶來全新的Web開發工具。另外今日頭條團隊即將開源讓Flutter真正支持View級別的混合開發(上層Flutter Framework引入Widget/LayerTree等概念本身實現了界面描述框架,下層Flutter Engine把LayerTree用OpenGL渲染成用戶界面),閒魚團隊也開源了基於 Redux數據管理的組裝式Flutter應用框架。什麼是Flutter呢?Flutter是一個跨平臺的免費開源的移動UI框架,是Google的移動應用SDK,用於在極短期內在iOS和Android平臺上建立高質量的原生體驗,簡而言之就是在iOS下和Android下共用一套代碼,一套代碼就能在兩個操做系統下運行,其官方編程語言是是Dart,學習這門語言很快就上手。Flutter提供不少豐富的UI組件庫,開發者能夠快速開發出靈活的UI界面,另外Flutter已經加入Material Design組件你們庭中,也就是說Flutter可使用Material Theming和Material 組件,能夠相信將來會有更多的創意設計UI風格會涌現。java
class CounterState extends State<Counter> {
int counter = 0;
void increment() {
// Tells the Flutter framework that state has changed,
// so the framework can run build() and update the display.
setState(() {
counter++;
});
}
Widget build(BuildContext context) {
// This method is rerun every time setState is called.
// The Flutter framework has been optimized to make rerunning
// build methods fast, so that you can just rebuild anything that
// needs updating rather than having to individually change
// instances of widgets.
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: increment,
child: new Text('Increment'),
),
new Text('Count: $counter'),
],
);
}
}
複製代碼
Future<Null> getBatteryLevel() async {
var batteryLevel = 'unknown';
try {
int result = await methodChannel.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level: $result%';
} on PlatformException {
batteryLevel = 'Failed to get battery level.';
}
setState(() {
_batteryLevel = batteryLevel;
});
}
複製代碼
2019年一月底,Flutter團隊公佈了2019年Flutter的產品線,爲如下幾點肯定了明確的計劃:android
web
應用。 而且Flutte UX研究團隊會按期根據用戶反饋來助力打造更優的Flutter,根據用戶反饋來調整開發重點,可見Flutter UX團隊的用心和付出程度,今年拭目以待。Flutter框架從到下包含三部分:函數式響應的Framework(Dart),Engine(C++),Embedder(Platform Specific)。下面直接上圖: ios
務必電腦安裝git,本文是在Windows環境下配置的,MAC下配置環境流程是一致的。首先要獲取Flutter SDK,使用git去克隆倉庫而後添加Flutter工具到本身的環境變量,運行flutter doctor來顯示剩下須要安裝的依賴,國內用戶最好配置一下兩個環境變量:git
PUB_HOSTED_URL=https://pub.flutter-io.cn
複製代碼
以下圖所示: github
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
複製代碼
如圖所示: web
$ git clone -b beta https://github.com/flutter/flutter.git
複製代碼
如圖所示: 編程
打開一個新的命令提示符窗口,運行下面命令flutter doctor,看是否須要安裝任何依賴項來完成安裝,這個命令會檢測環境和在終端生成報告,Dart SDK和Flutter捆綁在一塊兒,不必單獨去安裝Dart。初次運行它會下載本身的依賴庫而且自行編譯,可能比較慢。後續運行flutter命令就會很快。這裏注意,若是CMD窗口顯示亂碼問題,下面是解決方案:數組
HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe
若是該項下已存在CodePage項,則把值改成十進制」65001」,若是不存在,在該項下新建一個 DWORD(32位值),命名爲「CodePage」,值設爲「65001」flutter doctor --android-licenses
一直按y便可。最後從新輸入flutter doctor
,結果以下圖:
在Android Studio--File--Settings --plugins搜索Flutter便可,以下圖: 服務器
由於如今是要建立應用程序,所以在Create New Flutter Project
下選擇Flutter Application
: 微信
在下一個頁面輸入項目名字,配置Flutter SDK目錄,項目存放的路徑,項目的描述,公司的域名,項目的包名如圖所示:
項目創建完成後,編輯器給咱們生成的目錄以下:
整體來看和原生Android的工程結構不同了,由於代碼都是在lib目錄完成的,因此不能用Android多module多lib結構去建立module和lib,除非要用到原生交互的代碼,能夠在android目錄裏面去寫,而後在lib目錄裏面去引用。相對Android開發者而言,多了ios目錄,ios開發者而言,多了android目錄,其餘文件上面有具體詳細說明。由於lib目錄是開發者最主要關注的,打開lib目錄,發現有個後綴是dart的文件,裏面內容都是用dart語法來寫的,還發現有void main() => runApp(MyApp());
main()函數對於學過java而言都很是清晰,這個應該是入口函數,另外發現StatelessWidget,StatefulWidget一些小控件,這裏想應該是UI組件吧。這裏先無論那麼多,直接點擊運行圖標,運行效果圖以下:
如今仍是按照新手走,先本身擼個「Hello world」出來吧。把main.dart中全部代碼去掉,替換下面代碼,屏幕中心顯示Hello World
:
import 'package:flutter/material.dart';
//這個是Dart中單行函數或者方法的簡寫
void main() => runApp(MyApp());
//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
// 這個是應用的根widget
@override
Widget build(BuildContext context) {
//注意:一個app只能有一個MaterialApp
return MaterialApp(
//標題欄的名字
title: 'Hello Flutter',
//這個是Material library提供的一個widget,它提供了默認的導航欄、標題欄
//包含主屏幕的widget樹的body屬性
home:new Scaffold(
appBar:new AppBar(
title:const Text("Weclome to Flutter"), ), body:const Center( child:const Text("Hello World"), ), ), );
}
}
複製代碼
Android模擬器運行效果以下:
import 'dart:io';
import 'package:flutter/services.dart';
複製代碼
//入口函數
void main() {
//MaterialApp組件渲染後
runApp(MyApp());
//判斷若是是Android版本的話 設置Android狀態欄透明沉浸式
if(Platform.isAndroid){
//寫在組件渲染以後,是爲了在渲染後進行設置賦值,覆蓋狀態欄,寫在渲染以前對MaterialApp組件會覆蓋這個值。
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
}
複製代碼
還有另一種方法,由於Flutter主入口只有一個MainActivity,也就是說全部的Flutter頁面都會運行這個MainActivity,那咱們只須要在這個主入口判斷一下版本號而後將狀態欄顏色設置成透明,具體位置在android->app->src->main->xxx->MainActivity
:
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//設置狀態欄透明
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
{//API>21,設置狀態欄顏色透明
getWindow().setStatusBarColor(0);
}
GeneratedPluginRegistrant.registerWith(this);
}
}
複製代碼
最終效果Android和iOS下運行以下:
appBar:new AppBar(
//ios和android標題欄統一在中間
title:new Center(child :const Text("Weclome to Flutter")), ), 複製代碼
最終效果以下:
如今,經過在pubspec.yaml文件配置依賴項,去依賴一個提供英文單詞的包,在pub.dartlang.org/flutter/這個網站能夠找到不少開源軟件包,能夠搜索指定的軟件包查看對應版本。
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
english_words: ^3.1.5
複製代碼
flutter packages get
in flutter_demo...
//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
// 這個是應用的根widget
@override
Widget build(BuildContext context) {
//隨機生成函數
var wordPair = new WordPair.random();
return MaterialApp(
//標題欄的名字
title: 'Hello Flutter',
//這個是Material library提供的一個widget,它提供了默認的導航欄、標題欄
//包含主屏幕的widget樹的body屬性
home:new Scaffold(
appBar:new AppBar(
//ios和android標題欄統一在中間
title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( //使用隨機生成的英文單詞 爲何不能const呢 由於 內容發生變化 const是用來修飾常量的 child:new Text(wordPair.asPascalCase), ), ), );
}
}
複製代碼
注意的是Center widget 和 Text widget要用new
來建立,由於內容修改了,並非常量了,這時候按保存就能夠看到新的單詞出如今屏幕中間文本了。
上面MyApp是繼承StatelessWidget,StatelessWidget是無狀態的也是不可控的,意思是其屬性是不能改變的,全部的值都是最終的。在平時開發中,不少控件都須要根據特定場景改變自身的狀態,那麼Flutter有沒有提供有狀態的widget呢?答案確定是有的,Statefulwidget是有狀態的,在其生命週期保持的狀態可能會變化,實現一個有狀態的widget至少須要兩個類:StatefulWidgets類和State類。
main.dart
文件最底下://建立有狀態的widget
class RandomWordsWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return new RandomWordsState();
}
}
複製代碼
//用來保存RandomWords widget的狀態
class RandomWordsState extends State<RandomWordsWidget>{
@override
Widget build(BuildContext buildContext){
var wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
複製代碼
//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
// 這個是應用的根widget
@override
Widget build(BuildContext context) {
return MaterialApp(
//標題欄的名字
title: 'Hello Flutter',
//這個是Material library提供的一個widget,它提供了默認的導航欄、標題欄
//包含主屏幕的widget樹的body屬性
home:new Scaffold(
appBar:new AppBar(
//ios和android標題欄統一在中間
title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( child:new RandomWordsWidget() ), ), );
}
複製代碼
這時候運行的效果仍是跟以前同樣,不過實現方式不同。
下面建立一個滑動組件ListView,開發者接觸最多應該是這個滑動組件,下面實現當用戶滑動列表的時候,ListView會不斷增加,不斷顯示新的單詞。
final _normalWords = <WordPair>[];
複製代碼
_buildNormalWords
方法,用於構建一個ListView,ListView提供了itemBuilder屬性,這是一個工廠builder做爲匿名函數進行回調,這個函數須要傳入兩個參數,一個是BuildContext上下文和行迭代器。對於ListView每一行都會執行這個函數調用,這裏想一想好像是平時Android開發中ListView建立添加item的方法。//建立填充單詞的ListView
Widget _buildNormalWorlds() {
//內容上下16dp
return new ListView.builder(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
//每一個單詞都會條約一次itemBuilder,而後將單詞添加到ListTile中
itemBuilder: (context, i) {
//首先建立10條單詞
if (i >= _normalWords.length) {
//接着再生成10個單詞,添加到列表上
_normalWords.addAll(generateWordPairs().take(10));
}
return _buildItem(_normalWords[i]);
});
}
複製代碼
//設置每一個item項的內容和樣式
Widget _buildItem(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
textAlign: TextAlign.center,
),
);
}
複製代碼
build
方法,增長_buildNormalWords
方法的調用,不是直接用單生成庫,代方法以下:Widget build(BuildContext buildContext) {
// var wordPair = new WordPair.random();刪掉
// return new Text(wordPair.asPascalCase);刪掉
return new Scaffold(
appBar: new AppBar(
title: new Center(child: const Text("Weclome to Flutter")), ), body:_buildNormalWorlds(), );
}
複製代碼
build
方法,由於標題的設置放在了RandomWordsState裏了,因此不須要再額外添加,並將home變成RandomWords widget,代碼一會兒簡潔不少以下://程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
// 這個是應用的根widget
@override
Widget build(BuildContext context) {
return MaterialApp(
home:new RandomWordsWidget(),
);
}
}
複製代碼
最終效果如圖所示
_buildNormalWorlds
方法裏判斷是奇數項的話就構造出分割線添加到ListView,這裏注意分割線也是一項,那麼生成單詞的時候要稍微處理下,具體代碼以下://建立填充單詞的ListView
Widget _buildNormalWorlds() {
//內容上下16dp
return new ListView.builder(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
//每一個單詞都會條約一次itemBuilder,而後將單詞添加到ListTile中
itemBuilder: (context, i) {
//添加分割線
//奇數行,會添加一個分割線的widget,分割上下單詞
//偶數行,就正常構造添加ListTitle row
//構造一像素的分割線 也能夠本身去定義寬高
//i.isOdd判斷是否奇數,奇數行添加分割線
if(i.isOdd){
//注意:這裏執行後會跳出循環
return new Divider();
}
//這裏對2求商,就是計算出ListView中減去分割線後的實際單詞數量
//如 i爲 1,2,3,4,5,那麼商結果是0,1,1,2,2
//1 除以 2商是0 2除以2商是1 以此類推
final int index = i ~/2;
//首先建立10條單詞
if (index >= _normalWords.length) {
//接着再生成10個單詞,添加到列表上
_normalWords.addAll(generateWordPairs().take(10));
}
return _buildItem(_normalWords[index]);
});
}
複製代碼
實際效果以下:
_collected
的Set集合,這個集合用來存放收藏後的單詞,用Set的緣由是Set自己的特性不容許元素有重複值:final Set<WordPair> _collected = new Set<WordPair>();
複製代碼
_buildItem
方法中添加isCollected
來檢查單詞是否添加到收藏裏final bool isCollected = _collected.contains(pair);
複製代碼
_buildItem
之後置屬性來添加一個❤️圖標到ListTiles,matrial包下有默認的❤️圖標,這裏就設置下顏色就能夠,代碼以下:return new ListTile(
title: new Text(
pair.asPascalCase,
textAlign: TextAlign.center,
),
//trailing 是後置圖標屬性
trailing: new Icon(
//material 包下 icons.dart
isCollected ? Icons.favorite : Icons.favorite_border,
//圖標顏色設置
color:isCollected ? Colors.red : null,
),
);
複製代碼
運行結果後發現❤️型圖標添加到每一行最右邊處。
//trailing 是後置圖標屬性
trailing: new Icon(
//material 包下 icons.dart
isCollected ? Icons.favorite : Icons.favorite_border,
//圖標顏色設置
color:isCollected ? Colors.red : null,
),
//item的點擊事件屬性
onTap:(){
//狀態設置
setState(() {
//若是收藏了
if(isCollected){
//那就將set集合裏移除
_collected.remove(pair);
} else {
//添加
_collected.add(pair);
}
});
}
複製代碼
效果以下:
Widget build(BuildContext buildContext) {
return new Scaffold(
appBar: new AppBar(
title: new Center(child: const Text("Weclome to Flutter")), //增長圖標和點擊事件動做 Icons.list是icon的類型 onPressed添加點擊事件 點擊會執行_collectWordsPage方法 actions:<Widget>[ new IconButton(icon: const Icon(Icons.list),onPressed: _collectWordsPage), ], ), body:_buildNormalWorlds(), );
}
複製代碼
實際運行後,AppBar導航欄最右邊的位置添加了一個圖標,這時候點擊沒有任何反應,由於_collectWordsPage沒有任何代碼,下面添加跳轉到新頁面的實現。
//跳轉新頁面開始
void _collectWordsPage(){
Navigator.of(context).push(
);
}
複製代碼
toList
來轉換。MaterialPageRoute
是一種模態路由,能夠經過平臺自適應來切換屏幕,Android而言,頁面推送過渡向上滑動頁面,淡入淡出,彈出過渡則是向下滑動頁面。IOS而言,頁面從右側滑入,反向彈出,當另外一個頁面進入覆蓋時,該頁面向左移動。//跳轉新頁面開始
void _collectWordsPage() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
//傳遞收藏後的單詞
final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase, //單詞
),
);
},
);
},
),
);
}
複製代碼
Navigator.pop
,點擊返回按鈕會返回到主界面,代碼以下://跳轉新頁面開始
void _collectWordsPage() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
//傳遞收藏後的單詞
final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase, //單詞
),
);
},
);
//添加分割線開始
final List<Widget> divided = ListTile.divideTiles(
tiles: tiles,
context:context,
).toList();
return new Scaffold(
appBar: new AppBar(
title: new Center(child: const Text("Collect Words")), ), body: new ListView(children: divided), );
},
),
);
}
複製代碼
最終運行效果發現標題並非居中對齊,由於默認的有邊距,這時候須要自定義AppBar就能夠,代碼以下:
return new Scaffold(
appBar: new AppBar(
titleSpacing: 0.0,
//MaterialPageRoute這個自帶了返回鍵 下面的屬性設置取消返回鍵
automaticallyImplyLeading:false,
title: new Container(decoration: new BoxDecoration(color: new Color(0x00000000),
),
child:new Stack(
children: <Widget>[
new Container(
//左邊位置
alignment: Alignment.centerLeft,
child:new IconButton(
//圖標樣式
icon:new Icon(Icons.arrow_back),
//點擊事件
onPressed: (){
Navigator.pop(context);
}
),
),
new Center(child:new Text("Collect Words")),
],
),)
),
body: new ListView(children: divided),
);
複製代碼
最終運行效果以下,左邊是IOS,右邊是Android:
入門Flutter的第一天,簡單知道了一下幾點:
Widget
,它中間層只有C/C++代碼,Flutter
使用Dart語言實現系統的絕大部分功能(佈局,動畫,手勢)。如有錯誤,歡迎指正~