Flutter七日遊第一天:2018-12-16 天氣:冷
做爲一名資深安卓業餘愛好者(自誇),感受應該入一下Flutter的坑了,
無論怎麼說,新技術多少要了解一點,本系列就做爲個人學習筆記吧
先把今天入坑的感受寫一寫:java
1.環境的搭建前人把雷踩得差很少了,也不是很麻煩
2.什麼都沒幹呢,TM安裝包28M...真把我嚇一跳-----因而Flutter的"胖子"形象深刻我心(不過非debug版能在10M以內,仍是能夠接受的)
3.Flutter熱加載爽到爆,對於喜歡用真機的我,之前每次修改後-->肯定安裝-->打開...
4.單引號亮了,總算能像寫其餘語言那樣少按個Shift了,字符串插值也很良心
5.flutter支持canvas,so個人四大戰將(canvas,path,paint,貝塞爾)又能大顯身手了,不過Api略有不一樣,也略顯單薄
6.程序員有三件法寶:Ctrl+ Z(大膽改) , debug(細心查) , 類比(善分析)
複製代碼
Android 的SDK要在環境變量配置一下:
ANDROID_HOME
有什麼問題能夠在cmd用flutter doctor
命令檢查一下,對症下藥android
git clone -b beta https://github.com/flutter/flutter.git
複製代碼
PUB_HOSTED_URL=https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
複製代碼
setting-->plugins-->下方第二個-->搜索-->安裝-->重啓
複製代碼
打開AS後就能看到新建一個Flutter項目,而後就寫名字
initializing gradle 若是一直不動,android/gradle/wrapper/gradle-wrapper.properties
對應的gradle版本在http://services.gradle.org/distributions/
本身下載,放在本地ios
android:我最熟悉的android
|---app
|---src
ios:暫時不鳥它
lib:
|---main.dart
test:顧名思義,測試包
.gitignore .metadata .packages pubspec.lock pubspec.yaml README.md 連包都沒有,暫時不睬
複製代碼
android/app/src/main/AndroidManifest.xml
<application
android:name="io.flutter.app.FlutterApplication"
android:label="my_flutter"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
//略...
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
複製代碼
讓我有一種libgdx的即視感git
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
複製代碼
/**
* Generated file. Do not edit.
自動生成的文件,不要修改
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {//如已結婚,直接走人
return;
}
}
//貌似是判斷是否已經和registry結成連理
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {//有結婚戒指,直接走人
return true;
}
registry.registrarFor(key);//帶上戒指
return false;
}
}
複製代碼
好吧在:main.dart裏程序員
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
------------//心裏戲--------------
開面相對象的天眼一看:`void main() => runApp(MyApp());`
什麼鬼,不像Python,不像JavaScript,更不像Java,但我彷彿知道它想對我什麼:
我是入口函數,執行runApp函數,裏面傳入了個MyApp(),so,我是清白的,熊孩子是MyApp()
複製代碼
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
------------//心裏戲--------------
看到class有種他鄉遇故知的感受,繼承了StatelessWidget類並重寫了其build方法
而後返回了一個Widget對象,並能夠推理出MaterialApp()是一個Widget類對象
其中括號裏的感受很是像Python的字典或JavaScript的對象,不過用()包起來真怪怪的
按照通常的套路,左邊是屬性,右邊是屬性值,既然如此,玩玩唄.下面改了一下theme顏色
home裏傳入了一個MyHomePage,估計就是咱們要找的人了,title改一下
複製代碼
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
------------//心裏戲--------------
MyHomePage也是StatefulWidget家的,第一句話感受挺詭異,先mark一下
super(key: key)應該是說,key用它爸(即StatefulWidget)的,從上一步的入參title來看
this.title應該是入參的關鍵,so,這句話好像在說,我要兩個參數,key從我爸那裏拿
@override能夠看出createState()是一個父類方法,_MyHomePageState是一個類
也就說明 _MyHomePageState()是一個對象,(ps:看到State直接想到React)
複製代碼
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;//定義變量
void _incrementCounter() {
setState(() {
_counter++;//定義變量++
});
}
------------//心裏戲--------------
//結合JS和Python的經驗,從這裏能夠看出,貌似加_的,是不想暴露在外的內部成員
_incrementCounter()顯然是一個累加的方法,setState()裏的東西讓_counter++
setState個React這是如出一轍,mark一下,估計會刷新界面
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
------------//心裏戲--------------
abstract class State<T extends StatefulWidget> extends Diagnosticable
State有一個StatefulWidget的泛型,也重寫了build方法
[class Scaffold extends StatefulWidget] Scaffold也是StatefulWidget
如今焦點應該匯聚在StatefulWidget身上,不少地方都出現了,mark一下
Text(widget.title)----這裏應該就是標題了,AppBar,顧名思義
body應該是身體,Center,中間,child,孩子,Column列,mainAxisAlignment,主軸對齊,
center中間,children孩子,Text文字:You have pushed the button this many times:
感受蠻好玩的,拼在一塊兒大概是,列做爲孩子居中,並將文本做爲孩子主軸對齊方式居中
複製代碼
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
//onPressed:點擊響應的函數 tooltip--長按顯示文字 child--Icon加號圖片
複製代碼
ok,這就是我第一次看Flutter代碼時的感受,mark了三處, 下面帶着問題正式學一下Dart方言。github
const PI = 3.141592654; //const:編譯時就是常量
//const double PI = 3.141592654;
final x = 50; //final修飾的變量只能被賦值一次(運行時)
//final int x = 100;
main() {
// int radius = 10;
var radius = 10;
//radius = 10.0;//Error--A value of type 'double' can't be assigned to a variable of type 'int'. double c = getC(radius); //支持三目運算符 bool isBig = c > x; print(isBig ? "圓的周長大於${x}" : r"圓的周長\n小於${x}"*2); //x=100 圓的周長\n小於${x}圓的周長\n小於${x} //x=50 圓的周長大於50 } // 獲取圓的周長 radius : 半徑 double getC(int radius) { var c = 2 * PI * radius; return c; } 複製代碼
1.感受const就像英雄天生天賦,final就像等級到了,選擇英雄職業(不能轉職)
2.r會將裏面字符串原樣打出,無視各空白符
3.字符串*2就打印兩次,有點意思,差值表達式:${}
和JS,kotlin類似
4.能夠省略類型,可是若初始時賦值就不能再賦值其餘類型,因此Dart並不是弱類型語言!!!
但說它強又不怎麼嚴謹,看下圖,無力吐槽...(PS:緣由:見後面dynamic類型)編程
支持多類型,API比java多一些
能夠當作Java的ArrayList和數組的結合體,any,join等操做更像Python或js中的listcanvas
void baseUse() {
var list = [1, "a", "b", "c", true]; //支持多種類型
// var list=const[1,"a","b","c",true];
// var list =new List();
list[0] = "10"; //數組元素可修改爲不一樣類型
var el = list[list.length - 1]; //獲取--true
list.add("toly"); //尾增--[10, a, b, c, true, toly]
list.insert(1, true); //定點增--[10, true, a, b, c, true, toly]
list.remove("10"); //刪除元素--[true, a, b, c, true, toly]
list.indexOf(true); //首出索引--1
list.lastIndexOf(true); //尾出索引--4
list.removeLast(); //移除尾--[true, a, b, c, true]
print(list.sublist(2)); //截取--[b, c, true]
print(list.sublist(2, 4)); //截取--[b, c]
print(list);
print(list.join("!")); //true!a!b!c!true
}
複製代碼
void op() {
var numList = [3, 2, 1, 4, 5];
numList.sort();
print(numList); //排序--[1, 2, 3, 4, 5]
for (var value in numList) {
print(value); //1,2,3,4,5
}
numList.forEach(addOne); //2,3,4,5,6
numList.forEach((num) => print(num + 1)); //同上
var any = numList.any((num) => num > 3);
print(any); //只要有>3的任何元素,返回true
var every = numList.every((num) => num < 6);
print(every); //所有元素<6,返回true
var listX5 = numList.map((e) => e*=5);
print(listX5);//(5, 10, 15, 20, 25)
}
int addOne(int num) {
print(num + 1);
}
複製代碼
這個很少說了,基本上與主流語言一致api
void baseUse() {
//建立映射表
var dict = {"a": "page1", "b": "page30", "c": "page70", "price": 40};
// var dict = new Map();
print(dict); //{a: page1, b: page30, c: page70, price: 40}
print(dict["price"]); //40
dict["a"] = "page2";
print(dict); //{a: page2, b: page30, c: page70, price: 40}
print(dict.containsKey("price")); //true
print(dict.containsValue("price")); //false
print(dict.isEmpty); //false
print(dict.isNotEmpty); //true
print(dict.length); //4
dict.remove("c");
print(dict);//{a: page2, b: page30, price: 40}
}
複製代碼
void op() {
var dict = {"a": "page1", "b": "page30", "c": "page70", "price": 40};
dict.keys.forEach(print); //a,b,c,price
dict.values.forEach(print); //a,b,c,price
dict.forEach((k, v) => (print("$k=$v"))); //這裏用括號包着,好想吐槽...
}
複製代碼
原來是dynamic鍋,讓類型變成動態了數組
dynamic d = 20;
d = "toly";
var list = new List<dynamic>();
list.add("1");
list.add(3);
var list2 = new List<int>();
//list2.add("toly");//ERROR:The argument type 'String' can't be assigned to the parameter type 'int'. 複製代碼
//--------------------奇葩的~/----------
int a=10;
print(a/3);//3.3333333333333335
print(a~/3);//3
//--------------------奇葩的??=----------
int b = 9;
b = 5;
b ??= a; //----若是b空的則賦值
print(b); //5
//--------------------奇葩的??----------
int c = 10;
int d = 8;
var add10 = c = null ?? d + 10;//取第一個不爲空的表達式
print(add10); //18
//--------------------簡潔的=>----------
=> expr 等價於 {return expr;}
//--------------------好玩的{參數}----------
main() {
fun("toly");//toly,24,null
fun("toly", age: 24, sex: "男"); //toly,24,男
}
fun(String name, {int age=24, String sex}) {
print("$name,$age,$sex");
}
//--------------------好玩的[參數]----------
main() {
fun("toly"); //toly,null,null
fun2("toly", 24); //toly,24, 男
}
fun2(String name, [int age, String sex= "男"]) {
print("$name,$age,$sex");
}
//--------------------有趣的匿名方法----------
var power = (i) {
return i * i;
};
print(power(6)); //36
//--------------------這個理清楚,基本上匿名函數就OK了----------
var li = [1, 2, 3, 4, 5];
li.forEach((i) => print((i) {
return i * i;
}(i))); //1,4,9,16,25
複製代碼
PerSon(this.name, this.age)
簡化了Java中的那一坨,其餘差很少
class PerSon {
String name;
int age;
PerSon(this.name, this.age);
say(String name) {
print("are you ok $name");
}
}
main(){
var toly = new PerSon("toly", 24);
toly.say("ls");//are you ok ls
}
複製代碼
注意語法形式
class Student extends PerSon {
String school;
Student(String name, int age, this.school) : super(name, age);
}
main() {
new Student("ls", 23, "星龍學院").say("toly");//are you ok toly
}
複製代碼
就先認知這麼多吧,應該夠玩一玩的了。
新學同樣東西,最好選擇最熟悉的點切入,對我而言是繪製
有個
CustomPainter
類裏有canvas,二話不說,繼承之,爲了不看着亂,我新建了view包view/star_view.dart
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StarView extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
複製代碼
前面分析過,視圖的呈如今MyHomePage中-->createState方法-->build返回的對象裏
把文字的那塊body改成CustomPaint就好了,FloatingActionButton就放着吧。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: CustomPaint(
painter: StarView(),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
複製代碼
flutter中用的單位目測都是dp因此我用第三行那個,須要傳入一個context
就在構造方法裏傳一下,恰好build裏有個context,你用前兩個除一下也行
window.physicalSize //獲取屏幕尺寸px----1080.0, 2196.0
window.devicePixelRatio //設備像素比----3
MediaQuery.of(context).size //得到的是dp單位:360.0, 732.0
複製代碼
//使用是傳入context
body: CustomPaint(
painter: StarView(context),
),
複製代碼
Paint mHelpPaint;
BuildContext context;
StarView(this.context) {
mHelpPaint = new Paint();
mHelpPaint.style=PaintingStyle.stroke;
mHelpPaint.color=Color(0xffBBC3C5);
mHelpPaint.isAntiAlias=true;
}
複製代碼
之前Android裏面用的函數,修改了一些語法,給flutter用
/**
* 繪製網格路徑
*
* @param step 小正方形邊長
* @param winSize 屏幕尺寸
*/
Path gridPath(int step, Size winSize) {
Path path = new Path();
for (int i = 0; i < winSize.height / step + 1; i++) {
path.moveTo(0, step * i.toDouble());
path.lineTo(winSize.width, step * i.toDouble());
}
for (int i = 0; i < winSize.width / step + 1; i++) {
path.moveTo(step * i.toDouble(), 0);
path.lineTo(step * i.toDouble(), winSize.height);
}
return path;
}
複製代碼
@override
void paint(Canvas canvas, Size size) {
var winSize = MediaQuery.of(context).size;
canvas.drawPath(gridPath(20, winSize), mHelpPaint);
複製代碼
//繪製座標系
drawCoo(Canvas canvas, Size coo, Size winSize) {
//初始化網格畫筆
Paint paint = new Paint();
paint.strokeWidth = 2;
paint.style = PaintingStyle.stroke;
//繪製直線
canvas.drawPath(cooPath(coo, winSize), paint);
//左箭頭
canvas.drawLine(new Offset(winSize.width, coo.height),
new Offset(winSize.width - 10, coo.height - 6), paint);
canvas.drawLine(new Offset(winSize.width, coo.height),
new Offset(winSize.width - 10, coo.height + 6), paint);
//下箭頭
canvas.drawLine(new Offset(coo.width, winSize.height-90),
new Offset(coo.width - 6, winSize.height - 10-90), paint);
canvas.drawLine(new Offset(coo.width, winSize.height-90),
new Offset(coo.width + 6, winSize.height - 10-90), paint);
}
複製代碼
座標系路徑
/**
* 座標系路徑
*
* @param coo 座標點
* @param winSize 屏幕尺寸
* @return 座標系路徑
*/
Path cooPath(Size coo, Size winSize) {
Path path = new Path();
//x正半軸線
path.moveTo(coo.width, coo.height);
path.lineTo(winSize.width, coo.height);
//x負半軸線
path.moveTo(coo.width, coo.height);
path.lineTo(coo.width - winSize.width, coo.height);
//y負半軸線
path.moveTo(coo.width, coo.height);
path.lineTo(coo.width, coo.height - winSize.height);
//y負半軸線
path.moveTo(coo.width, coo.height);
path.lineTo(coo.width, winSize.height);
return path;
}
複製代碼
感受flutter裏的Canvas很貧弱...好多api都沒有,不知道是我沒找到仍是什麼
canvas居然沒辦法畫文字,這不科學,mark一下。座標系也就只能這樣湊合一下了
還有Color用着挺彆扭的,畫線傳參爲何非要Offset,連個重載都沒有
好吧,我又要拿星星來丟人現眼了
我已經n角星的java代碼翻譯成dart方言了
/**
* n角星路徑
*
* @param num 幾角星
* @param R 外接圓半徑
* @param r 內接圓半徑
* @return n角星路徑
*/
Path nStarPath(int num, double R, double r) {
Path path = new Path();
double perDeg = 360 / num; //尖角的度數
double degA = perDeg / 2 / 2;
double degB = 360 / (num - 1) / 2 - degA / 2 + degA;
path.moveTo(cos(_rad(degA)) * R, (-sin(_rad(degA)) * R));
for (int i = 0; i < num; i++) {
path.lineTo(
cos(_rad(degA + perDeg * i)) * R, -sin(_rad(degA + perDeg * i)) * R);
path.lineTo(
cos(_rad(degB + perDeg * i)) * r, -sin(_rad(degB + perDeg * i)) * r);
}
path.close();
return path;
}
double _rad(double deg) {
return deg * pi / 180;
}
複製代碼
canvas.translate(160, 320);//移動到座標系原點
canvas.drawPath(nStarPath(5,80,40), mPaint);
複製代碼
/**
* 畫正n角星的路徑:
*
* @param num 角數
* @param R 外接圓半徑
* @return 畫正n角星的路徑
*/
Path regularStarPath(int num, double R) {
double degA, degB;
if (num % 2 == 1) {
//奇數和偶數角區別對待
degA = 360 / num / 2 / 2;
degB = 180 - degA - 360 / num / 2;
} else {
degA = 360 / num / 2;
degB = 180 - degA - 360 / num / 2;
}
double r = R * sin(_rad(degA)) / sin(_rad(degB));
return nStarPath(num, R, r);
}
/**
* 畫正n邊形的路徑
*
* @param num 邊數
* @param R 外接圓半徑
* @return 畫正n邊形的路徑
*/
Path regularPolygonPath(int num, double R) {
double r = R * cos(_rad(360 / num / 2)); //!!一點解決
return nStarPath(num, R, r);
}
複製代碼
canvas.translate(0, 320);
canvas.save();//繪製n角星
for (int i = 5; i < 10; i++) {
canvas.translate(64, 0);
canvas.drawPath(nStarPath(i, 30, 15), mPaint);
}
canvas.restore();
canvas.translate(0, 70);
canvas.save();//繪製正n角星
for (int i = 5; i < 10; i++) {
canvas.translate(64, 0);
canvas.drawPath(regularStarPath(i, 30), mPaint);
}
canvas.restore();
canvas.translate(0, 70);
canvas.save();//繪製正n邊形
for (int i = 5; i < 10; i++) {
canvas.translate(64, 0);
canvas.drawPath(regularPolygonPath(i, 30), mPaint);
}
canvas.restore();
複製代碼
第一個按鈕的fab點擊更改數字,這裏換成顏色試一下:
//-----------main.dart-------------------
Color _color = Colors.black;
void _changeColor() {
setState(() {
_color=randomRGB();
});
}
body: CustomPaint(
painter: StarView(context,_color),
),
floatingActionButton: FloatingActionButton(
onPressed: _changeColor,
tooltip: 'Increment',
child: Icon(Icons.add),
),
//-----------隨機顏色-------------------
Color randomRGB(){
Random random = new Random();
int r = 30 + random.nextInt(200);
int g = 30 + random.nextInt(200);
int b = 30 + random.nextInt(200);
return Color.fromARGB(255, r, g, b);
}
//-----------star_view.dart-------------------
StarView(this.context,Color color) {
print(color);
mPaint = new Paint();
mPaint.color = color;
}
複製代碼
通過初始項目的分析以及Dart方言的簡單入門,再加上Canvas的繪製
基本上熟悉了Dart的語法與Flutter的套路(和React很像),第一天就這樣吧
1.setState和React這是如出一轍,mark一下,估計會刷新界面
----通過測試,是的,調用setState會從新繪製界面,和React同樣
複製代碼
2.MyHomePage也是StatefulWidget家的,第一句話感受挺詭異,先mark一下
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
----根據繼承的語法以及{}的可選參數,不難理解,key是從老爸那拿的
複製代碼
3.如今焦點應該匯聚在StatefulWidget身上,不少地方都出現了,mark一下
---保持mark
4.canvas居然沒辦法畫文字,這不科學,mark一下
---保持mark
複製代碼
項目源碼 | 日期 | 備註 |
---|---|---|
V0.1-github | 2018-12-16 | Flutter第1天--初始分析+Dart方言+Canvas簡繪 |
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
個人github | 個人簡書 | 個人掘金 | 我的網站 |
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持