iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
Mobile Development

攜手神隊友ChatGPT:攝護腺自我照護App開發歷程!系列 第 5

D5-Drug介面,揭開藥物的神秘面紗_part1

  • 分享至 

  • xImage
  •  

Part1:今日目標

1.設計靈感: Drug藥物資訊介面
2.程式實作&Dart語言學習: 第一層結構(Drug_Page.dart)
3.Dart語言學習: 建構子、異步方法
4.實作結果

Part2:今日內容

1.設計靈感: Drug藥物資訊介面

將所有藥品資料存成兩個 json 檔案(文字資料清單: DLI.json、藥品圖片超連結清單: DA.json),透過讀取檔案的方式,將所有攝護腺肥大藥品資訊提供給使用者。為了達成上述目標,將 Drug 藥品介面設計成兩層結構:

  • 第一層結構: 藥品滾動式列表摘要,透過滑動式卷軸,將常見的幾種藥品的英文品名、適應症和圖示,以摘要列表的方式讓使用者查閱。
  • 第二層結構: 目標是為每種藥品提供更詳盡的資訊,包括: 中文品名、英文品名、適應症、副作用、劑型、常見用量和注意事項。這個層次的結構是當使用者點選了第一層結構中的某個藥品後所顯示的介面。在這個介面上,將會呈現該藥品的全面資訊。(明日文章內容)

2.程式實作: 第一層結構(Drug_Page.dart)

Drug_Page.dart: 這支程式碼表示一個 Flutter 應用程式的介面,該介面包含一個藥物列表,並且能夠載入資料並根據資料動態顯示相關資訊。該程式碼遵循 Flutter 的小部件樹結構,並使用了許多 Flutter 的 UI 元件,例如AppBarListView.builderFutureBuilder,以實現一個動態且具互動性的使用者介面。
https://ithelp.ithome.com.tw/upload/images/20230908/20120073060uVQSTut.png

(1)DrugHomePage類別:

  • 這是一個 StatefulWidget 類別,表示這個小部件是有狀態的,可以隨不同狀態進行更改。
  • 它有一個名為title的final屬性,表示頁面的標題。
  • 它有一個建構子,該建構子接受兩個參數:title(必填參數)和key(可選參數),並在建構實例時初始化這些屬性。建構子為下面這段程式碼:
const DrugHomePage({required this.title, Key? key}) : super(key: key);
  • 繼承StatefulWidget,並實現createState方法以建立與之關聯的 DrugHomePageState
class DrugHomePage extends StatefulWidget {
  final String title;

  // 使用`const`的方式來創建,以實現性能的優化
  // 將建構子聲明為 const,並且 title 參數使用 required 關鍵字表示為必填參數
  // 在建構子中添加一個名為 key 的參數。在 Flutter 中,key 參數通常用於識別 Widget 樹中的元素,這對於管理和更新 UI 元素很重要
  const DrugHomePage({required this.title, Key? key}) : super(key: key);

  @override
  // 繼承`StatefulWidget`,並實現`createState`方法以建立與之關聯的 `DrugHomePageState`
  DrugHomePageState createState() => DrugHomePageState();
}

(2)DrugHomePageState 類別:

  • 這是DrugHomePage 的狀態類別,用於管理DrugHomePage的狀態。

  • 它包含一個名為loadData的異步方法,用於從應用程式的資源中載入資料,這些資料包括DLI.jsonDA.json,格式如下:
    https://ithelp.ithome.com.tw/upload/images/20230908/20120073wRgJoZ1tjp.png
    https://ithelp.ithome.com.tw/upload/images/20230908/20120073Owdf7wZb9t.png
    https://ithelp.ithome.com.tw/upload/images/20230908/20120073pFjWCUzFPn.png

  • build方法用於構建小部件樹,它返回一個Scaffold小部件,其中包含應用程式的主要 UI。

  • Scaffold中,使用了FutureBuilder,該小部件等待loadData 方法完成,然後根據結果構建不同的 UI。

  • 如果載入成功,它使用ListView.builder顯示一個藥物列表,其中包含每個藥物的相關資訊和圖片;如果載入失敗,它顯示錯誤信息,如果仍在載入中,則顯示進度指示器。

  • 異步方法架構如下圖: Future, async
    https://ithelp.ithome.com.tw/upload/images/20230908/20120073Hsgt8GBgbx.png

class DrugHomePageState extends State<DrugHomePage> {
  // 載入資料(DLI.json and DA.json)
  Future<Map<String, dynamic>> loadData() async {
    // 使用 rootBundle 加載 DLI.json 和 DA.json 檔案的內容
    String jsonDLIString = await rootBundle.loadString('assets/data/DLI.json');
    String jsonDAString = await rootBundle.loadString('assets/data/DA.json');  // DA.json 裡面是網址,手機模擬器也要連上wi-fi才能連到網路抓圖片

    // 將 JSON 字串解碼為 Map 格式的資料
    Map<String, dynamic> data = {
      "DLI": jsonDecode(jsonDLIString),
      "DA": jsonDecode(jsonDAString),
    };

    return data;
  }

