該文已授權公眾號 「碼個蛋」,轉載請指明出處
上一節介紹了 Dart
的一些語法,以及配置環境的網址,這節我們就可以開始瞭解下 Flutter
了
主要包括 MaterialApp
、Scaffold
、Text
、Image
、Icon
、Button
以及 AppBar
部分內容,準備出發~
Flutter runApp
新建 flutter 專案後,可以看到 lib 下的 main.dart 中 void main() => runApp(MyApp());
這句就是程式的入口了。這裡可以簡單看下原始碼
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
///....
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
///....
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
複製程式碼
首先會建立一個 WidgetsBinding
單例物件,然後把傳入的 App 新增到 rootWidget
中,scheduleWarmUpFrame
方法比較長,這邊看下對該方法的註釋第一句就能瞭解方法的主要功能了
Schedule a frame to run as soon as possible
「安排框架儘快執行起來」(原諒我這渣英語,只能看懂不會翻譯..大概就是「快速啟動框架」的意思吧)
Flutter App
接著看下 MyApp
這個類,繼承自 StatelessWidget
並在 build
方法返回一個 MaterialApp
例項,(偷偷講下,其實這邊還可以返回 CupertinoApp
,這是一個 iOS 風格的 widget,基本上你看到部件帶 「Cupertino」的都是 iOS 風格的 widget,這裡先不講 iOS 風格的部件,目前 flutter 對 Cupertino 系列的 widget 支援不是很好,包括部件的廣度,多語言的支援等等方面都不是很友好,所以我們還是繼續看 MD 風格的 Android 部件吧~),這裡先看下 MaterialApp
的建構函式,介紹一些常用的引數
const MaterialApp({
Key key,
this.navigatorKey,
this.home, // 主介面的內容 widget
this.routes = const <String, WidgetBuilder>{}, // 帶 router 和路由跳轉有關
this.initialRoute,
this.onGenerateRoute,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
this.builder,
this.title = '', // *類似標題
this.onGenerateTitle, // 主要用於多語言情況下,需要根據當前語言替換 title,需要使用該值
this.color, // 主題色,如果該值未設定,取 theme.primaryColor,未設定 theme 則取藍色
this.theme, // App 的主題風格,包括主題色,按鈕預設顏色等等
this.locale, // 帶 locale 的和多語言適配相關
this.localizationsDelegates,
this.localeListResolutionCallback,
this.localeResolutionCallback,
this.supportedLocales = const <Locale>[Locale('en', 'US')],
this.debugShowMaterialGrid = false,
this.showPerformanceOverlay = false,
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,
this.debugShowCheckedModeBanner = true, // debug 模式下,是否顯示 DEBUG 標示橫幅
})
複製程式碼
MaterialApp
繼承自 StatefulWidget
,它和 MyApp
所繼承的類 StatelessWidget
,就是日常開發中,自定義部件通常繼承的抽象類了。
StatelessWidget
是狀態不可變部件,通過其構建的部件一般用來展示固定內容,例如需要展示固定的功能按鈕列表,不需要根據不同介面狀態進行修改其展示內容StatefulWidget
是可改變狀態的部件,比如我們需要通過網路或者資料庫獲取資料,然後修改部件鎖展示的資料內容,則需要通過StatefulWidget
來構建。當然,不是說StatelessWidget
不能實現修改介面資料的功能,這就需要涉及到 狀態管理 的概念了,後面有機會再講,這邊先埋坑【坑1】
Flutter Scaffold
進入 App
後就需要構建介面了,Flutter
提供了 Scaffold
來快速構建一個 MaterialDesign
風格的介面,還是先看下 Scaffold
的建構函式吧,瞭解幾個比較常用的部分。
const Scaffold({
Key key,
this.appBar, // 介面頂部的那條欄,這邊需要返回一個 AppBar 例項
this.body, // 介面的內容部分
this.floatingActionButton, // 懸浮部分,可以通過 floatingActionButtonLocation 設定位置
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer, // 側滑抽屜部分,從左側滑出(應該和語言有關,和文字方向同向)
this.endDrawer, // 側滑抽屜部分,從右側滑出
this.bottomNavigationBar, // 底部導航欄,就是通常看到的底部 TAB 切換部件
this.bottomSheet, // 展示從底部彈出的,起到提示作用的,通過 showModalBottomSheet 展示
this.backgroundColor, // 介面的背景色
this.resizeToAvoidBottomPadding = true, // 避免 body 被底部彈出部件填充,例如輸入法鍵盤
this.primary = true, // 當前的 Scaffold 是否需要被展示在螢幕最上層
})
複製程式碼
來張圖吧,簡潔明瞭
瞭解完 Scaffold
的整體構造後,我們從上到下,通過建構函式來了解下各個 Widget
的使用方法
AppBar
AppBar({
Key key,
this.leading, // 用於設定 AppBar 前置的按鈕,例如設定返回我們需要的返回按鈕等
this.automaticallyImplyLeading = true, // 是否使用系統預設生成的按鈕,如果替換 leading 的預設按鈕,最好將該屬性設定成 false
this.title, // AppBar 所需要展示的元件,傳入一個 Widget 例項,通常使用 Text 展示一個標題
this.actions, // AppBar 末尾懸浮的一些操作元件,例如常見的會在末尾設定一個「...」按鈕,點選彈出一個 menue 提供給使用者操作選擇
this.flexibleSpace, // AppBar 的背景,可以設定顏色,背景圖等等
this.bottom, // bottom 用於展示頂部導航 TAB
this.elevation = 4.0,
this.backgroundColor, // AppBar 的背景色,如果只需要修改顏色,可以不通過 flexibleSpace 修改
this.brightness,
this.iconTheme, // 按鈕的預設樣式
this.textTheme, // 文字的預設樣式
this.primary = true,
this.centerTitle, // 是否將展示的 title 居中
this.titleSpacing = NavigationToolbar.kMiddleSpacing, // AppBar title 兩側的空白間隔
this.toolbarOpacity = 1.0,
this.bottomOpacity = 1.0,
})
複製程式碼
在展示 AppBar
的 demo
之前,我們先學習幾個基本的元件 Text
、Image
、Icon
、Button
分佈用於展示文字,圖片,圖示,按鈕
Text
const Text(this.data, { // Text 需要展示的文字
Key key,
this.style, // 文字的樣式,包括顏色,大小,間距等等屬性,這邊就不繼續展示 TextStyle 建構函式了,不然我怕大家都不想繼續看了,稍後通過例子來說明
this.textAlign, // 文字的對齊方式,包括左對齊,右對齊,居中等,詳見 TextAlign 類
this.textDirection, // 文字方向,ltr(left to right) 或者 rtl(right to left)
this.locale,
this.softWrap, // 當文字一行顯示不完是否換行
this.overflow, // 如果超出限制的行數,以哪種方式省略未展示的內容
this.textScaleFactor, // 文字縮放比例
this.maxLines, // 最多展示的行數
this.semanticsLabel,
})
複製程式碼
說了那麼多,相信很多小夥伴都要急著擼程式碼了吧,接著來展示一些 Text
的示例,接下來的例子都會直接替換 HomePage
內的展示內容,其餘都是相同的,接下來請關注 Text
別的部件先忽略,後面會介紹,這邊先埋坑【坑2】
import 'package:flutter/material.dart';
void main() => runApp(DemoApp());
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.lightBlue),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: const EdgeInsets.only(top: 10.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('綠色背景黑色文字展示',
style: TextStyle(
color: Colors.black, // 設定文字顏色,不可和 foreground 同時設定
fontSize: 24.0, // 字型大小
letterSpacing: 2.0, // 每個字元之間的間隔
background: Paint()..color = Colors.green)), // 背景色
Text('這是一個帶紅色下劃線的文字展示',
style: TextStyle(
color: Colors.black,
fontSize: 24.0,
// 文字裝飾線,除了 underline 還有 overline, lineThrough,
// 不同的樣式小夥伴可以通過自己修改程式碼來檢視
decoration: TextDecoration.underline,
// 文字裝飾線的型別,除了 solid 還有 double,dotted,dashed,wavy 可選
decorationStyle: TextDecorationStyle.solid,
// 裝飾線的顏色
decorationColor: Colors.red))
],
)),
));
}
}
複製程式碼
該部分程式碼檢視原始碼 text_main.dart
檔案
最後的展示效果如下圖:
Image
/壞笑 按照慣例,我們還是先看下 Image
的建構函式吧
const Image({
Key key,
// 一個 ImageProvider 例項,但是 ImageProvider 是一個抽象類,Flutter 已經給我們提供如下
// AssetImage,NetworkImage,FileImage,MemoryImage 這四種圖片載入器,為了方便呼叫
// 我們可以直接通過 Image.asset, Image.network, Image.file, Image.memory 簡化,
// 通過方法名,可以看出分別從 asset 檔案,網路,檔案,記憶體中載入圖片
@required this.image,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width, // 圖片寬度
this.height, // 圖片高度
this.color, // 圖片背景色
this.colorBlendMode, // color 和圖片的混合模式(這個值比較多,可以一個個嘗試)
this.fit, // 圖片填充方式 fill, cover, contain, fillWidth, fillHeight, scaleDown, none
this.alignment = Alignment.center, // 對齊方式
this.repeat = ImageRepeat.noRepeat, // 若未填充滿空間,重複展示的方式
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.filterQuality = FilterQuality.low,
})
複製程式碼
好了好了,我知道你們又想自己寫程式碼嘗試下了,在這之前,需要你先準備一張本地圖片,然後在專案的根目錄,也就是 lib
資料夾同層,建立一個新的資料夾,命名為 images
,把你準備好的圖片放到這個目錄下。放好之後開啟 pubspec.yaml
把圖片資原始檔註冊下
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# 這邊註冊資原始檔,以後有圖片檔案也可以只註冊 images 資料夾,會自動讀取內部的檔案
assets:
- images/ali.jpg
複製程式碼
註冊完成後,就可以繼續愉快的擼程式碼了~
class HomePage extends StatelessWidget {
final String _assetAli = 'images/ali.jpg';
final String _picUrl =
'https://timg05.bdimg.com/timg?wapbaike&quality=60&size=b1440_952&cut_x=143&cut_y=0&cut_w=1633&'
'cut_h=1080&sec=1349839550&di=cbbc175a45ccec5482ce2cff09a3ae34&'
'src=http://imgsrc.baidu.com/baike/pic/item/4afbfbedab64034f104872baa7c379310b551d80.jpg';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: const EdgeInsets.only(top: 10.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 這種展示圖片方式和下一種會有相同的效果
Image(image: AssetImage(_assetAli), width: 80.0, height: 80.0),
// 接下來載入圖片都會使用這些比較方便的方法
Image.asset(_assetAli, width: 80.0, height: 80.0),
// 載入一張網路圖片
Image.network(_picUrl,
height: 80.0,
// 橫向重複
repeat: ImageRepeat.repeatX,
// MediaQuery.of(context).size 獲取到的為上層容器的寬高
width: MediaQuery.of(context).size.width),
// 通過設定混合模式,可以看到圖片展示的樣式已經修改
Image.asset(_assetAli,
width: 80.0, height: 80.0, color: Colors.green, colorBlendMode: BlendMode.colorDodge),
// 會優先載入指定的 asset 圖片,然後等網路圖片讀取成功後載入網路圖片,會通過漸隱漸現方式展現
// cover 方式按照較小的邊佈滿,較大的給切割
// contain 會按照最大的邊佈滿,較小的會被留白
// fill 會把較大的一邊壓縮
// fitHeight, fitWidth 分別按照長寬來佈滿
FadeInImage.assetNetwork(
placeholder: _assetAli, image: _picUrl, width: 120.0, height: 120.0, fit: BoxFit.cover),
// Icon 相對屬性少了很多,需要傳入一個 IconData 例項,flutter 提供了很多圖示,
// 但是實際情況我們需要加入我們自己的圖示,這邊再埋坑【坑3】
// size 為圖示顯示的大小,color 為圖示的顏色,這邊通過 Theme 獲取主題色調
Icon(Icons.android, size: 40.0, color: Theme.of(context).primaryColorDark)
],
)),
));
}
}
複製程式碼
該部分程式碼檢視原始碼 image_main.dart
檔案
最後的效果如下:
Button
Flutter
提供了各種型別的 Button
幾乎是大同小異的,這邊就抽取一些比較常用的展示下效果,常用的主要有 RaisedButton
、FlatButton
、IconButton
、OutlineButton
、MaterialButton
、FloatActionButton
、FloatingActionButton.extended
Button
都有一個 onPress
引數,是 VoidCallback
型別的引數,通過檢視原始碼可以知道 VoidCallback
是無參無返回值的一種型別引數。如果該引數傳入的值為 null
那麼這個按鈕的就不可點選狀態,無點選效果,等會可以在例子中檢視。還有就是 child
引數,這裡就是傳入你需要展示的內容,比如 Text
、Icon
等等。別的引數基本可以通過引數名瞭解,這邊不擴充套件了(再看原始碼我怕你們都不想繼續看下去了...)
Talk is cheap, show me the code
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: const EdgeInsets.only(top: 10.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
print('This is a Rased Button can be clicked');
},
child: Text('Raised Enable'),
),
RaisedButton(onPressed: null, child: Text('Raised Disable')),
FlatButton(
onPressed: () => print('This is a Flat Button can be clicker'),
child: Text('Flat Enable'),
),
FlatButton(onPressed: null, child: Text('Flat Disable')),
IconButton(icon: Icon(Icons.android), onPressed: () {}),
IconButton(icon: Icon(Icons.android), onPressed: null),
MaterialButton(onPressed: () {}, child: Text('Material Enable')),
MaterialButton(onPressed: null, child: Text('Material Disable')),
OutlineButton(onPressed: () {}, child: Text('Outline Enable')),
OutlineButton(onPressed: null, child: Text('Outline Enable')),
],
)),
),
floatingActionButton:
FloatingActionButton.extended(onPressed: () {}, icon: Icon(Icons.android), label: Text('Android')),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
複製程式碼
該部分程式碼檢視原始碼 button_main.dart
部分
最終的效果圖:
這篇終於到末尾了,最後留了 3 個坑等以後解決
最後程式碼的地址還是要的:
-
文章中涉及的程式碼:demos
-
基於郭神
cool weather
介面的一個專案,實現BLoC
模式,實現狀態管理:flutter_weather -
一個課程(當時買了想看下程式碼規範的,程式碼更新會比較慢,雖然是跟著課上的一些寫程式碼,但是還是做了自己的修改,很多地方看著不舒服,然後就改成自己的實現方式了):flutter_shop
如果對你有幫助的話,記得給個 Star,先謝過,你的認可就是支援我繼續寫下去的動力~