官方英文原文: flutter.io/flutter-for…
說明:此文上接 給Android開發者的Flutter指南(上)。
四、工程結構與資源
1. 在哪放置不同解析度(resolution-dependent
)的圖片檔案?
在Android
中,resources
與assets
是兩個獨立的資料夾,而在Flutter
中,只存在assets
,所有放在Android
的res/drawable-*
資料夾中的檔案全都放在Flutter
中的assets
資料夾中。
Flutter
跟ios
一樣遵循簡單的基於密度(density-base
)的格式,assets
包含1.0x
、2.0x
、3.0x
或者更高乘數,Flutter
中並沒有dp
這一說,而是使用與裝置無關的邏輯畫素,在devicePixelRatio 中描述了單個邏輯畫素與物理畫素的關係。
對應於Android
密度的關係如下:
Android density qualifier | Flutter pixel ratio |
---|---|
ldpi | 0.75x |
mdpi | 1.0x |
hdpi | 1.5x |
xhdpi | 2.0x |
xxhdpi | 3.0x |
xxxhdpi | 4.0x |
assets
可以存在與任意資料夾中,Flutter
沒有規定資料夾結構,因此即使你將assets
放在與pubspec.yaml
相同的位置,Flutter
也能正確的讀取到。
在Flutter 1.0 beta2
以前,在Flutter
中定義的assets
不能被本地層(native
層)訪問,同理,本地層的assets
和resources
檔案也不能被Flutter
訪問,因為它們存在於分立的資料夾中。
而從Flutter 1.0 beta2
開始,assets
儲存於本地層的assets
資料夾中,且可以被本地層通過AssetManager
訪問,但是Flutter
依然不能訪問本地層的resources
和assets
:
val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png")
複製程式碼
如果向Flutter
工程中新增一個叫做my_icon.png
的圖片資源,比方說,把它放到一個叫做Images
的資料夾中(名字是任意的),那麼我們應該把1.0x
的基礎圖片放到Images
的根目錄,而其他大小,比如2.0x
、3.0x
等大小的圖片分別放在Images
中名字為2.0x
、3.0x
的子資料夾中,如下示例路徑:
images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image
複製程式碼
接著在pubspec.yaml
檔案中宣告這些圖片資源:
assets:
- images/my_icon.jpeg
複製程式碼
接著就可以通過AssetImage
訪問圖片資源了:
return AssetImage("images/a_dot_burr.jpeg");
複製程式碼
或者直接在Image
控制元件中使用:
@override
Widget build(BuildContext context) {
return Image.asset("images/my_image.png");
}
複製程式碼
2. 在哪儲存strings
字串資源?怎樣處理本地化?
目前Flutter
沒有像系統宣告字串資源那樣的形式,因此當前最佳方式就是將字串宣告成static
形式,然後儲存在一個特定的類中,之後都從這個類中獲取,如下示例:
class Strings {
static String welcomeMessage = "Welcome To Flutter";
}
複製程式碼
然後在程式碼中這樣呼叫這些字串資源:
Text(Strings.welcomeMessage)
複製程式碼
Flutter
對Android
中的輔助功能有了基礎支援,目前工作還在進行中。開發者可以通過檢視intl package來獲取關於國際化和本地化的資訊。
3. 對應於Gradle
檔案的是啥?怎麼新增依賴?
Android
中,依賴新增在gradle
構建指令碼中,而Flutter
則是使用Dart
自己的構建系統和Pub
包管理器,Flutter
的構建工具會將本地Android
和IOS
的構建工作委託到它們各自的構建系統。
gradle
檔案在Flutter
工程目錄的android
資料夾下,只有需要針對單個平臺新增本地依賴時才新增到gradle
,其他普通場景直接在pubspec.yaml
新增外部依賴就好了。找包?上 Pub
五、 Activity 與 fragment
Note: 你幾乎不會想讓Android
因為Flutter
應用而重啟activity
,因為它違背了Android
文件中的提出的建議,因此例如需要支援分屏,那麼也需要新增screenLayout
和density
。
1. Flutter中與activity
和fragment
相對應的是啥?
在Android
中,activity
代表了使用者的單個焦點所在,而Fragment
則代表使用者互動及介面的一個行為或者說一個部分。Fragment
可以模組化你的程式碼,用來為大屏裝置組合出複雜的使用者互動介面、以及比例化應用UI
。在Flutter
中,這兩者的概念都彙集到在Widget
。
正如在Intent
部分所提到的,在Flutter
中,Widget
就代表著螢幕,因為在Flutter
中萬物皆Widget
。我們使用Navigator
來切換Route
,而Route
代表著不同螢幕或頁面、亦或只是不同狀態、或是相同資料的渲染效果。
2. 如何監聽Android中activity的生命週期事件?
在Android
中,我們會複寫activity
中的生命週期方法,或者在Application
中註冊ActivityLifecycleCallbacks
,而在Flutter
中,並沒有這個概念,但是我們可以通過給WidgetBinding
觀察者下個鉤子(hook
)來監聽生命週期事件,然後監聽didChangeAppLifecycleState()
的變化事件,其生命週期事件如下:
inactive
: 應用處於非活動狀態,此時不在接收使用者輸入。這個事件只在IOS
中有效,因為在Android
中沒有與這個狀態相對映的事件。paused
: 當前應用對使用者可見,但不在響應使用者輸入,且執行在後臺。等同於Android
中的onPause
。resumed
: 應用可見且正在響應使用者輸入。等同於Android
中的onPostResume()
suspending
: 此時應用掛起了。等同於Android
中的onStop
;不會觸發IOS
上的事件,因為沒有與之對映的狀態事件。
關於這些狀態的更多細節,請檢視 AppLifecycleStatus documentation..
你可能注意到了,只有那麼幾個可用的Android
生命週期事件。這是因為FlutterActivity
已經捕獲了幾乎所有的生命週期事件,並將它們傳送到了Flutter
引擎中,然後很多事件都被它遮蔽掉了。Flutter
會管理引擎的啟動和關閉動作,因而大多數情況下我們都沒多大必要在Flutter
層監聽activity
生命週期事件。如果要監聽或者釋放本地層的資源,那麼可以在本地層以任何頻率進行。
以下示例描述瞭如何監聽Activity
中的生命週期事件:
import 'package:flutter/widgets.dart';
class LifecycleWatcher extends StatefulWidget {
@override
_LifecycleWatcherState createState() => _LifecycleWatcherState();
}
class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver {
AppLifecycleState _lastLifecycleState;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_lastLifecycleState = state;
});
}
@override
Widget build(BuildContext context) {
if (_lastLifecycleState == null)
return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr);
return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.',
textDirection: TextDirection.ltr);
}
}
void main() {
runApp(Center(child: LifecycleWatcher()));
}
複製程式碼
六、佈局
1. 對應於LinearLayout的是啥?
在Android
中,LinearLayout
用於橫向和縱向佈局控制元件,而在Flutter
中則是使用Row
或Column
控制元件來實現與之相同的行為。
如下示例,當佈局中子控制元件重複利用率比較高時,使用這種容器控制元件就很方便了:
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
複製程式碼
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Column One'),
Text('Column Two'),
Text('Column Three'),
Text('Column Four'),
],
);
}
複製程式碼
更多線性佈局細節,請看這裡 Flutter For Android Developers : How to design LinearLayout in Flutter?.
2. 對應於RelativeLayout的是啥?
在Flutter
實現與之相同效果的方法很少。可以通過組合Column
、Row
和 Stack
控制元件來實現類似效果,也可以通過控制元件的構造器指定其子控制元件在它內部的佈局規則來實現。
關於如何構建一個RelativeLayout
,可以檢視這裡 StackOverflow.
3. 對應於ScrollView的是啥?
在Flutter
中,實現ScrollVIew
的最簡單方式就是使用LsitView
。這看起來好像有點誇張,但是在Flutter
中,ListView
控制元件既是Android
中的ScrollView
,也是ListView
。
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
複製程式碼
4. 在Flutter中如何處理螢幕旋轉?
在AndroidManifest.xml
中新增如下配置即可:
android:configChanges="orientation|screenSize"
複製程式碼
七、檢測手勢與觸控事件處理
1. 如何為控制元件新增 OnClick 事件監聽器?
在Android
中,是通過呼叫View
的setOnClickListener
方法繫結監聽器的,而在Flutter
中可以有以下兩種新增觸控事件監聽器的方式:
- 如果控制元件支援事件檢測,那麼可以給它傳入一個函式用以處理這個事件。比如,
RaisedButton
包含一個onPressd
引數:
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
print("click");
},
child: Text("Button"));
}
複製程式碼
- 如果控制元件並不支援事件檢測,那麼可以將這個控制元件包裹在
GestureDetector
中,然後傳入一個函式給onTap
引數:
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
child: FlutterLogo(
size: 200.0,
),
onTap: () {
print("tap");
},
),
));
}
}
複製程式碼
2. 如何處理控制元件中的其他手勢?
使用GestureDetector
可以監聽大量手勢,例如:
-
單擊(
Tab
)onTabDown
: 觸發單擊事件的指標已經開始與螢幕在特定點上進行聯絡onTapUp
: 觸發單擊事件的指標停止與螢幕在特定點上的聯絡onTap
: 形成了單擊事件onTapCancel
: 觸發時會導致之前觸發onTabDown
的指標無法形成單擊事件
-
雙擊(
Double Tab
)onDoubleTab
: 使用者在螢幕的同一個點上連續快速點選了兩次
-
長按
Long Press
onLongPress
: 指標持續在同一個位置上與螢幕進行了一段事時間的聯絡。
-
垂直拖動(
Vertical drag
)onVerticalDragStart
: 指標已經和螢幕聯絡,並且可能開始垂直移動。onVerticalDragUpdate
: 正在和螢幕聯絡的指標已經開始在垂直方向上進行移動。onVerticalDragEnd
: 之前與螢幕進行聯絡且在垂直方向移動的指標,現在已經不需要再與螢幕聯絡了,並且在停止聯絡的瞬間,指標依然以一定的速度移動。
-
水平拖動(
Horizontal drag
)(請參考上面的垂直拖動)onHorizontalDragStart
onHorizontalDragUpdate
onHorizontalDragEnd
下面示例描述了在使用GestureDetector
雙擊Flutter logo
時,logo
旋轉的效果:
AnimationController controller;
CurvedAnimation curve;
@override
void initState() {
controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
child: RotationTransition(
turns: curve,
child: FlutterLogo(
size: 200.0,
)),
onDoubleTap: () {
if (controller.isCompleted) {
controller.reverse();
} else {
controller.forward();
}
},
),
));
}
}
複製程式碼
八、ListView 與 Adapter
1. 在Flutter中,替代ListView的是啥?
在Flutter
中,等效於ListView
的是...ListView
對於Android
的ListView
,我們會建立一個介面卡(adapter
)傳給ListView
,然後ListView
渲染這個介面卡返回的每一行資料。而我們必須確保每行資料最後都被我們回收,否則就可能會導致顯示錯亂和記憶體問題。
而因為Flutter
的控制元件是不可變的,我們給ListView
傳入一個集合的控制元件,然後Flutter
就會自己確保滑動的流暢、快速了。
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView(children: _getListData()),
);
}
_getListData() {
List<Widget> widgets = [];
for (int i = 0; i < 100; i++) {
widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i")));
}
return widgets;
}
}
複製程式碼
2. 我怎麼知道列表的哪個條目被點選了呢?
在Android
中,ListView
中包含了查詢點選了哪個條目的onItemClickListener
監聽器,而在Flutter
中,則是通過傳入列表的控制元件來處理觸控動作。
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView(children: _getListData()),
);
}
_getListData() {
List<Widget> widgets = [];
for (int i = 0; i < 100; i++) {
widgets.add(GestureDetector(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row $i")),
onTap: () { // 給列表中的每個控制元件新增一個點選事件監聽器
print('row tapped');
},
));
}
return widgets;
}
}
複製程式碼
3. 如何動態更新ListView?
在Android
中是通過更新介面卡,然後呼叫notifyDataSetChanged
來處理。
而在Flutter
中,如果你是在setState()
方法中更新控制元件集,那麼你會很快看到你的資料並沒有被更新,這是因為當setState()
被呼叫時,Flutter
渲染引擎會在控制元件樹中搜尋是否存在發生改變的東西,而當它找到了你的ListView
,它會進行==
檢查,然後判定這兩個ListView
是相同的,因而不會發生任何改變,也就不會請求更新。
一個簡單更新ListView
的方法就是,在setState()
方法中建立一個新的List
,然後將舊集合的資料拷貝過來。這一解決方案是簡單,但是不推薦在資料量大的時候使用,如下示例:
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getRow(i));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView(children: widgets),
);
}
Widget getRow(int i) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row $i")),
onTap: () {
setState(() {
widgets = List.from(widgets);
widgets.add(getRow(widgets.length + 1));
print('row $i');
});
},
);
}
}
複製程式碼
一個高效且有效的方式是通過ListView.Builder
來建立ListView
,這種方式在你的資料量巨大或者需要動態改變資料的情況下非常有用,這實質上跟Android
中的RecyclerView
等價了,因為它會自動回收列表資料:
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getRow(i));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
}));
}
Widget getRow(int i) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row $i")),
onTap: () {
setState(() {
widgets.add(getRow(widgets.length + 1));
print('row $i');
});
},
);
}
}
複製程式碼
與建立ListView
不同的是,建立ListView.Build
需要傳入兩個主要引數,一個是初始列表的長度,一個是itemBuild
函式(構建列表條目的函式)。
itemBuild
與Android
中的adapter#getView()
方法類似,它給你一個位置,然後需要返回你需要在對應位置上渲染的行。
最後注意到, onTab
函式已經不需要在創新建立資料列表了,取而代之的是通過.add()
來新增到其中。
九、文書處理
1. 如何給文字控制元件設定字型?
在Android SDK(Android O)
,可以建立一個Font
資源,然後作為FontFamily
引數傳入TextView
。而在Flutter
中,則是將字型檔案放到一個資料夾中,然後在pubspec.yaml
中引用即可,跟引入圖片是類似的。
fonts:
- family: MyCustomFont
fonts:
- asset: fonts/MyCustomFont.ttf
- style: italic
複製程式碼
然後在Text
控制元件中使用:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: Text(
'This is a custom font text',
style: TextStyle(fontFamily: 'MyCustomFont'),
),
),
);
}
複製程式碼
2. 如何給Text控制元件定義風格?
除了字型,我們還可以定義Text
控制元件的其他風格屬性,Text
控制元件的風格引數中包含一個TextStyle
物件,我們可以定義它的很多引數,例如:
- color
- decoration
- decorationColor
- decorationStyle
- fontFamily
- fontSize
- fontStyle
- fontWeight
- hashCode
- height
- inherit
- letterSpacing
- textBaseline
- wordSpacing
十、表單輸入(Form input)
有關表單的更多資訊請檢視Flutter cookbook 中的這裡Retrieve the value of a text field。
1. 對應於輸入框中的“hint”的是啥?
在Flutter
中,可以通過給Text
控制元件傳入一個InputDecoration
物件來顯示“hint
”或者佔位字元,如下示例:
body: Center(
child: TextField(
decoration: InputDecoration(hintText: "This is a hint"),
)
)
複製程式碼
2. 如何展示驗證錯誤資訊?
和顯示hint
一樣,傳一個InputDecoration
物件給Text
控制元件的建構函式即可。
但是你肯定不想一開始就顯示錯誤,而是在出現錯誤時才顯示,因此發生錯誤時傳入一個新的InputDecoration
物件即可。
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
String _errorText;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: TextField(
onSubmitted: (String text) {
setState(() {
if (!isEmail(text)) {
_errorText = 'Error: This is not an email';
} else {
_errorText = null;
}
});
},
decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()),
),
),
);
}
_getErrorText() {
return _errorText;
}
bool isEmail(String em) {
String emailRegexp =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = RegExp(emailRegexp);
return regExp.hasMatch(em);
}
}
複製程式碼
十一、Flutter Plugins
1. 如何訪問GPS感測器?
使用geolocator社群外掛
2. 如何訪問相機? 使用這個image_picker
3. 如何登入Facebook?
4. 如何使用Firebase ?
大部分Firebase
函式轉換自 first party plugins,以下是第一批整合的外掛,由Flutter
團隊維護:
- firebase_admob for Firebase AdMob
- firebase_analytics for Firebase Analytics
- firebase_auth for Firebase Auth
- firebase_database for Firebase RTDB
- firebase_storage for Firebase Cloud Storage
- firebase_messaging for Firebase Messaging (FCM)
- flutter_firebase_ui for quick Firebase Auth integrations (Facebook, Google, Twitter and email)
- cloud_firestore for Firebase Cloud Firestore
4. 如何構建自定義的 Native integration(本地整合)
如果沒有找到我們想要的外掛,那麼可以檢視the developing packages and plugins,學習如何構建我們自己的外掛。
Flutter
的外掛架構,類似於EventBus
:發出訊息給接收器處理,接收器處理後將結果返回過來。在這裡,接收器程式碼執行在本地層,也就是Android
或IOS
.
5. 如何在Flutter應用中使用NDK?
如果你已經在Android
應用中使用了NDK
,並且想要讓Flutter
也能使用到這些類庫,那麼就需要構建自定義外掛了。
首先讓自定義的外掛能與Android
應用互動,即在Android
應用中通過JNI
呼叫native
函式,一旦拿到拿到結果了,就回送給Flutter
,然後渲染結果。
目前不支援直接從Flutter
中呼叫native
程式碼。
十二、 主題(themes)
1. 如何給應用定製主題?
Flutter
自帶Material Design
風格的主題,不像Android
可以在XML
檔案中宣告主題,然後在AndroidManifest.xml
中使用。在Flutter
中是在頂層控制元件中宣告主題的。
如果想要在應用中充分使用Material
元件,那麼可以使用MaterialApp
作為應用的入口。MaterialApp
包含有大量的實現了Material Design
風格的通用控制元件,它建立在WidgetsApp
之上,只是新增了具有Material
特性的功能。
同樣也可以使用WidgetsApp
作為應用控制元件,它提供了一些相同的功能,但不如MaterialApp
豐富。
想要在任意子元件上自定義顏色和風格的話,那麼給MaterialApp
傳入一個ThemeData
物件,例如,在下面示例中,初始樣本顯示為藍色,而文字選擇後顯示為紅色。
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
textSelectionColor: Colors.red
),
home: SampleAppPage(),
);
}
}
複製程式碼
十三、資料庫與本地儲存
1. 怎樣訪問Shared Preference?
在Flutter
中,可以通過Shared_Preferences plugin來實現這些功能,這個外掛包含了Shared Preferences
和NSUserDefaults
(ios
平臺)
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: _incrementCounter,
child: Text('Increment Counter'),
),
),
),
),
);
}
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
prefs.setInt('counter', counter);
}
複製程式碼
2. 如何訪問SQLite資料庫?
使用SQFlite外掛
十五、通知
1. 如何設定推送通知?
在Android
中可以使用Firebase Cloud Messaging
給應用設定推送通知。
Flutter
中,通過Firebase_Messaging可以使用到這一功能,更多關於使用Firebase Cloud Messaging API
的資訊,請檢視firebase_messaging外掛文件。