版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2019.11.03 星期日 |
前言
Flutter是谷歌的移動UI框架,可以快速在iOS和Android上構(gòu)建高質(zhì)量的原生用戶界面。 Flutter可以與現(xiàn)有的代碼一起工作。在全世界,F(xiàn)lutter正在被越來越多的開發(fā)者和組織使用,并且Flutter是完全免費、開源的。目前公司的部分模塊就是在使用Flutter進(jìn)行開發(fā)。感興趣的可以看下面幾篇文章。
1. Flutter開發(fā)技術(shù)與分享(一) —— 基本概覽(一)
開始
首先看下主要內(nèi)容
通過使用
VS Code
編寫跨平臺應(yīng)用程序,深入研究Flutter
框架,以在單個代碼庫中構(gòu)建iOS
和Android
應(yīng)用程序。下面是翻譯文章的地址。
然后看下寫作環(huán)境
寫作環(huán)境:Dart 2, Flutter 1.7, VS Code
自十年前iOS
和Android
平臺風(fēng)起云涌以來,跨平臺開發(fā)一直是整個移動開發(fā)領(lǐng)域的目標(biāo)。 能夠為iOS和Android編寫一個應(yīng)用程序的功能可以為您的公司和團(tuán)隊節(jié)省大量時間和精力。
多年來,已經(jīng)發(fā)布了用于跨平臺開發(fā)的各種工具,包括基于Web的工具(例如Adobe
的PhoneGap),強大的框架(例如Microsoft
的Xamarin)以及更新的工具(例如Facebook
的React Native)。 每個工具集都有其優(yōu)缺點,并且在移動行業(yè)中獲得了不同程度的成功。
進(jìn)入跨平臺領(lǐng)域的最新框架是Google
的Flutter。 Flutter在兩個平臺上均具有快速的開發(fā)周期,快速的UI呈現(xiàn),獨特的UI設(shè)計以及本機應(yīng)用程序性能。
Introduction to Flutter
Flutter
應(yīng)用程序是使用Dart編程語言編寫的,該語言最初也來自Google
,現(xiàn)在是ECMA
標(biāo)準(zhǔn)。 Dart
與其他現(xiàn)代語言(例如Kotlin
和Swift
)具有許多相同的功能,并且可以轉(zhuǎn)編譯為JavaScript
代碼。
作為跨平臺框架,Flutter
最類似于React Native
,因為Flutter
允許響應(yīng)式和聲明式編程風(fēng)格。但是,與React Native
不同,Flutter
不需要使用Javascript
橋接,這可以縮短應(yīng)用程序的啟動時間和整體性能。 Dart
通過使用Ahead-Of-Time
或AOT
編譯來實現(xiàn)此目的。
Dart
的另一個獨特之處在于它還可以使用Just-In-Time or JIT
編譯。 Flutter
的JIT
編譯通過允許熱重裝(hot reload)
功能在開發(fā)過程中刷新UI而無需全新的構(gòu)建,從而改善了開發(fā)工作流程。
正如您將在本教程中看到的那樣,Flutter
框架主要圍繞widgets
的概念構(gòu)建。在Flutter中,widgets
不僅用于應(yīng)用程序的視圖,而且還用于整個屏幕甚至應(yīng)用程序本身。
除了跨平臺的iOS和Android開發(fā)之外,學(xué)習(xí)Flutter還可以讓您搶先開發(fā)Fuchsia平臺,Fuchsia
平臺目前是Google
開發(fā)的實驗性操作系統(tǒng)。
在本教程中,您將構(gòu)建一個Flutter
應(yīng)用程序,該應(yīng)用程序?qū)⒉樵?em>GitHub API中的GitHub組織中的團(tuán)隊成員,并在可滾動列表中顯示團(tuán)隊成員信息:
您可以同時使用iOS
模擬器或Android
模擬器來開發(fā)應(yīng)用程序!
在構(gòu)建應(yīng)用程序時,您將了解有關(guān)Flutter的以下知識:
Setting up your development environment
Creating a new project
Hot reload
Importing files and packages
Using widgets and creating your own
Making network calls
Showing items in a list
Adding an app theme
順帶著你也會學(xué)習(xí)一點關(guān)于Dart
的知識。
Setting up your development environment
Flutter
開發(fā)可以在macOS
,Linux
或Windows
上完成。 盡管您可以將任何編輯器與Flutter工具鏈一起使用,但是有 IntelliJ IDEA,Android Studio和Visual Studio Code的IDE
插件可以簡化開發(fā)周期。 在本教程中,我們將使用VS Code
。
在此處here可以找到有關(guān)使用Flutter框架設(shè)置開發(fā)機器的說明。基本步驟因平臺而異,但大多數(shù)情況是:
- 1) 下載適用于您開發(fā)計算機操作系統(tǒng)的安裝包,以獲取
Flutter SDK
的最新穩(wěn)定版本 - 2) 將安裝包解壓縮到所需位置
- 3) 將flutter工具添加到您的路徑
- 4) 運行
flutter doctor
命令,該命令將安裝Flutter框架(包括Dart
)并提醒您任何缺少的依賴項 - 5) 安裝缺少的依賴項
- 6) 使用Flutter插件/擴展程序設(shè)置您的IDE
- 7) 測試驅(qū)動一個應(yīng)用
Flutter網(wǎng)站上提供的說明做得很好,可以讓您輕松地在所選平臺上設(shè)置開發(fā)環(huán)境。本教程的其余部分假定您已經(jīng)為Flutter開發(fā)設(shè)置了VS Code
,并且已經(jīng)解決了flutter doctor
發(fā)現(xiàn)的所有問題。
如果您使用的是Android Studio
,那么您也應(yīng)該能夠很好地遵循。您還需要運行iOS模擬器,Android模擬器,或者已設(shè)置預(yù)配置的iOS設(shè)備或Android設(shè)備進(jìn)行開發(fā)。
注意:要在iOS模擬器或iOS設(shè)備上進(jìn)行構(gòu)建和測試,您需要使用已安裝Xcode的
macOS
。
Creating a new project
在安裝了Flutter擴展的VS Code
中,通過選擇View ? Command Palette…
或在macOS上單擊Cmd-Shift-P
或在Linux或Windows上單擊Ctrl-Shift-P
來打開命令面板。 在面板中輸入Flutter:New Project
,然后按回車鍵。
輸入項目的名稱ghflutter
,然后按回車鍵。 選擇一個文件夾來存儲項目,然后等待Flutter在VS Code
中設(shè)置項目。 項目準(zhǔn)備就緒后,將在編輯器中打開文件main.dart
。
在VS Code
中,您會在左側(cè)看到一個面板,該面板顯示您的項目結(jié)構(gòu)。 有適用于iOS和Android的文件夾,還有一個包含main.dart
的lib
文件夾,并且具有適用于兩個平臺的代碼。 僅在本教程中,您將在lib
文件夾中工作。
用以下內(nèi)容替換main.dart
中的代碼:
import 'package:flutter/material.dart';
void main() => runApp(GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GHFlutter',
home: Scaffold(
appBar: AppBar(
title: Text('GHFlutter'),
),
body: Center(
child: Text('GHFlutter'),
),
),
);
}
}
頂部附近的main()
函數(shù)對單個行函數(shù)使用=>
運算符來運行該應(yīng)用程序。 您有一個名為GHFlutterApp
的應(yīng)用程序類。
您在這里看到您的應(yīng)用程序本身是一個StatelessWidget
。 Flutter應(yīng)用程序中的大多數(shù)實體都是無狀態(tài)或有狀態(tài)的widgets
。 您可以覆蓋widgets
的build()
方法來創(chuàng)建應(yīng)用widgets
。 您正在使用MaterialApp
widgets
,該widgets
提供了Material Design
之后的應(yīng)用所需的許多組件。
對于本入門教程,通過右鍵單擊,選擇Delete
選項,然后確認(rèn)刪除,從項目中刪除test
文件夾中的測試文件widget_test.dart
。
如果您使用的是macOS
,請啟動iOS模擬器。 您也可以在macOS,Linux或Windows上使用Android模擬器。 如果iOS模擬器和Android模擬器都在運行,則可以使用VS Code
窗口右下方的菜單在它們之間進(jìn)行切換:
要構(gòu)建和運行項目,您需要首先設(shè)置啟動配置。
通過單擊左側(cè)面板上的crossed bug
圖標(biāo),切換到Debug View
。
您會注意到,到目前為止,尚未定義任何配置。 單擊No Configuration
以獲取下拉列表并選擇Add Configuration
VS Code
將創(chuàng)建一個launch.json
文件,其詳細(xì)信息如下:
注意:選擇
Add Configuration
項后,將自動為您生成此文件。 在本教程中,您無需修改它。
現(xiàn)在,您已經(jīng)完成所有工作,可以通過按F5
或選擇Debug ? Start Debugging
或單擊綠色的播放圖標(biāo)來構(gòu)建和運行項目。 您會看到Debug Console
已打開,并且如果在iOS上運行,則會看到用于構(gòu)建項目的Xcode。 如果在Android上運行,則會看到Gradle
被調(diào)用以進(jìn)行構(gòu)建。
這是在iOS模擬器中運行的應(yīng)用程序:
下面,在Android模擬器中運行:
您看到的慢速模式banner
表明該應(yīng)用程序正在調(diào)試模式下運行。
您可以通過單擊VS Code
窗口頂部工具欄右側(cè)的停止按鈕來停止正在運行的應(yīng)用程序:
通過單擊VS Code
左上方的圖標(biāo)或選擇View ? Explorer
,可以返回項目視圖。
Hot Reload
Flutter
開發(fā)的最佳方面之一是能夠在進(jìn)行更改時熱重新加載您的應(yīng)用程序。 這類似于Android Studio
的Instant Run/Apply Changes
。
構(gòu)建并運行該應(yīng)用程序,使其在模擬器或模擬器上運行:
現(xiàn)在,無需停止正在運行的應(yīng)用程序,請將應(yīng)用程序欄字符串更改為其他內(nèi)容:
appBar: AppBar(
title: Text('GHFlutter App'),
),
現(xiàn)在,單擊工具欄上的熱重載按鈕或直接保存main.dart
文件:
一兩秒鐘之內(nèi),您應(yīng)該會看到正在運行的應(yīng)用程序中反映出的更改:
熱重載功能可能并不總是有效,官方文檔official docs可以很好地解釋無法使用的情況,但總體而言,在構(gòu)建UI時可節(jié)省大量時間。
Importing a File
您將希望能夠從創(chuàng)建的其他類中導(dǎo)入代碼,而不是將所有Dart代碼都保存在單個main.dart
文件中。 現(xiàn)在,您將看到一個導(dǎo)入字符串的示例,該示例在需要本地化面向用戶的字符串時會有所幫助。
右鍵單擊lib
并選擇New File
,在lib
文件夾中創(chuàng)建一個名為strings.dart
的文件:
將以下類添加到新文件中:
class Strings {
static String appTitle = "GHFlutter";
}
將以下import
添加到main.dart
的頂部
import 'strings.dart';
更改widget
以使用新的字符串類,以便GHFlutterApp
類如下所示:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(Strings.appTitle),
),
body: Center(
child: Text(Strings.appTitle),
),
),
);
}
}
按下F5
鍵即可構(gòu)建并運行該應(yīng)用,您應(yīng)該不會有任何變化,但是現(xiàn)在您正在使用字符串文件中的字符串。
Widgets
Flutter
應(yīng)用程序中的幾乎每個元素都是widget
。 widget
被設(shè)計為不可變的,因為使用不可變的widget
有助于使應(yīng)用程序UI保持輕便。
您將使用兩種基本類型的小部件:
-
Stateless - 無狀態(tài):僅依賴于自己的配置信息的
widget
,例如圖像視圖中的靜態(tài)圖像。 -
Stateful - 有狀態(tài):需要維護(hù)動態(tài)信息并通過與
State
對象進(jìn)行交互來實現(xiàn)的信息。
無狀態(tài)小部件和有狀態(tài)widget
都在Flutter
應(yīng)用程序中的每幀上重新繪制,不同之處在于,有狀態(tài)widget
將其配置委托給State
對象。
要開始制作自己的widget
,請在main.dart
底部創(chuàng)建一個新類:
class GHFlutter extends StatefulWidget {
@override
createState() => GHFlutterState();
}
您已經(jīng)創(chuàng)建了StatefulWidget
子類,并且您將覆蓋createState()
方法以創(chuàng)建其狀態(tài)對象。 現(xiàn)在,在GHFlutter
上方添加GHFlutterState
類:
class GHFlutterState extends State<GHFlutter> {
}
GHFlutterState
使用GHFlutter
的參數(shù)擴展State
。
制作widget
時的主要任務(wù)是覆蓋將widget
呈現(xiàn)到屏幕時調(diào)用的build()
方法。
在GHFlutterState
中添加一個build()
重寫:
@override
Widget build(BuildContext context) {
?
}
填寫build()
如下:
@override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text(Strings.appTitle),
),
body: Text(Strings.appTitle),
);
}
Scaffold
是用于材料設(shè)計widgets
的容器。 它充當(dāng)widgets
層次結(jié)構(gòu)的根。 您已在Scaffold
中添加了一個AppBar
和一個body
,每個都包含一個Text widget
。
更新GHFlutterApp
,使其使用新的GHFlutter
小部件作為其home
屬性,而不是構(gòu)建自己的支架:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
home: GHFlutter(),
);
}
}
構(gòu)建并運行該應(yīng)用程序,您將看到新的widget
在起作用:
尚未發(fā)生太大變化,但是現(xiàn)在您可以設(shè)置以構(gòu)建新的widget
。
Making Network Calls
之前,您已將strings.dart
文件導(dǎo)入到項目中。 您可以類似地導(dǎo)入Flutter
框架和Dart
中包含的其他軟件包。
例如,您現(xiàn)在將使用框架中可用的包進(jìn)行HTTP
網(wǎng)絡(luò)調(diào)用,并將生成的響應(yīng)JSON
解析為Dart
對象。 在main.dart
頂部添加兩個新導(dǎo)入:
import 'dart:convert';
import 'package:http/http.dart' as http;
您會注意到http
包不可用。 這是因為尚未將其添加到項目中。 導(dǎo)航到pubspec.yaml
文件,然后在dependencies
和cupertino_icons:^ 0.1.2
下添加以下內(nèi)容:
cupertino_icons: ^0.1.2
# HTTP package
http: ^0.12.0+2
注意:注意縮進(jìn)。 保持 http 軟件包聲明的縮進(jìn)與
cupertino_icons
軟件包的縮進(jìn)相同。
現(xiàn)在,當(dāng)您保存pubspec.yaml
文件時,VS Code
中的Flutter
擴展名將運行flutter pub get
命令。 Flutter將獲得聲明的http
軟件包,您的軟件包也將在main.dart
中可用。
現(xiàn)在,您將在main.dart
中看到有關(guān)當(dāng)前未使用的導(dǎo)入的指示器。
Dart
應(yīng)用程序是單線程的,但是Dart提供了對在其他線程上運行代碼以及運行異步代碼的支持,這些異步代碼不會使用async / await
模式阻止UI線程。
您將進(jìn)行異步網(wǎng)絡(luò)調(diào)用以檢索GitHub
團(tuán)隊成員的列表。 在GHFlutterState
的頂部添加一個空列表作為屬性,還添加一個屬性以容納文本樣式:
var _members = [];
final _biggerFont = const TextStyle(fontSize: 18.0);
名稱開頭的下劃線使該類的成員成為私有成員。
要進(jìn)行異步HTTP
調(diào)用,請向GHFlutterState
添加方法_loadData()
:
_loadData() async {
String dataURL = "https://api.github.com/orgs/raywenderlich/members";
http.Response response = await http.get(dataURL);
setState(() {
_members = json.decode(response.body);
});
}
您已經(jīng)在_loadData()
上添加了async
關(guān)鍵字,以告知Dart
它是異步的,并且還在http.get()
調(diào)用上阻塞了await
關(guān)鍵字。 您使用的dataUrl
值設(shè)置為GitHub API
端點,該端點檢索GitHub
組織的成員。
HTTP調(diào)用完成后,您將向回調(diào)傳遞給setState()
,該回調(diào)在UI線程上同步運行。 在這種情況下,您將解碼JSON響應(yīng)并將其分配給_members
列表。
將initState()
重寫添加到GHFlutterState
,該狀態(tài)在初始化狀態(tài)時調(diào)用_loadData()
:
@override
void initState() {
super.initState();
_loadData();
}
Using a ListView
現(xiàn)在您已經(jīng)有了Dart
成員列表,您需要一種在UI列表中顯示它們的方法。 Dart
提供了一個ListView
widget
,可讓您在列表中顯示數(shù)據(jù)。 ListView
的行為類似于Android
上的RecyclerView
和iOS上的UITableView
,在用戶滾動列表以實現(xiàn)平滑滾動性能時回收視圖。
將_buildRow()
方法添加到GHFlutterState
中:
Widget _buildRow(int i) {
return ListTile(
title: Text("${_members[i]["login"]}", style: _biggerFont)
);
}
您將返回一個ListTile widget
,該widget
顯示從ith
成員的JSON解析的login
值,并使用您之前創(chuàng)建的文本樣式。
更新GHFlutterState
的構(gòu)建方法,使其主體為ListView.builder
:
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
}),
你已經(jīng)添加padding
,itemCount
設(shè)置為成員的數(shù)量,并使用_buildRow()
為給定的位置設(shè)置itemBuilder
。
您可以嘗試熱重載,但可能會收到“Full restart may be required”
消息。 如果是這樣,請按F5鍵構(gòu)建并運行該應(yīng)用程序:
進(jìn)行網(wǎng)絡(luò)通話,解析數(shù)據(jù)并在列表中顯示結(jié)果就是這么簡單!
Adding dividers
要將分隔符添加到列表中,您需要將item
數(shù)量加倍,然后在列表中的位置為奇數(shù)時返回Divider widget
。 如下更新GHFlutterState
的構(gòu)建方法:
body: ListView.builder(
itemCount: _members.length * 2,
itemBuilder: (BuildContext context, int position) {
if (position.isOdd) return Divider();
final index = position ~/ 2;
return _buildRow(index);
}),
確保不要錯過itemCount
上的* 2
。 有了分隔線后,您已經(jīng)從構(gòu)建器中刪除了padding
。 在itemBuilder
中,您要么返回Divider()
,要么通過整數(shù)除法并使用_buildRow()
來構(gòu)建行項目來計算新索引。
嘗試熱重載,您應(yīng)該在列表上看到分隔線:
要將padding
重新添加到每一行中,您想在_buildRow()
中使用Padding widget
:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text("${_members[i]["login"]}", style: _biggerFont)
)
);
}
ListTile
現(xiàn)在是padding widget
的子widget
。 熱重新加載以查看行上的padding
,而不是分隔線上的padding
。
Parsing to Custom Types
在上一節(jié)中,JSON解析器將JSON響應(yīng)中的每個成員作為Dart Map
類型添加到_members
列表中,相當(dāng)于Kotlin
中的Map
或Swift
中的Dictionary
。
但是,您還希望能夠使用自定義類型。
在main.dart
文件中添加一個新的Member
類型:
class Member {
final String login;
Member(this.login) {
if (login == null) {
throw ArgumentError("login of Member cannot be null. "
"Received: '$login'");
}
}
}
成員具有login
屬性和一個構(gòu)造函數(shù),如果登錄值為null
,則該構(gòu)造函數(shù)將拋出錯誤。
更新GHFlutterState
中的_members
聲明,以便它是Member
對象的列表:
var _members = <Member>[];
更新_buildRow()
以在Member
對象上使用login
屬性,而不是使用映射上的login
鍵:
title: Text("${_members[i].login}", style: _biggerFont)
現(xiàn)在,更新發(fā)送到_loadData()
中的setState()
的回調(diào),以將解碼后的映射轉(zhuǎn)換為Member
對象并將其添加到成員列表中:
setState(() {
final membersJSON = json.decode(response.body);
for (var memberJSON in membersJSON) {
final member = Member(memberJSON["login"]);
_members.add(member);
}
});
如果嘗試進(jìn)行熱重裝,您可能會看到一個錯誤,但是停止并按F5鍵來構(gòu)建和運行該應(yīng)用,您應(yīng)該會看到與以前相同的屏幕,除了現(xiàn)在使用新的Member
類。
Downloading Images with NetworkImage
來自GitHub
的每個成員都有其頭像的URL。 現(xiàn)在,您將該頭像添加到Member
類中,并在應(yīng)用程序中顯示頭像。
更新Member
類以添加一個avatarUrl
屬性,該屬性不能為null
:
class Member {
final String login;
final String avatarUrl;
Member(this.login, this.avatarUrl) {
if (login == null) {
throw ArgumentError("login of Member cannot be null. "
"Received: '$login'");
}
if (avatarUrl == null) {
throw ArgumentError("avatarUrl of Member cannot be null. "
"Received: '$avatarUrl'");
}
}
}
使用NetworkImage
和CircleAvatar
widget
更新_buildRow()
以顯示頭像:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text("${_members[i].login}", style: _biggerFont),
leading: CircleAvatar(
backgroundColor: Colors.green,
backgroundImage: NetworkImage(_members[i].avatarUrl)
),
)
);
}
通過將頭像設(shè)置為ListTile
的leading
屬性,它將在行內(nèi)標(biāo)題之前顯示。 您還使用Colors
類在圖片上設(shè)置了背景色。
現(xiàn)在更新_loadData()
以在創(chuàng)建新Member
時使用映射中的“ avatar_url”
值:
final member = Member(memberJSON["login"], memberJSON["avatar_url"]);
使用F5停止,構(gòu)建和運行該應(yīng)用程序。 您會在每一行中看到您的成員頭像:
Cleaning the Code
現(xiàn)在,您的大多數(shù)代碼都位于main.dart
文件中。 為了使代碼更簡潔,您可以重構(gòu)已添加到文件中的widget
和其他類。
在lib
文件夾中創(chuàng)建名為member.dart
和ghflutter.dart
的文件。 將Member
類移至member.dart
,并將GHFlutterState
和GHFlutter
類移至ghflutter.dart
。
您在member.dart
中不需要任何import
語句,但是ghflutter.dart
中的導(dǎo)入應(yīng)如下所示:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'member.dart';
import 'strings.dart';
您還需要更新main.dart
中的導(dǎo)入,以便整個文件包含以下內(nèi)容:
import 'package:flutter/material.dart';
import 'ghflutter.dart';
import 'strings.dart';
void main() => runApp(GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
home: GHFlutter(),
);
}
}
按下F5來構(gòu)建和運行該應(yīng)用程序,您應(yīng)該看不到任何更改,但是代碼現(xiàn)在更簡潔了。
Adding a Theme
您可以通過將theme
屬性添加到您在main.dart
中創(chuàng)建的MaterialApp
中,輕松地將主題添加到應(yīng)用中:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
theme: ThemeData(primaryColor: Colors.green.shade800),
home: GHFlutter(),
);
}
}
您將綠色用作主題的“材料設(shè)計”顏色值。
按下F5來構(gòu)建和運行應(yīng)用程序,以查看新的主題:
大多數(shù)應(yīng)用程序屏幕截圖均來自Android模擬器。 您還可以在iOS模擬器中運行最終的主題應(yīng)用程序:
這就是我所說的跨平臺!
有關(guān)Flutter
和Dart
的知識還有很多。 最好的起點是:
-
flutter.dev上的
Flutter
主頁。 您會發(fā)現(xiàn)很多很棒的文檔和其他信息。 - 在此處here查看可用的
widgets
。 - 這里here有一個很好的指南供
Android
開發(fā)人員過渡到使用Flutter
。 - 適用于
React Native
開發(fā)人員的類似指南在這里here。
后記
本篇主要講述了Flutter 入門,感興趣的給個贊或者關(guān)注~~~