上一節,我們完成了Flutter的環境搭建。本節,我們開始搭建項目
,簡單了解Flutter
及其基礎組件
。
- 項目創建
1.1 命令行創建
1.2 Android Studio創建 - 熟悉工程
2.1 簡單實現
2.2 熟悉widget
2.3 Text
2.4 MaterialApp
2.5 ListView 列表視圖 - 常用基礎組件
3.1 基礎文本
3.2 富文本
3.3 基礎容器Container
1. 項目創建
1.1 命令行創建
以前
不支持
駝峰
寫法,需要通過小寫字母
+下劃線_
命名,但是現在支持
哦
flutter create flutter_demo
- 創建成功
- 按照指令,到
指定文件夾
去運行項目
:
cd FlutterDemo
flutter run
注意:
- 如果此時
未打開模擬器
,會提示需要選擇
一個模擬器
。- 如果此時打開了
多個模擬器
,也會提示您選中一個模擬器
來運行
。
image.png選中
模擬器后,flutter
會自動使用Xcode工具
進行編譯
。
image.png- 如果需要
真機調試
,我們需要手動打開
項目工程
,去Xcode配置證書
終端
運行Flutter命令鍵
:Flutter run key commands. r Hot reload. ?????? 熱重載(比對被修改部分,更新被修改代碼) R Hot restart. 熱重啟(所有資源重新加載) h Repeat this help message. d Detach (terminate "flutter run" but leave application running). c Clear the screen q Quit (terminate the application on the device). s Save a screenshot to flutter.png. b Toggle the platform brightness setting (dark and light mode). (debugBrightnessOverride) w Dump widget hierarchy to the console. (debugDumpApp) t Dump rendering tree to the console. (debugDumpRenderTree) L Dump layer tree to the console. (debugDumpLayerTree) S Dump accessibility tree in traversal order. (debugDumpSemantics) U Dump accessibility tree in inverse hit test order. (debugDumpSemantics) i Toggle widget inspector. (WidgetsApp.showWidgetInspectorOverride) I Toggle oversized image inversion ???. (debugInvertOversizedImages) p Toggle the display of construction lines. (debugPaintSizeEnabled) o Simulate different operating systems. (defaultTargetPlatform) z Toggle elevation checker. g Run source code generators. M Write SkSL shaders to a unique file in the project directory. v Launch DevTools. P Toggle performance overlay. (WidgetsApp.showPerformanceOverlay) a Toggle timeline events for all widget build methods.
- 如果使用
安卓模擬器
,可以在頂部控制欄
配置模擬器
:
image.png選中模擬器,運行:
image.png
- 由于
個人習慣
,我更喜歡
在Xcode
中開發
,在終端
使用命令運行
。執行flutter run
時,會讓我們選擇
想使用的模擬器
(后續我主要使用iPhone模擬器
)。
1.2 Android Studio
創建
- 除了使用
flutter create
命令創建項目,我們還可使用Android Studio
創建,也可以使用VSCode
創建,它們都有flutter插件
。
- 啥? 你問
Xcode
是否可以創建?
想想蘋果
與谷歌
的競爭者關系,就知道蘋果不可能
做這樣的支持
插件的。??
1.2.1 使用Android Studio
創建
-
四種
創建方式:
-
項目
基本信息
image.png -
項目
唯一標識
和支持
的平臺
image.png -
點
Finish
后,會進行網絡請求
,拉取資源
,創建成功。
(如果沒配置
鏡像,拉取資源非常緩慢
。 上一節有介紹如何配置鏡像)
image.png
坑點
如果Android Studio
正在運行
項目,我們command + Q
強制退出。下次打開Android Studio
時,會回到
當時的緩存
。如果緩存
成功找回,會運行正常
。如果緩存
找不到,會導致無法運行,而且新建工程
也無法運行
。處理方式:
刪除
flutter目錄下cache緩存文件夾
中的lockfile
文件,再運行項目
即可。(相當于XCode
的Clean
操作)# pwd請替換為自己flutter的文件目錄 rm /pwd/flutter/bin/cache/lockfile
2. 熟悉工程
-
創建項目
后,可以看到main.dart
有很多代碼
。我們最快熟悉
的方式是:全部刪除
,手動實現
并分析
:
image.png
2.1 簡單實現
Flutter
中,我們使用的開發語言
是Dart
,現在我們先體驗
,完整的Dart語法
,我們可以去官網了解
- 手動實現:
// 以下代碼,均使用Dart語言編寫
// 資源包 (可以理解為我們iOS的UIKit)
import 'package:flutter/material.dart';
// main入口函數 (就像iOS main.m中的main函數)
void main() {
// runApp(app); // runApp就像iOS的 UIApplication, 而app就像我們的根控制器
// flutter中沒有控制器和視圖的概念,都是widget組件。
runApp(
Center( //我們用Center部件,自動居中展示。這是Center的構造函數
// child就像subView
// Text是一種文本框樣式,設置默認值和顯示方向(ltf: left to right 從左到右)
child: Text('Hello', textDirection: TextDirection.ltr,),
)
);
}
- flutter
運行
,文本框成功展示
:
image.png
2.2 熟悉widget
Widget
: 作用類似
于OC中的UIView
,是小部件
。分為兩大類:
StatelessWidget
:無狀態組件。快捷鍵stless
。
創建后
就決定
了樣式,狀態更改需要
手動創建新widget
。StatefulWidget
: 有狀態組件。快捷鍵stful
。
本質
上也是無狀態組件
,渲染
時無狀態
,因為UI本身
是無狀態
的。但它會保留組件狀態
,記錄組件狀態的屬性改變
時,會自動重新渲染
。直到該組件完全銷毀
,才會釋放
記錄的屬性
2.2.1 無狀態組件(StatelessWidget)
- 我們將
main
入口的Center組件
改成自定義
的MyWidget
無狀態組件。
import 'package:flutter/material.dart';
void main() {
runApp(
MyWidget(), //()是構造函數,類似C++。
);
}
// 無狀態Widget快捷鍵是 stless
// 一個Widget,就是一個class類
class MyWidget extends StatelessWidget {
@override
// build:會將小部件放到渲染樹中去
// 渲染樹: 會從main入口的第一個widget開始渲染,然后以樹結構依次向下渲染組件
// 所有widget都必須有build方法,build返回的是什么,MyWidget在外界渲染的就是什么
Widget build(BuildContext context) {
return Center(
child: Text(
'Hello Flutter2',
textDirection: TextDirection.ltr,
),
);
}
}
- 選擇
模擬器
,debug運行
,模擬器啟動后
,可以修改文本內容
,點擊熱重載
按鈕,感受熱重載的強大
。
reload熱重載
功能,是通過比較新舊代碼
變化,來更新
被改動部分代碼的。 但是有些場景
下熱重載
是失敗
的,只能restart重啟
才可以。具體場景,參考Flutter官網介紹
Dart語言簡寫:
- Dart語言中,如果
函數
只有一行內容
時,可以使用=>
縮寫:// 改動前 //void main() { // runApp(MyWidget()); //} // 簡寫: void main() => runApp(MyWidget());
2.3 Text
-
Text
是一個文本組件
,是StatelessWidget
不可變組件。
2.3.1 構造函數與參數
- 以
Text
為例,構造函數是Text()
,包含必選參數
和可選值
,可選值可以賦默認值
:
image.png
2.3.2 final和const修飾符
final
和const
都類似于Swift
的let
,是不可變
的。
-
final
可以不賦初始值,運行時
再賦值
。 -
const
必須在創建
時就賦值
。
比如Text
中使用頻率最高
的style
和textAlign
,都是final
聲明,因為Text
本身是StatelessWidget
不可變的組件。
- 給
Text
加入樣式,使用變量
創建TextStyle
,_
下劃線表示私有變量
import 'package:flutter/material.dart';
// 入口,展示MyWidget組件
void main() => runApp(MyWidget());
class MyWidget extends StatelessWidget {
@override
// build 確定組件返回的內容
Widget build(BuildContext context) {
// final創建一個_textStyle不可變變量,_開頭的屬性,表示私有屬性
final _textStyle = TextStyle(color: Colors.red, fontSize: 40.0, fontWeight: FontWeight.bold);
return Center(
child: Text(
'Hello Flutter',
textDirection: TextDirection.ltr,
style: _textStyle, // 直接使用變量
),
);
}
}
Text
和TextStyle
相關屬性
和參數
,可以Command + 鼠標左鍵
查看
2.4 MaterialApp
-
MaterialApp
:Flutter推薦方式,提供快速構建
APP的方式(包括導航欄
、內容
、主題
等)
home
: 類似于根控制器
,需要指定
一個widget
Scaffold
: 類似于導航控制器
NavigationController,包含導航欄相關屬性
。是一個可變組件
statefulWidget
appBar
:導航欄
body
: 內容theme
:主題
,可控制主題色
import 'package:flutter/material.dart';
// 入口,展示MyWidget組件
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
// MaterialApp:Flutter推薦方式,提供快速構建APP的方式(包括導航欄、內容、主題等)
return MaterialApp(
// home: 類似于根控制器,也是需要指定一個widget
// Scaffold: 類似于導航控制器NavigationController,包含導航欄相關屬性。是一個可變組件statefulWidget
home: Scaffold(
// appBar:導航欄
appBar: AppBar(
title: Text('Flutter Demo'),
),
// body: 內容
body: MyWidget(),
),
// theme:主題,可控制主題色
theme: ThemeData(
primaryColor: Colors.yellow
),
);
}
}
class MyWidget extends StatelessWidget {
@override
// build 確定組件返回的內容
Widget build(BuildContext context) {
// final創建一個_textStyle不可變變量,_開頭的屬性,表示私有屬性
final _textStyle = TextStyle(color: Colors.red, fontSize: 40.0, fontWeight: FontWeight.bold);
return Center(
child: Text(
'Hello Flutter',
textDirection: TextDirection.ltr,
style: _textStyle, // 直接使用變量
),
);
}
}
2.5 ListView 列表視圖
-
列表視圖
:類似iOS的UITableView
,但是沒有Section
的概念。
2.5.1 準備Model
在布局之前,先準備數據
:
- 新建
Model
文件夾,新建car.dart
文件:
image.png
// 不需要導入material.dart,因為Car直接繼承Object
// Car 模型
class Car {
// 構造函數: {}內是可選值
const Car({
this.name,
this.imageUrl,
});
// 名稱
final String name;
// 圖片鏈接
final String imageUrl;
}
final List<Car> datas = [
Car(
name: '保時捷918 Spyder',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-7d8be6ebc4c7c95b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '蘭博基尼Aventador',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-e3bfd824f30afaac?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '法拉利Enzo',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-a1d64cf5da2d9d99?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: 'Zenvo ST1',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-bf883b46690f93ce?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '邁凱倫F1',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-5a7b5550a19b8342?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '薩林S7',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-2e128d18144ad5b8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
)
];
2.5.2 設置ListView
- 如果直接導入
Car模型
,需要導入頭文件
三種導入
頭文件
的方式
直接手寫
,在頂部import
左鍵
點擊Car
,出現紅色小燈泡
,點擊import Library
鼠標光標
放在Car
上,按住Option+Enter
鍵,再按一次Enter
鍵,可快捷導入import Library
final List<Car> datas = []
-
Container
: 類似于html
的div
,也類似于iOS
的UIView
,就是用來放東西
的。需要設置大小
(也可以被子控件撐出
大?。?/li> -
Column
: 內容垂直排列
的容器 -
row
: 內容水平排列
的容器 -
stack
: 內容重疊
的容器 -
Image
:圖片
的可變組件
,network
加載網絡圖片
-
SizedBox
:空容器
,有大小。(有時為了便于
內部插入元素
,會直接使用Container
) -
MaterialApp
的debugShowCheckedModeBanner
:隱藏
導航欄Debug
角標
import 'package:flutter/material.dart';
import 'Model/car.dart';
// 入口,展示MyWidget組件
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
// MaterialApp:Flutter推薦方式,提供快速構建APP的方式(包括導航欄、內容、主題等)
return MaterialApp(
// 隱藏導航欄Debug角標
debugShowCheckedModeBanner: false,
// home: 類似于根控制器,需要指定一個widget
home: Home(),
// theme:主題,可控制主題色
theme: ThemeData(
primaryColor: Colors.yellow
),
);
}
}
// Home 組件
class Home extends StatelessWidget {
// 回調函數,返回widget組件
Widget _cellForRow(BuildContext context, int index) {
// Container類似于html的div,也類似于iOS的UIView,就是用來放東西的
// 需要大?。ㄒ部梢员蛔涌丶纬龃笮。? return Container(
color: Colors.white,
// height: 20,
margin: EdgeInsets.all(10), //EdgeInsets.only(top: 1),
// 子控件
// Column 內容垂直排列的容器 row 內容水平排列的容器 stack 內容重疊的容器
// Image圖片可變組件,network加載網絡圖片
child: Column(
children: <Widget>[
Image.network(datas[index].imageUrl),
SizedBox(height: 8,),
Text(
datas[index].name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
fontStyle: FontStyle.italic),),
SizedBox(height: 8,)
]
)
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text("Flutter Demo"),
),
// ListView 列表組件(沒有iOS的Section概念)
body: ListView.builder(
// cell個數
itemCount: datas.length,
// cell內容(等同與cellForRow)build 是渲染
// iOS中是使用代理和協議完成,這里是直接使用回調函數,有兩個入參
itemBuilder: _cellForRow,
),
);
}
}
// 模型數組
// 沒導入頭文件時,會提示需要導入Car頭文件
// 三種導入頭文件的方式
// 1. 直接手寫,在頂部import
// 2. 左鍵點擊Car,出現紅色小燈泡,點擊import Library
// 3. 鼠標光標放在Car上,按住Option+Enter鍵,再按一次Enter鍵,可快捷導入import Library
// final List<Car> datas = []
-
效果展示:
image.png 實際開發中,我們可以將
ListView
內容抽離
出來,做成單獨文件listView_demo.dart
:
import 'package:flutter/material.dart';
import 'car.dart';
class ListViewDemo extends StatelessWidget {
// 回調函數,返回widget組件
Widget _cellForRow(BuildContext context, int index) {
return Container(
color: Colors.white,
margin: EdgeInsets.all(10)
child: Column(
children: <Widget>[
Image.network(datas[index].imageUrl),
SizedBox(height: 8,),
Text(
datas[index].name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
fontStyle: FontStyle.italic),),
SizedBox(height: 8,)
]
)
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
// cell個數
itemCount: datas.length,
itemBuilder: _cellForRow,
);
}
}
- 在
main.dart
中,直接使用我們封裝的ListViewDemo
(導入頭文件
)即可:
import 'package:flutter/material.dart';
import 'Model/listView_demo.dart';
// 入口,展示MyWidget組件
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
// MaterialApp:Flutter推薦方式,提供快速構建APP的方式(包括導航欄、內容、主題等)
return MaterialApp(
// 隱藏導航欄Debug角標
debugShowCheckedModeBanner: false,
// 根組件
home: Home(),
// 主題
theme: ThemeData(
primaryColor: Colors.yellow
),
);
}
}
// Home 組件
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text("Flutter Demo"),
),
// 列表
body: ListViewDemo(),
);
}
}
3. 常用基礎組件
- 上面講了
簡單封裝
,我們現在對幾個基礎組件
進行簡單封裝
:
3.1 基礎文本
- 新建一個
base_widget.dart
文件,創建TextDemo
組件:
- 通過
屬性
聲明變量
(內容
和樣式
),_
開頭的屬性為私有屬性
。$
+屬性名
:快捷插入
變量內容
。 如果后面有其他字符
等信息,可使用{}包裹
起來。Text
可設置maxLines
最大行數,超出部分樣式通過overflow
設置(ellipsis
為尾部省略號
)
import 'package:flutter/material.dart';
//【普通文本 Demo】
class TextDemo extends StatelessWidget {
// 文本樣式(私有屬性)
final TextStyle _textStyle = TextStyle(
fontSize: 24.0,
);
final String _title = 'Flutter入門';
final String _author = 'HT';
@override
Widget build(BuildContext context) {
// $ + 屬性名: 快捷插入變量內容。 如果后面有其他字符等信息,可使用{}包裹起來。
return Text(
'《$_title》這是一個TextDemo,使用Flutter開發。由iOS開發者${_author}開發,快速配置,簡易上手的零基礎學習方式。歡迎閱讀和上手練習,不懂之處,留言交流',
textAlign: TextAlign.center,
style: _textStyle,
maxLines: 4, // 最多4行
overflow: TextOverflow.ellipsis, // 超出顯示省略號
);
}
}
- 在
main.dart
中指定body
為TextDemo
:
image.png -
展示樣式:
image.png
3.2 富文本
- 使用
RichText
組件,通過給text
指定TextSpan
類型,添加children:<TextSpan>
數組,數組內創建TextSpan
,并賦值樣式
即可:
import 'package:flutter/material.dart';
//【富文本 Demo】
class RichTextDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RichText(
//基礎元素
text: TextSpan(
text: '《Flutter 入門》',
style: TextStyle(
fontSize: 30,
color: Colors.black,
) ,
// 子元素
children:<TextSpan>[
// 元素一
TextSpan(
text: 'HT',
style: TextStyle(
fontSize: 20,
color: Colors.blue,
),
),
// 元素二
TextSpan(
text: '666',
style: TextStyle(
fontSize: 40,
color: Colors.red,
),
),
]),
);
}
}
- 在
main.dart
中指定body
為RichTextDemo
:
image.png -
展示樣式:
image.png
3.3 基礎容器Container
- 創建基礎組件
BaseWidgetDemo
,返回Container
組件:
//【基礎組件Demo】
class BaseWidgetDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 容器背景藍色(沒有給定大小時,根據子視圖大小撐開)
return Container(
color: Colors.blue,
// 子視圖橫向布局
child: Row(
children: <Widget>[
Container(
// 內邊距
padding: EdgeInsets.only(left: 10,top: 10,right: 10,bottom: 10),
// 外邊距
margin: EdgeInsets.only(left: 10,top: 10,right: 10,bottom: 10),
// 子元素紅色
color: Colors.red,
// 子元素內容包含?號圖片
child: Icon(Icons.add),
),
Container(
// 子元素紅色
color: Colors.red,
// 子元素內容包含?號圖片
child: Icon(Icons.add),
),
],
),
);
}
}
-
展示樣式:
image.png
Dart
中沒有iOS
的Button
概念:
因為Button
實際是圖片
+文字
+手勢
+狀態
組成,以及封裝相應的響應事件
。我們一般使用手勢小部件
,包裝圖片
和文字
即可。
【快捷鍵】
command
+option
+L
:自動格式化
command
+-
:折疊當前函數
command
+shift
+-
:折疊全部函數
command
+{
:回到上一步
command
+}
: 回到下一步
command
+shift
+O
: 快速到指定文件
stless
:不可變組件
stful
: 可變組件
本節主要是Flutter
的基礎體驗
,通過本節,其實我們已經感受到了Flutter
的便捷
。
下一節,Flutter入門三:自動布局(Row/Column/Stack)與兩種Widget