  @override
  Widget build(BuildContext context) {
    return Theme(
        data: ThemeData(
            primarySwatch: Colors.pink,  // 設置主題的主要顏色為粉紅色
            appBarTheme: const AppBarTheme(backgroundColor: Colors.pink)  // 設置 AppBar 的背景色為粉紅色
        ),
      child: Scaffold(
          backgroundColor: Colors.pink,  // 添加這一行
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.primary,
            foregroundColor: Theme.of(context).colorScheme.onPrimary,
            title: Text(widget.title),  // 顯示標題文字
          ),
          // 使用 FutureBuilder 來處理異步操作
          body: FutureBuilder<Map<String, dynamic>>(
            future: loadData(),  // 調用 loadData() 方法來獲取資料
            builder: (BuildContext context,
                AsyncSnapshot<Map<String, dynamic>> snapshot) {
              if (snapshot.hasData) {
                final data = snapshot.data;

                // 使用 ListView.builder 顯示資料列表
                return ListView.builder(
                  itemCount: data!["DLI"].length,
                  itemBuilder: (BuildContext context, int index) {
                    // 取得圖片位址
                    String imgSrc = "";
                    // 檢查是否存在對應的圖片網址
                    // 在DA.json這個檔案中,是用"中文品名"作為對應圖片網址的key
                    bool containsKey = data["DA"].containsKey(data['DLI'][index]['中文品名']);
                    if (containsKey == true) {
                      imgSrc = data['DA'][data['DLI'][index]['中文品名']];
                    } else {
                      imgSrc =
                      // 使用預設圖片
                      "https://blog.thomasnet.com/hubfs/shutterstock_774749455.jpg";
                    }

                    // 使用 GestureDetector 實現點擊效果
                    return GestureDetector(
                      // D6(明日講解內容)
                      // onTap: () {
                      //   // 導航到藥物詳細資訊介面,並將相關資料傳遞過去
                      //   Navigator.push(
                      //     context,
                      //     MaterialPageRoute(
                      //       builder: (context) => DrugInformationPage(
                      //         data: data['DLI'][index],
                      //         imgSrc: imgSrc,
                      //       ),
                      //     ),
                      //   );
                      // },
                      // 顯示在Drug這個介面的內容(即ListView.builder列表顯示的內容: 英文品名、適應症)
                      child: Card(  // 列表顯示的資料
                        child: Row(
                          children: [
                            Expanded(
                              child: ListTile(
                                title: Text(data['DLI'][index]['英文品名'],  // 顯示英文品名
                                            style: const TextStyle(fontWeight: FontWeight.bold
                                            )),
                                subtitle: Text(data['DLI'][index]['適應症']),
                              ),
                            ),
                            Image.network(
                              imgSrc,
                              width: 100,
                              height: 100,
                            )
                          ],
                        ),
                      ),
                    );
                  },
                );
              } else if (snapshot.hasError) {  // 處理錯誤情況
                return Text('Error: ${snapshot.error}');
              } else {  // 顯示載入中的進度條
                return const CircularProgressIndicator();
              }
            },
          )),
    );
  }
}

3.Dart語言學習:

(1) 建構子(constructors)

const DrugHomePage({required this.title, Key? key}) : super(key: key);
  • 這個建構子被聲明為 const,這表示這個小部件的屬性在創建後不會變化。
  • 建構子是DrugHomePage類別中的一個特殊方法
    用於初始化該類別的實例。這個建構子接受兩個參數:title和可選的key,並用來設定DrugHomePage物件的初始狀態,同時將key參數傳遞給父類別。建構子的目的是在創建DrugHomePage 實例時提供必要的資訊並確保初始設定正確。
  • 建構子使用const修飾詞
    創建 DrugHomePage的實例時,可以使用const的方式來創建,以實現性能的優化,特別是當小部件在Widget中可能多次重建時。
    ,而使用const建構子可以確保在編譯時期就確保對象的不可變性。
  • title 參數使用 required 關鍵字表示為必填參數
  • 在建構子中添加一個名為 key 的參數
    在建構子中添加一個名為key的參數。在Flutter中,key參數通常用於識別 Widget 樹中的元素,這對於管理和更新UI元素很重要。通常key的類型是Key或其子類,例如GlobalKey

(2) 異步方法(Asynchronous methods)

  • 定義: 是指在程式執行過程中,在等待某個操作完成時,允許程式繼續執行其他工作,當該操作完成後得到輸出,接續進行後續的工作。
  • 非阻塞性(Non-blocking): 異步方法允許程式在等待某個操作完成的同時繼續執行其他任務,透過此方式能提高程式的執行效率。
  • 異步操作的觸發和執行: 異步方法通常包括一個或多個異步操作(例如大型檔案操作、網路請求、資料庫查詢、計算密集型工作、定時任務、非同步IO操作、異步API呼叫等),這些操作是由特殊的關鍵字(如 awaitasync)觸發執行。
  • async 關鍵字: 在 Dart 和許多其他程式語言中,使用 async 關鍵字來定義異步方法。這表示該方法包含異步操作,可以使用 await 來等待這些操作完成。
  • await 關鍵字: 在異步方法內,使用 await 來等待異步操作完成。當遇到 await 時,程式會暫停執行當前的方法,直到異步操作完成為止。這樣可以確保後續的程式碼在操作完成後再執行。
  • Future對象: 異步方法通常返回一個 Future 對象,它代表異步操作的未來結果。程式可以使用 Future 對象來等待操作完成並取得結果或處理異常情況。

Part3: 實作結果

https://ithelp.ithome.com.tw/upload/images/20230908/20120073NEJypHuPnc.png

食能以時,身必無災
解釋: 飲食能夠有節制,身體必然不會有疾病
Eating in moderation at the right times ensures a healthy body.

整個晚上脹氣又拉肚子,以後一定要多照顧自己的腸胃,痛.../images/emoticon/emoticon02.gif


上一篇
D4-Medicine介面,讓疾病資訊一目瞭然_part2
下一篇
D6-Drug介面,揭開藥物的神秘面紗_part2
系列文
攜手神隊友ChatGPT:攝護腺自我照護App開發歷程!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言