您好,登錄后才能下訂單哦!
距離Google發(fā)布Flutter已經(jīng)有很長一段時間了,又是一門新的技術(shù),那么我們到底是學(xué)呢還是學(xué)呢還是學(xué)呢?不要問我,我不知道,鬼特么知道我這輩子還要學(xué)習(xí)多少東西。其實新技術(shù)的出現(xiàn)也意味著,老技術(shù)會面臨淘汰危機,而你將面臨著失業(yè)危機。用一句話來說:你永遠(yuǎn)不知道意外和驚喜哪個先來~~
Flutter的安裝就不在這里演示了,可以從下面幾個網(wǎng)站上學(xué)習(xí)安裝。
這些網(wǎng)站也通過豐富的Flutter
學(xué)習(xí)資料
在創(chuàng)建一個Flutter
應(yīng)用后,我們可以看到如下的demo代碼。(其中注釋是個人翻譯,如有不正確請諒解)
import 'package:flutter/material.dart';
//應(yīng)用啟動
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// 這個App的根Widget
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', //應(yīng)用名
theme: ThemeData(
// 這個應(yīng)用的主題
//
// 你用 "flutter run"運行這個應(yīng)用,你將看到一個藍(lán)色的ToolBar。
// 你也可以改變下面primarySwatch 的值,從Colors.blue變成 Colors.green。
// 然后執(zhí)行 "hot reload" ,可以看到計數(shù)器并沒有恢復(fù)初始狀態(tài)0,這個應(yīng)用也并沒有重啟。
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// 我們可以知道這個MyHomePage 的 widget 是這個應(yīng)用的首頁,而且它是有狀態(tài)的,
//這就意味著下面定義的State對象中的字段能夠影響應(yīng)用的顯示。
//這個類是這個狀態(tài)的配置類,它所持有的這個title值是其父類提供的,
//被創(chuàng)建狀態(tài)的方法使用,在Widget的子類中總是被標(biāo)記為“final”
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// 回調(diào)setState去告訴Flutter framework 已經(jīng)有一些狀態(tài)發(fā)生了改變,
// 讓下面這個返回Widget的build方法去展示更新的內(nèi)容。當(dāng)然,如果我們沒有回調(diào)
// 這個setState,那么build方法也不會被調(diào)用,也就不會有什么更新展示。
_counter++;
});
}
@override
Widget build(BuildContext context) {
// 這個方法在每次setState的時候被調(diào)用,例如上面的_incrementCounter方法。
//
// Flutter framework 是被優(yōu)化過的,所以它的重新運行build方法是非常快速的,只需要
// 運行你需要更新的內(nèi)容,不需要去分別所有的widgets的實例。
return Scaffold(
appBar: AppBar(
// 我們能夠使用在App.build方法中創(chuàng)建的值,并且賦值
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center是一個布局Widget,它提供一個child 并且規(guī)定了只能居于父類的正中心
child: Column(
// Column 也是一個布局Widget,它有一系列的子布局并且這些子布局都是垂直方向的。
// 默認(rèn)情況下,Column會調(diào)整它自己的大小去適應(yīng)子級的橫向大小。
//
// 調(diào)用 "debug painting"可以看每一個widget的線框
//
// Column 有大量的屬性去控制自己的大小和它子級的位置,這里使用了mainAxisAlignment
// 讓其子布局內(nèi)容是垂直方向排列。
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
我使用的是
Android Studio
創(chuàng)建的Flutter
應(yīng)用,可以看到如下所示的編譯界面
cdn.xitu.io/2019/3/3/16942a8bec851ea7?w=862&h=190&f=png&s=29763">
Run
(就是那個綠色的三角)之后我們可以看到如下運行結(jié)果:“+”
號我們可以看到,中間的數(shù)字一直在增加,所以demo給我的是一個簡單計數(shù)器的實現(xiàn)我們從官網(wǎng)知道Flutter
是用Dart
語言進(jìn)行編碼的,我們是不是需要單獨去學(xué)習(xí)掌握這門語言呢?在我看來是不需要的,因為單獨去學(xué)習(xí)一門新的語言的過程是很枯燥的,我們可以從Demo中去學(xué)習(xí),這樣更高效一些。所以我們來分析一下上述例子給了我們一個怎樣的知識點。
import 'package:flutter/material.dart';
void main() => runApp(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'),
);
}
}
通過上述代碼我們知道:
material
的dart
文件。main()
方法調(diào)用了MyApp
的一個類。runApp
可知)對于一個Flutter小白就會有疑問了:
material
的文件呢?Flutter提供了許多widgets
,可幫助您構(gòu)建遵循Material Design
的應(yīng)用程序。Material應(yīng)用程序以MaterialApp
widget開始, 該widget在應(yīng)用程序的根部創(chuàng)建了一些有用的widget,其中包括一個Navigator, 它管理由字符串標(biāo)識的Widget棧(即頁面路由棧)。Navigator可以讓您的應(yīng)用程序在頁面之間的平滑的過渡。 是否使用MaterialApp完全是可選的,但是使用它是一個很好的做法。
也就是說主要是為了向開發(fā)者提供已經(jīng)實現(xiàn)好了的material
設(shè)計風(fēng)格,我們可以進(jìn)入(Windows下Ctrl +鼠標(biāo)左鍵
,Mac下Command+鼠標(biāo)左鍵
)material.dart
源碼,可以發(fā)現(xiàn)如下:
library material;
export 'src/material/about.dart';
export 'src/material/animated_icons.dart';
...
// 很多,這里就不占用大量篇幅
export 'widgets.dart';
從官網(wǎng)我們知道已經(jīng)有大量的widgets
供給我們使用,那么這些在哪里呢?
當(dāng)然就是上面的widgets.dart
文件了,我們進(jìn)入這個文件中可以看到內(nèi)容大致如下:
export 'src/widgets/animated_cross_fade.dart';
...
export 'src/widgets/framework.dart';
...
export 'src/widgets/will_pop_scope.dart';
也是不同的dart
文件,我們進(jìn)入第一個animated_cross_fade
:
class AnimatedCrossFade extends StatefulWidget {
/// Creates a cross-fade animation widget.
...
}
從給的注釋可以知道,這就是一個帶淡入淡出動畫的Widget
,這個Widget
繼承自StatefulWidget
,可以看到StatefulWidget
也就是繼承自Widget
abstract class StatelessWidget extends Widget {
/// Initializes [key] for subclasses.
const StatelessWidget({ Key key }) : super(key: key);
/// Creates a [StatelessElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
abstract class StatefulWidget extends Widget {
/// Initializes [key] for subclasses.
const StatefulWidget({ Key key }) : super(key: key);
/// Creates a [StatefulElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState();
}
到此我們驚奇的發(fā)現(xiàn),Demo代碼中的MyApp
繼承的StatelessWidget
原來也在這里,但是MyHomePage
卻繼承自StatefulWidget
,這是為什么呢?這就會引出第二個問題:
StatelessWidget
和StatefulWidget
的區(qū)別是什么呢?
StatefulWidget
可以擁有狀態(tài),這些狀態(tài)在widget生命周期中是可以變的,而StatelessWidget
是不可變的。StatefulWidget
至少由兩個類組成:
StatelessWidget
用于不需要維護(hù)狀態(tài)的場景,它通常在build方法中通過嵌套其它Widget
來構(gòu)建UI,在構(gòu)建過程中會遞歸的構(gòu)建其嵌套的Widget
這也就是為什么MyApp是繼承自StatelessWidget
而 MyHomePage 繼承自StatefulWidget
:
Widget
StatefulWidget
的原因是:通過點擊+
去增加數(shù)字大小,改變了顯示的狀態(tài),所以需要繼承StatefulWidget
。我們回到 MyApp這個類的build
方法中,可以看到它返回了一個MaterialApp
的一個Widget,在前面說過,Material Design的應(yīng)用是以MaterialApp
widget開始的,所以返回了一個MaterialApp
return MaterialApp(
title: 'Flutter Demo', //應(yīng)用名
theme: ThemeData(
primarySwatch: Colors.blue, // 主題色
),
home: MyHomePage(title: 'Flutter Demo Home Page'), // 首頁
);
從上可以知道由于計數(shù)是一個可變的更新狀態(tài),那么就需要兩個類去實現(xiàn):
StatefulWidget
, 就是我們的MyHomePage
State
用于維護(hù)這個狀態(tài),也就是我們的_MyHomePageState
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// 我們可以知道這個MyHomePage 的 widget 是這個應(yīng)用的首頁,而且它是有狀態(tài)的,
//這就意味著下面定義的State對象中的字段能夠影響應(yīng)用的顯示。
//這個類是這個狀態(tài)的配置類,它所持有的這個title值是其父類提供的,
//被創(chuàng)建狀態(tài)的方法使用,在Widget的子類中總是被標(biāo)記為“final”
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
MyHomePage
這個類里面沒有太多內(nèi)容:
createState
返回了一個_MyHomePageState
的有狀態(tài)的State
到此處我們知道了實際上對數(shù)據(jù)的操作肯定就在_MyHomePageState
中:
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// 回調(diào)setState去告訴Flutter framework 已經(jīng)有一些狀態(tài)發(fā)生了改變,
// 讓下面這個返回Widget的build方法去展示更新的內(nèi)容。當(dāng)然,如果我們沒有回調(diào)
// 這個setState,那么build方法也不會被調(diào)用,也就不會有什么更新展示。
_counter++;
});
}
@override
Widget build(BuildContext context) {
// 這個方法在每次setState的時候被調(diào)用,例如上面的_incrementCounter方法。
//
// Flutter framework 是被優(yōu)化過的,所以它的重新運行build方法是非常快速的,只需要
// 運行你需要更新的內(nèi)容,不需要去分別所有的widgets的實例。
return Scaffold(
appBar: AppBar(
// 我們能夠使用在App.build方法中創(chuàng)建的值,并且賦值
title: Text(widget.title),
),
body: Center(
// Center是一個布局Widget,它提供一個child 并且規(guī)定了只能居于父類的正中心
child: Column(
// Column 也是一個布局Widget,它有一系列的子布局并且這些子布局都是垂直方向的。
// 默認(rèn)情況下,Column會調(diào)整它自己的大小去適應(yīng)子級的橫向大小。
//
// 調(diào)用 "debug painting"可以看每一個widget的線框
//
// Column 有大量的屬性去控制自己的大小和它子級的位置,這里使用了mainAxisAlignment
// 讓其子布局內(nèi)容是垂直方向排列。
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
可以看出這里提供了兩個方法:build
和_incrementCounter
,我們已經(jīng)知道一般Widget
里面的build
方法返回的是一個頁面布局。
_incrementCounter
的實現(xiàn)內(nèi)容很簡單:
就是使用setState
方法去自增這個_counter
,但此處一點要注意,更改狀態(tài)一定要使用 setState
,如果不調(diào)用 setState
將不會有任何的改變,即使你自增了這個_counter
。(可以自己嘗試一下)
我們從注釋中可知,這個build方法在每次更新狀態(tài)(setState
)的時候進(jìn)行調(diào)用,我們在build
的方法中增加一行打印的代碼進(jìn)行驗證:
@override
Widget build(BuildContext context) {
print("build again");
return Scaffold(
...
);
發(fā)現(xiàn)果然是每一次點擊+
就會調(diào)用一次build
方法,那這就會引出一個問題:這樣每一次都進(jìn)行更新,會影響新能嗎?
Flutter framework 被優(yōu)化于快速重啟運行,只需要運行你需要更新的內(nèi)容,不需要去分別重新構(gòu)建所有的widgets的實例。
所以完全不必?fù)?dān)心這個每次都執(zhí)行build
方法會影響性能。
從整體的布局我們知道,build返回了一個Scaffold
的widget:
class Scaffold extends StatefulWidget {
/// Creates a visual scaffold for material design widgets.
const Scaffold({
Key key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.extendBody = false,
this.drawerDragStartBehavior = DragStartBehavior.down,
}) : assert(primary != null),
assert(extendBody != null),
assert(drawerDragStartBehavior != null),
super(key: key);
可以知道,這個也是繼承于StatefulWidget
,里面有很多可以設(shè)置的初始值,這里使用到了三個:
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,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
將從MyApp
攜帶的title賦值給appBar
的title,讓其顯示在界面頂端。內(nèi)容(body
)使用了一個Center
居中布局,讓其child(也是一個widget)只能顯示在當(dāng)前正中位置。
class Center extends Align {
/// Creates a widget that centers its child.
const Center({ Key key, double widthFactor, double heightFactor, Widget child })
: super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}
緊接著返回了一個Column
的child,這個widget 是縱向排列,可有有一系列的子集,所以在Column
布了兩個Text
,一個顯示固定文本,一個顯示可變的文本:
class Column extends Flex {
Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
}) : super(
children: children,
key: key,
direction: Axis.vertical,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
}
需要注意的是,對變量的占位符使用的$
符號,就跟java中使用%
是一樣的。
最后一個就是我們點擊事件的按鈕floatingActionButton
,通過onPressed
去調(diào)用_incrementCounter
方法實現(xiàn)自增計數(shù)。
整個運行的流程就到這里算是講完了。
在文章開始的時候我們知道,我們有一個一道雷一樣的圖標(biāo),那就是Hot Reload
,這個怎么個意思呢?就是說你如果更新了你的代碼,不用重新運行整個都重新運行,直接使用這個就可以了,可以很迅速的將你更新的內(nèi)容重新顯示。
在這里有一個很有意思的事情:
我們點擊+
讓計數(shù)器顯示到1,然后將主題的顏色改成綠色:primarySwatch: Colors.green,
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// 主題從藍(lán)色更改為綠色
primarySwatch: Colors.green,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
然后點擊hot reload
會發(fā)現(xiàn),我們的主題更改為綠色了,但是我的計數(shù)器顯示的數(shù)字仍然是1,并沒有變成0。這也印證了上面的那句話,hot reload
只會更改所需要更改的內(nèi)容,不會影響全部。
這里在做一個總結(jié),希望對才你有所幫助
flutter
中,絕大部分可使用的內(nèi)容都是widget
StatelessWidget
;如果涉及狀態(tài)的變更就是用StatefulWidget
StatefulWidget
的實現(xiàn)需要兩步:一個是需要創(chuàng)建繼承StatefulWidget
的類;另一個就是創(chuàng)建繼承State
的類,一般在State中控制整個狀態(tài)。setState
方法,不然不會起作用hot reload
只會影響更改的內(nèi)容免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。