Flutter App開發教學(二)

C.J
11 min readJan 26, 2024

--

延續上一篇文章,本篇進一步把Flutter開發的知識做介紹,並且提供進一步的實作。

https://medium.com/@w96284ps/flutter-app%E9%96%8B%E7%99%BC%E6%95%99%E5%AD%B8-%E4%B8%80-64661160906f

使用照片的方法

  • 如果沒有assets資料夾,可以直接建立一個。
  • 將你要的照片拖曳到資料夾中
  • 到pubspec.yaml註冊你的照片
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

# To add assets to your application, add an assets section, like this:
assets:
- assets/kithub_ico_full1024.png
- assets/bear.jpeg
- assets/snake.jpeg
  • 到你要呈現照片的地方Coding
//圖片Widget
Padding(
padding: EdgeInsets.all(20.0),
child: Image.asset("assets/kithub_ico_full1024.png"),
),

Stateful Wedgit 介紹

StatefulWidget 和 StatelessWidget :在Flutter中,StatefulWidget 和 StatelessWidget 是兩種不同的Widget類型,主要差異在於是否包含狀態(state)。當Widget的外觀不會隨時間變化時,使用StatelessWidget較精簡。當Widget的外觀需要根據某些內部狀態的變化而變化時,應該使用StatefulWidget。

設計App:

設計一個熱播電影頁面,當點選Button”上一部” or “下一部”後更改圖片,這是一個會更改State的應用,需要使用StatefulWidget 。

結合換頁的的Navigator功能,並新增電影時刻表分頁。

實作APP:

  • 加入照片資源:
  • 註冊照片
  • 建立dart檔案:
  • 編輯main.dart:

我使用Stateful物件搭建一個Scaffold架構,我先建立現在的圖片檔案名稱,並且建立一個指針,到時候點選按鈕後改指針的數值,後續同步修改imageFileName。

//建立Stateful物件


class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);


@override
State<MyApp> createState() => _MyAppState();
}


//導入State協定,並繼續編輯MyApp的內容
class _MyAppState extends State<MyApp> {
//建立現在的圖片檔案名稱
late String imageFileName = "m1.jpg";


//建立一個指針,到時候點選按鈕後改指針的數值,然後同步修改imageFileName。
int imgPointer = 1;


...

在Body合適的地方放入Button,點擊後就幫指針數字+1,然後按照指針的數字修改imageFileName,再來就是發動setState(() {})把Stateful Wedget更新囉。

                 ElevatedButton(
onPressed: () {
if (imgPointer == 10) {
imgPointer = 1;
} else {
imgPointer += 1;
}
imageFileName = "m$imgPointer.jpg";
setState(() {});
},
child: Text("下一部電影"),
),

為了換頁,我新增一個按鈕,當按下後我們就看得到下一頁了。

Builder(
builder: (context) => ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TimeTablePage()),
);
},
child: const Text('查看時間表'),
),
),

這邊我寫的方法並不是直接建立一個ElevatedButton,而是用Builder包裹這個ElevatedButton,這是因為直接放一個按鈕來執行會有錯誤。下圖就是報錯的內容,這個錯誤通常發生於 Navigator.push 調用時的 context 不正確。但我已經確認了ElevatedButton在Widget build(BuildContext context)的下面,意思是Navigator往ElevatedButton上層找會找到Widget build的context,但例外就是層層疊疊的Wedget會讓Navigator找不到,所以用Builder打包ElevatedButton,讓Navigator往上找一層就看到context,這樣就可以順利換頁了。

完整的Code如下:

import 'package:flutter/material.dart';
import 'package:test3/image_show.dart';
import 'package:test3/time_table_page.dart';


void main() => runApp(const MyApp());


//建立Stateful物件
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);


@override
State<MyApp> createState() => _MyAppState();
}


//導入State協定,並繼續編輯MyApp的內容
class _MyAppState extends State<MyApp> {
//建立現在的圖片檔案名稱
late String imageFileName = "m1.jpg";


//建立一個指針,到時候點選按鈕後改指針的數值,然後同步修改imageFileName。
int imgPointer = 1;


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('1月熱播電影'),
backgroundColor: Colors.green,
),
//用Center會把內容物排版在中間,比較美
body: Center(
//用Colum把內容物由上而下排列。
child: Column(
children: <Widget>[
//圖片顯示
ImageShow(
imgName: imageFileName,
),
//用Row包裹按鈕區,這樣排版比較美。
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 上一步電影
ElevatedButton(
onPressed: () {
if (imgPointer == 1) {
imgPointer = 10;
} else {
imgPointer -= 1;
}
imageFileName = "m$imgPointer.jpg";
setState(() {});
},
child: Text("上一部電影"),
),


// 查看時間表
Builder(
builder: (context) => ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TimeTablePage()),
);
},
child: const Text('查看時間表'),
),
),


// 下一步電影
ElevatedButton(
onPressed: () {
if (imgPointer == 10) {
imgPointer = 1;
} else {
imgPointer += 1;
}
imageFileName = "m$imgPointer.jpg";
setState(() {});
},
child: Text("下一部電影"),
),
],
),
],
),
),
),
);
}
}
  • 編輯image_show.dart
import "package:flutter/material.dart";


class ImageShow extends StatelessWidget {
final String imgName;


const ImageShow({Key? key, required this.imgName}) : super(key: key);


@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 0.6, // 寬高比例
child: FractionallySizedBox(
widthFactor: 1, // 寬度佔滿父容器
heightFactor: 1, // 高度佔滿父容器
child: Image.asset(
"assets/$imgName",
fit: BoxFit.cover, // 保持原本的比例,填滿整個容器
),
),
);
}
}
  • 編輯time_table_page.dart
import 'package:flutter/material.dart';


class TimeTablePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('1月電影時刻表'),
backgroundColor: Colors.green,
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context); // 返回到上一頁
},
child: const Text('Go back to Home Page'),
),
),
);
}
}

--

--

C.J

一個文組轉職程式設計的奇幻旅程,說說自己在旅途中的感想跟一些撞牆的故事,希望我的文章可以幫到你。