[4] 快速學會 Flutter 基礎入門
0. 影片教學
本篇文章是一個步驟指導,建議可以搭配影片一起學習:
1. 安裝 Flutter 開發環境
Flutter
首先到 docs.flutter.dev/get-started/install/macos 下載 flutter
下載完成後,選擇要解壓縮的目錄之後進行解壓縮,因為它不需要進行安裝,所以位置要選一個不會被變動到的位置。
設定路徑:
$ vim .bash_profile
將 flutter 路徑填入:
$ export PATH=$PATH:/Users/jakechang/Downloads/flutter/bin
重新讀取設定檔案:
$ source ~/.bash_profile
檢查缺少的軟體:
$ flutter doctor
會依序檢查電腦是否有安裝這些軟體:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.0.3, on macOS 12.4 21F79 darwin-x64, locale zh-Hant-TW)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] IntelliJ IDEA Community Edition (version 2022.1.3)
[✓] VS Code (version 1.68.0)
[✓] Connected device (3 available)
[✓] HTTP Host Availability
要全部打勾才可以順利執行 flutter,這邊是我已經全部安裝好的狀態,一般初學者大致上會缺少以下軟體 Android / Xcode / IntelliJ,這邊就根據這些軟體的安裝來解說。
Android Studio
- 到這邊下載 developer.android.com/studio ,直接進行安裝
- 完成之後要安裝 cmdline-tools ,可以參考這裡:stackoverflow.com/questions/68236007/i-am-g..
- 回到命令列,執行 flutter doctor --android-licenses
- 最後開啟 Android Studio 新增一個專案,然後設定第一個 Android 模擬器
Xcode
- 直接在 App Store 下載安裝 Xcode
- 安裝 CocoaPods,cocoapods.org
- 最後一樣開啟 Xcode 新增一個專案,然後開啟一個 iOS 模擬器
IntelliJ IDEA
IntelliJ IDEA 是一個可以開發 flutter 的 IDE 工具,到這邊下載 jetbrains.com/idea/download/#section=mac
在 IntelliJ IDEA 上安裝 Flutter 套件:Preferences -> Plugins,搜尋 Flutter 進行安裝
都完成之後,再重新執行一次 flutter doctor ,確認每個項目都已經打勾
2. Hello Flutter
主程式為 main.dart,修改 main 這個入口函式:
void main() {
runApp(const Center(
child: Text("Hello Flutter", textDirection: TextDirection.ltr),
));
}
重新執行模擬器,螢幕上會出現 Hello Flutter
可以使用 stless 的關鍵字建立 class:
void main() {
runApp(const DemoApp());
}
class DemoApp extends StatelessWidget {
const DemoApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Center(
child: Text("Hello Text", textDirection: TextDirection.ltr,),
);
}
}
3. Container
Center 容器只能調整位置,不能增加其它屬性,例如背景顏色。若要放入其它屬性,就必須使用 Container 容器:
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
alignment: Alignment.center,
child: const Text("Hello Text", textDirection: TextDirection.ltr,),
);
}
其它 Container 屬性可參考:api.flutter.dev/flutter/widgets/Container-c..
4. Column
如果要放兩個以上的 Text 元件,就必須要使用 Column
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const <Widget>[
Text("Hello Text1", textDirection: TextDirection.ltr,),
Text("Hello Text2", textDirection: TextDirection.ltr,),
]
),
);
}
5. MaterialApp
一個具有 header 與 body 的排版方式:
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Header"),
),
body: const Center(
child: Text("Body"),
),
),
);
}
6. Button
一個最簡單的 Button 使用方式:
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Header"),
),
body: Center(
child: RaisedButton(
onPressed: btnClickEvent,
child: const Text("Button"),
)
),
)
);
}
void btnClickEvent() {
print("hello button");
}
同時放上 Button 與 Text
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Header"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RaisedButton(
onPressed: btnClickEvent,
child: const Text("Button"),
),
const Text("Text", textDirection: TextDirection.ltr,),
],
)
),
)
);
}
6. Button 與 Text 的互動
接下來示範按鈕按下去, Text 會一直累加數字。因為這邊要去即時更改 UI 元件,需要將 Widget 狀態設定為 Stateful,另外這邊也將按鈕的事件直接寫在 onPressed 裡面。
void main() {
runApp(const TestApp());
}
class TestApp extends StatefulWidget {
const TestApp({Key? key}) : super(key: key);
@override
State<TestApp> createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
int count = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Header"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RaisedButton(
onPressed: (){
setState((){
count++;
print("$count");
});
},
child: const Text("Button"),
),
Text(count.toString(), textDirection: TextDirection.ltr,),
],
)
),
)
);
}
}
一個簡單的範例
最後來製作一個簡單的登入頁面,輸入完帳號密碼之後,可以跳轉到第二頁,並且把輸入的資料帶入呈現。
首先先來製作登入頁面:
void main() {
runApp(const DemoApp2());
}
class DemoApp2 extends StatefulWidget {
const DemoApp2({Key? key}) : super(key: key);
@override
State<DemoApp2> createState() => _DemoApp2State();
}
class _DemoApp2State extends State<DemoApp2> {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
int count = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Header"),
),
body: Container(
margin: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: emailController,
decoration: const InputDecoration(hintText: "Email"),),
TextField(
controller: passwordController,
decoration: const InputDecoration(hintText: "Password"),),
Center(
child: RaisedButton(
onPressed: onPressed,
child: const Text("Send"),
),
)
],
)
),
),
);
}
void onPressed() {
print(emailController.text + passwordController.text);
}
}
將 button 按鈕事件改寫成:
RaisedButton(
onPressed: (){
print(emailController.text + passwordController.text);
},
child: const Text("Send"),
)
加入 Page2 頁面,一樣也是使用 Stateful:
class Page2 extends StatefulWidget {
const Page2({Key? key}) : super(key: key);
@override
State<Page2> createState() => _Page2State();
}
class _Page2State extends State<Page2> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Pages"),
),
body: Center(
child: Text("page2"),
),
);
}
}
按鈕加入跳轉到第二頁:
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => const Page2()));
},
child: const Text("Send"),
),
修正主程式後,就可以完成跳轉功能:
runApp(const MaterialApp(
home: DemoApp2(),
));
最後要將 email 與 password 兩個參數帶入給第二頁,所以在 Page2 先新增兩個變數:
class Page2 extends StatefulWidget {
final String email;
final String password;
const Page2({Key? key, required this.email, required this.password}) : super(key: key);
@override
State<Page2> createState() => _Page2State();
}
class _Page2State extends State<Page2> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Pages"),
),
body: Center(
child: Text("Email: $widget.email, Password: $widget.password"),
),
);
}
}
修改第一頁按鈕事件:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Page2(email: emailController.text, password: passwordController.text,)
)
);