## flutter7000單計畫 flutter build web project: [https://123456dr.github.io/RCvocabulary/](https://123456dr.github.io/RCvocabulary/) :::danger 記得先創new branch to rc ::: :::danger everything 持續更新中 ::: :::info 目錄 [TOC] ::: ``` 已知有一個格式為= { 'absence': {'pos': 'n.', 'chinese': '缺席'}, 'absent': {'pos': 'adj./v.', 'chinese': '缺席'}, 'accept': {'pos': 'v.', 'chinese': '接受'},} 的map, 我想在介面新增一個按鈕, 當按下按鈕可以把map裡的資料案格式原封不動的複製, 並且新增一個輸入框有一個按鈕可以貼上複製的內容 並把貼上的東西新增至原本存在的map ``` ### 注意事項 & 說明 :::danger ! ! ! 如果是長期使用者, 建議使用同一次開啟的網頁頁面, =>開啟頁面後不得關閉,(關閉頁面可能造成新增的資料流失) 手機 : 把該頁面按分享鍵,選擇新增到手機主頁面 電腦版 : ![image](https://hackmd.io/_uploads/ryLW8FbPC.png =80%x) ! ! ! ::: :::success *系統會自動紀錄 最後一次瀏覽的頁面! 節省刷新後重選類別! *注意⚠️ 若遇到過長的文字,可以水平滑動查看喔! *單字發音 電腦版使用者[男聲] 手機板使用者[女聲] p.s 強烈建議在我研究出手機板男聲前使用電腦練習 (男生標準清楚&手機版單字庫尚未優化不能正常顯示) (預計學測後修正) *於填空模式多按幾次翻譯按鈕即可成功重載英文句子 --- 說明: 1.點選級別/類型 進入頁面, 右上方可切換模式(目前可使用:學習+測驗 ,'填空'施工中) * 於預設-測驗模式 : 輸入上方提示之英文單字, 按下確認(鍵盤或按鈕),若輸入正確將該單字從題庫移至已學會資料庫 * 於學習模式 : 看英文練中文,熟練後按 * 按learned 將該單字從題庫移至已學會資料庫 * 按pass 跳過該題(表示仍會出現於題目) 2.小按鈕: * 點案音量符號可撥放發音 * 可點選「ANS」查看該題英文單字 3.下方按鈕 * 「查看已學會單字」 : 點選查看作答正確列表, 可復原使該單字再次出現於題目中 * 「查看剩餘單字」, 將列出未作答單字, 可點選「刪除」排除該單字出現在題目的可能 4.點選「重置」, 將把已學會清單清空, 用於題目回答完畢時,一鍵重新學習, [注意此動作無法復原] 5.主頁面點選「input」進入頁面, * 點按'編輯單字'可自定義單字 *小細節 : 輸入框按鍵盤enter會自動跳下一個輸入框喔! * 可於編輯單字區輸入單字, 在輸入完英文後, 可選擇系統建議中譯, [注意單字英中為必填,詞性可選填] 按下ADD鍵送出,可於下方列表第一項查看 * 上方一鍵清除為清空所有新增單字 失誤將造成無可挽回的慘況orz ! ! ! 設計細節: 剛學會或剛復原的單字將顯示在資料庫頂端,便於回復上一步 *備註RCC專用倉庫暫不開放 ::: ### 更新log :::info 7/3 - 5h: (學測... * 更新填空頁面 * 新增生成句子按鈕,一個單字多句子(避免出現語意錯誤句子之無解&不特定生成文章XD) * 修正挖空後句子顯示(填入)答案 * 困難 * 途中遇到翻譯按鈕和句子生成紐連動 * 待新增 7/2 - 4h: (要學測了還做orz * 完成句子填空頁面 * 英文句子挖空 * 英文句子解答填空 * 英文句子中譯 * 英文句子朗讀 * 新增學習頁面自動發音選項 * 當切換到別的頁面=>失效並自動關閉選項 * 新增個人網頁連結於首頁https://123456dr.github.io/2024/05/28/RCvocabulary/ 6/13: * 改成按下類別才顯示清單 =>為了解決手機無法載入單字列表頁面(資料過多,當機) * 在自定義單字頁面新增刪除開關啟用鍵,雙重點擊(啟用+按刪除)減少用戶失誤永久刪除單字 6/12: * FocusScope.of(context).requestFocus(_focusNode1); 當點擊新增單字的'Add'後將自動回到單字輸入框 =>失敗 * 解決的一鍵復原/移出題庫的checkbox不顯示 =>因為對話框沒有及時改變狀態的效果, 我選擇在當用戶按下checkbox時自動關閉再開啟對話框, 達到重載效果 Navigator.of(context).pop(); _showClassSelectionDialog(context); * 修改[自定義單字] : 當用戶輸入的中文翻譯和自動翻譯選項不同時隱藏選項, 若用戶手動清空該輸入框則再次顯示中文翻譯建議 * 修正資料庫項目計數 : 原先為刪除/復原一個單字num-- 導致連按快於系統反應出現num=-1 連帶影響單字轉換儲存庫的動作(題庫與以學會資料庫間轉換) =>改為刪除/復原單字時setState(){num=該資料庫項目.length} 6/10: * 新增建立自定義單字分類! * 新增一鍵復原/學會類別! 可以方便選擇練習範圍(自定義單字的部分,input頁面) *小細節,在輸入自訂單字時,送出內容後分類輸入框 欄位不會清空,可以節省一次性輸入同類別時的時間! *在資料庫查看頁面,也按照類別分類,可以輕鬆查找 (帶新增搜尋單字系統) 6/8主要更新: 1.新增三個模式頁面[填空, 學習, 測驗] 主模式為測驗(意即進入分級預設模式) *填空頁面為英文句子待更新 https://www.wordnik.com/users/rcc/API 2.優化程式碼 將多部分重複片段整合 *待處理:獨立頁面程式碼 3.編輯單字頁面: 三個輸入框, 輸入完第一個按鍵盤確定會自動跳到下一個框! 最後一個則是自動送出! 4.花了5小時新增第二級單字+精簡程式碼+除錯! 5.新增了主頁面連結複製按扭, 原先設計的超連結限定電腦使用,所以製作複製扭讓手機用戶可以以瀏覽器貼上網址的方式前往察看我的其他資料! 6/2主要更新: 1.新增答題發音,資料庫查看發音 2.修正input自定義單字儲存問題 3.新增input輸入英單提供建議中譯 4.新增input一鍵清空自訂單字 5.加密個人分頁(rc儲藏室) 6.新增多項清除作業提示框(清除,重置) 7.改善輸入框視覺效果 5/31: 1.新增ANS三切功能[提示,顯示,隱藏] ::: :::spoiler 發音 待:randomwords,建議中文翻譯 :::spoiler async 和 await 异步函数是指可以在执行过程中暂停并等待异步操作完成后再继续执行的函数。在 Dart 中,使用 async 和 await 关键字可以定义和使用异步函数。 异步函数的特点包括: 可以通过 async 关键字标记函数为异步函数。 在异步函数中可以使用 await 关键字等待异步操作完成,而不会阻塞函数的执行。 异步函数可以返回一个 Future 对象,用于表示异步操作的结果或错误。 异步函数可以在执行过程中暂停并允许事件循环处理其他任务,提高程序的并发性和性能。 异步函数通常用于执行需要等待的 I/O 操作(如网络请求、文件读写等)或耗时的计算,以避免阻塞应用程序的主线程,保持应用程序的响应性。 ::: --- ### 過程 將大考中心的英文單字文本, 以[選取複製]的方式丟到c++程式碼分析成Json格式! 1.複製文本,為了讓網頁有單字中文的顯示,先透過c++程式碼將英文單字與詞性分離並輸出單字部分,接著複製純單字至線上翻譯網頁大量翻譯內容 2.得到英文 詞性 中文後再撰寫程式碼轉換成Json格式輸出 #### 檢討: 事後仔細考慮, 發現直接在dart檔案裡修改資料儲存模式, 先把資料文本存入陣列,在撰寫出轉換Json格式的程式碼修飾文本即可, 也可以乾脆案規律從文本陣列提取資料減去繁瑣流程! ```cpp= #include <iostream> using namespace std; int main() { string a; string b[5000][3]; int i=0; while(i<2006 ){ cin>>a; b[i][0]=a; cin>>b[i][1]; i++; } int I=i; i=0; while(cin>>a){ //cin>>a; b[i][2]=a; i++; } for(int j=0;j<I;j++){ cout<<b[j][0]<<" "<<b[j][1]<<" "<<b[j][2]<<endl; } //cout<<i; return 0; } ``` Json格式 ![image](https://hackmd.io/_uploads/BJOtshE4C.png =50%x) 翻譯網站:[https://www.onlinedoctranslator.com/app/translationprocess](https://www.onlinedoctranslator.com/app/translationprocess) (原本有考慮從頭到尾用API google translate, 但考慮到離學測的日子不遠, 先做出簡易實用版, 只有:使用者自定單字的建議中譯有引用到!) ### 遇到的問題 2. 發現單字計數不會隨按下按鈕及時改變 => 把length改成num++ 3. 發現單字經過調動(已學會、復原或新增等), 該單字會移置顯示畫面底端=>把LIST 從index值(頭到尾)顯示改為length-index-1(尾到頭) 4. 5/29新增葉面記錄功能,直接紀錄上次作答的類別頁面, 減少刷新夜面重選選單的困擾 5. 改善bottom overflow =>捲動+加長到螢幕底 ``` SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints( minHeight: MediaQuery.of(context).size.height, ), ``` 6. 更新:按鍵盤enter等同按鈕確認 7. 修改答案提示的底線數量根據單字長度變化 8. 6/8花了2小時編修正邊查找[ 單字庫顯示錯誤 ]的問題, 發現:在學習模式做完所有單字 (all移出題庫存入已學會單字庫), 在沒有剩餘單字的情況下按learned按鈕會送null到'已學會map',導致存入不合法的空資料 ==>應在每次按下learned按鈕後setstate檢查題庫是否為空, 若不為空則正常執行(該單字移入已學會db), 否則執行showrandomword進行函式在沒有題庫下的清空輸出動作 --- ### reference: * [flutter讀取pdf文字](https://pub.dev/documentation/pdf_text/latest/) * https://tw-hkt.blogspot.com/2019/11/2019-flutter-30-1117.html * https://ithelp.ithome.com.tw/users/20139951/ironman/3913 * https://www.onlinedoctranslator.com/app/translationprocess --- ### note :::spoiler - [dart語法](https://tw-hkt.blogspot.com/2019/11/flutter-30-day-1-flutter.html) - [material & scaffold](https://segmentfault.com/a/1190000018445793) ```off debug flag put that in materialapp(): debugShowCheckedModeBanner: false ``` ``` #string to int var x = int.parse('100'); --- #int to string String x = 100.toString(); --- #double to string &&取小數點後兩位 String x = 3.14159/toStringAsFixed(2); --- #print all content :plus 'r'infront of the text String s2 = r"aaa \n bb \n cc"; #string plus together String a,b; print(a+b); --- #?? 是否為空,賦值運算 var a=100; var b; print(a ??= 77);//輸出結果:100 print(b ??= 66);//輸出結果:66 --- #list var list1 = <String>[]; list1.add('100'); list1[0]="333" var list2 = [10,666,168]; print(list1.length); 副函式[ 宣告 List x]: void fun(List xx){} void fum([x]){} //可傳或不傳參數 (void ==無回傳值) /* void setData({int x=0, int y=0}){ print('x: $x , y: $y'); } main() { setData(x:100,y:66); setData(y:66,x:100); setData(y:66); } */ --- #for迴圈 for(int i=0;i < list.length;i++){ print('${list[i]}'); } for(var i in list1){ print('$i'); } --- #map [key:xx (key是唯一)] var w ={'mon':'賦值可重複', 'tue':'賦值可重複',} (the last one still need plus','behind value) var w = new Map(); w['mon']='xx'; --- #do while do{ //程式碼執行區塊 }while(條件式); --- ``` ``` #class 類別 /* class 類別名稱 { <類別欄位>,fields :類別屬性變數值 <存取、設定子>,getters/setters :透過 get 和 set 的方法來存取類別資料 <構造方法>,constructors:類別實體畫後初始化動作邏輯 <方法>,functions:類別中的方法 } */ void main(){ //類別名稱 物件名稱 = new 類別建構子(); Role role = new Role(); role.name ="喔"; role.walk(); } class Role { //屬性 String name=""; int age=20; //構造方法 Role(){ age=21; } //方法 void walk(){ print("walk"); } void run(){ print("run"); } } ``` ``` #繼承 class Animal { void eat() { print("Animal : eat"); } } class Dog extends Animal { void eat() { print("Dog : eat"); } void eatTest() { this.eat(); // this 呼叫自己的方法 super.eat(); // super 呼叫父類別的方法 } } void main() { Animal animal = new Animal(); animal.eat(); Dog dog = new Dog(); dog.eatTest(); } ``` ![image](https://hackmd.io/_uploads/r1PxA0TzR.png) #### text situation ``` Alignment.bottomCenter:置底中間 Alignment.bottomLeft:左下角 Alignment.center:正中間 Alignment.centerLeft:置左邊中間 Alignment.centerRight 置右中間 Alignment.topCenter:正上方中間 Alignment.topLeft:左上角 Alignment.topRight:右上角 ``` ##### row ``` /* [mainAxisAlignment:]+ MainAxisAlignment.start:靠左對其 MainAxisAlignment.end: 靠右對齊 MainAxisAlignment.center: 置中 MainAxisAlignment.spaceBetween: 中間留空間 MainAxisAlignment.spaceAround: 兩邊空間均分 MainAxisAlignment.spaceEvenly: 均分空間 */ /* [crossAxisAlignment:]+ CrossAxisAlignment.start: 縱向起點 CrossAxisAlignment.end: 縱向終點 CrossAxisAlignment.center: 縱向中心點 CrossAxisAlignment.stretch: 佔滿縱向 CrossAxisAlignment.baseline: 縱向對齊 baseline */ return Row( children: <Widget>[ Container( color:Colors.black, child:Text('A',style:TextStyle(frontSize: 100)), ), ], ); ``` #### center ``` Center(child: ...) ``` #### TextStyle ``` [style: TextStyle(xxx)] + fontSize: 30, //大小 color: Colors.red, //顏色 decoration: TextDecoration.underline, //底線 decoration: TextDecoration.lineThrough, //刪除線 fontWeight: FontWeight.bold, //粗體 ``` #### 一般按鈕 [detail](https://ithelp.ithome.com.tw/articles/10259474) ``` IconButton:圖示按鈕 TextButton:文字按鈕 OutlineButton:邊框按鈕 ElevatedButton:漂浮按鈕 style: ElevatedButton.styleFrom( elevation: 5, shadowColor: Color.fromARGB(255, 136, 154, 244), backgroundColor:Color.fromARGB(255, 135, 247, 237), ), ``` #### 漂浮按鈕 ``` //myapp的scaffold裡加 floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () { print('press...'); -- foregroundColor: Colors.amber, backgroundColor: Colors.red, //位置 floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat ``` #### images ``` Image.asset( 'assets/images/rcc.jpg', width: 200, height: 200, ), Image.network('https://i.imgur.com/ZX0PtRb.png') ``` #### 文字輸入框 ``` 定義: final TextEditingController Account = new TextEditingController(); 畫面輸入框: Container( child: TextField( controller: Account, decoration: InputDecoration(hintText: '帳號...'), ), ), --- [ final TextEditingController myController = new TextEditingController(); ] TextField( controller: myController, decoration: InputDecoration(hintText: '請輸入...') ,) //按下按鈕輸出 輸入值 在按鈕函式裡+[ print(myController.text); ] /* controller: 監聽文字輸入內容(★最重要★) onChanged:當文字有改變觸發事件 onSubmitted:當按下鍵盤Entenr觸發事件 enable:是否禁用 maxLength:最大長度 maxLines:最大行數 style:文字輸入框樣式 textAlign:文字對齊方式 obscureText:設定是否為隱碼,設定為true文字將會被隱碼,false則會顯示明文正常文字內容 inputFormatters:設定允許輸入的文字格式 decoration:文字輸入框元件裝飾效果 */ ``` #### 頁面跳轉 ``` 當按鈕被按下時(onPressed) //去 Navigator.push(content, MaterialPageRoute(builder: (context) => Bpage())); //回 Navigator.pop(context); --- //傳送輸入框資料 Container( child: TextField( controller: Account, decoration: InputDecoration(hintText: '帳號...'), ), ), //按下確認按鈕 onPressed: (){ Navigator.push(context, MaterialPageRoute(builder:(context) => BPage(Account:Account.text,PassWd:PassWd.text))); } --- //HomePage import 'BPage.dart'; //Bpage接收資料(定義接收的資料欄位) int intVal=0; String strVal=""; BPage({Key? key, this.intVal=0, this.strVal=""}) : super(key: key); ``` //class 傳遞(封包) ``` class Product { String name; String desc; int price; int stock; Product(this.name, this.desc, this.price,this.stock); } - Product product = new Product('產品名稱xxx', '產品內容xxx', 100, 66); - ``` #### 顯示限時訊息(下方) ``` //onPress onPressed: () { //顯示 SnackBar 簡單基本訊息 showMySnackBar(context); - void showMySnackBar(BuildContext context) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: const Text('顯示訊息'), duration: const Duration(seconds: 5), action: SnackBarAction( label: '復原', onPressed: () { print('復原...'); }, ), )); } } ``` #### 確認 取消對話框 ``` onPressed: () async { final ConfirmAction? action = await confirmDialog(context); print("你選擇:$action"); }, -- enum ConfirmAction { ACCEPT, CANCEL } Future<ConfirmAction?> confirmDialog(BuildContext context) async { return showDialog<ConfirmAction>( context: context, barrierDismissible: false, //控制點擊對話框以外的區域是否隱藏對話框 builder: (BuildContext context) { return AlertDialog( title: Text('確認取消對話視窗'), content: const Text('內容訊息'), actions: <Widget>[ ElevatedButton( child: const Text('確認'), onPressed: () { Navigator.of(context).pop(ConfirmAction.ACCEPT); }, ), ElevatedButton( child: const Text('取消'), onPressed: () { Navigator.of(context).pop(ConfirmAction.CANCEL); }, ) ], ); }, ); } ``` #### TapBar上方導航元件 ``` //在myApp final List<Tab> myTabs = <Tab>[ Tab(text: '首頁'), Tab(text: '聊天室'), Tab(text: '個人資料'), ]; final pages = [HomePage(), ChatPage(), AccountPage()]; -- //home home: DefaultTabController( length: myTabs.length, -- //scaffold,appBar的title下 bottom: TabBar( tabs: myTabs, -- //body body: TabBarView( children: <Widget>[HomePage(), ChatPage(), AccountPage()], ), ``` #### data ``` [document](https://tw-hkt.blogspot.com/2019/11/flutter-30-day-27sharedpreferences.html) ``` :::