備忘錄_20160105(定位) 修改 回首頁

程式 2021-03-10 15:01:59 1615359719 100
念佛記數器 counter - 使用 flutter 製作(dart語言)

念佛記數器 counter - 使用 flutter 製作(dart語言)
APK檔案 flutter_counter.apk
專案資料 flutter_counter.zip

螢幕固定在 portrait ,不旋轉。分平台設定最安全。

for android
編輯 android/app/src/main/AndroidManifest.xml

主要是加入 screenOrientation 參數

<application ...>
 <activity
    android:name=".MainActivity"
    android:screenOrientation="portrait" ...>

for ios
編輯 ios/Runner/Info.plist

主要是加入 UISupportedInterfaceOrientations 參數

<key>UISupportedInterfaceOrientations</key>
<array>
 <string>UIInterfaceOrientationPortrait</string>
</array>

pubspec.yaml
name: flutter_counter
description: A new Flutter application.

# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.0
  intl: ^0.16.1
  shared_preferences: 2.0.3
  confirm_dialog: ^1.0.0
  prompt_dialog: ^1.0.0
  alert_dialog: ^1.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter

# For information on the generic Dart part of this file, see themcmd
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
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:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

main.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:confirm_dialog/confirm_dialog.dart';
import 'package:prompt_dialog/prompt_dialog.dart';
import 'package:flutter/src/material/icons.dart';
import 'package:alert_dialog/alert_dialog.dart';

void main() {
  runApp(MyApp());
}

String strAppCaption="記數器 ver 0.30";
String strTargetKey="";

Widget myAppBar=AppBar(
  backgroundColor: Color.fromARGB(255, 0, 0, 0),
  title: Text(strAppCaption));

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        '/pageMain': (context) => pageMain(),
        '/pageDailyCounter': (context) => pageDailyCounter(),
        '/pageTargetCounterList': (context) => pageTargetCounterList(),
        '/pageDailyHistory': (context) => pageDailyHistory(),
        '/pageTargetCounter': (context) => pageTargetCounter(),
      },
      home: pageMain(),
    );
  }
}

class pageMain extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
        appBar: myAppBar,
        body: Column(
          children: <Widget>[
            Expanded(
              flex: 5,
              child: Container(
                width: double.infinity,
                color: Color.fromARGB(255, 60, 60, 60),
                child: GestureDetector(
                    behavior: HitTestBehavior.translucent,
                    onTap: () {
                      Navigator.pushNamed(context, "/pageDailyCounter");
                    },
                    child: Container(
                      width: double.infinity,
                      height: double.infinity,
                      child: Container(
                          alignment: Alignment(0.0, 0.0),
                          child: Text("日課", style: TextStyle(color: Colors.white, fontSize: 48))
                      ),
                    )
                ),
              ),
            ),
            Expanded(
              flex: 5,
              child: Container(
                width: double.infinity,
                color: Color.fromARGB(255, 30, 30, 30),
                child: GestureDetector(
                    behavior: HitTestBehavior.translucent,
                    onTap: () {
                      Navigator.pushNamed(context, "/pageTargetCounterList");
                    },
                    child: Container(
                      width: double.infinity,
                      height: double.infinity,
                      child: Container(
                          alignment: Alignment(0.0, 0.0),
                          child: Text("標的", style: TextStyle(color: Colors.white, fontSize: 48))
                      ),
                    )
                ),
              ),
            ),
          ],
        )
    );
  }
}

class pageDailyCounter extends StatefulWidget {
  @override
  pageDailyCounterState createState() => new pageDailyCounterState();
}

class pageDailyCounterState extends State<pageDailyCounter> {

  bool booCanCount=false;
  String strKey="";
  int iDailyCounter=0;

  String getStrCurrentDate()
  {
    //var now=(new DateTime.now()).add(Duration(hours:8));
    var now=new DateTime.now();
    var formatter=new DateFormat('yyyy-MM-dd'); // yyyy-MM-dd HH:mm:ss
    return formatter.format(now);
  }

  String getStrKey()
  {
    return "day-"+getStrCurrentDate();
  }

  Future getCounts() async {
    booCanCount=false;
    SharedPreferences oSP=await SharedPreferences.getInstance();
    strKey=getStrKey();
    int iVal=await oSP.getInt(strKey);
    if(oSP==null || iVal==null) { iVal=0; }
    booCanCount=true;
    setState(() { iDailyCounter=iVal; });
  }

  Future setCounts() async {

    if(booCanCount==false) { return; }

    booCanCount=false;
    SharedPreferences oSP=await SharedPreferences.getInstance();
    iDailyCounter++;
    await oSP.setInt(strKey, iDailyCounter);

    strKey=getStrKey();
    int iVal=await oSP.getInt(strKey);
    if(oSP==null || iVal==null) { iVal=0; }

    setState(() { iDailyCounter=iVal; });

    booCanCount=true;
  }

  Future clearAll() async {
    booCanCount=false;

    SharedPreferences oSP=await SharedPreferences.getInstance();
    Set<String> ssKeys=await oSP.getKeys();

    for(var i=0; i<ssKeys.length; i++) {
      if(ssKeys.elementAt(i).startsWith("day-")==true) {
        await oSP.remove(ssKeys.elementAt(i));
      }
    }

    booCanCount=true;

    setState(() {
      iDailyCounter=0;
    });
  }

  @override
  Widget build(BuildContext context) {

    if(strKey=="") { getCounts(); }

    return Scaffold(
      appBar: AppBar(
          backgroundColor: Color.fromARGB(255, 0, 0, 0),
          title: Text(strAppCaption),
          actions: [
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, "/pageDailyHistory");
              },
              child: Text("歷史"),
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all<Color>(Color.fromARGB(255, 160, 160, 160)),
                  foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
            ),
            ElevatedButton(
              onLongPress: () async {
                if(await confirm(
                    context,
                    title: Text("歸零詢問"),
                    content: Text("日課全部歸零,請問您是否確定?"),
                    textOK: Text("確定清空"),
                    textCancel: Text("先取消好了"))) {
                  clearAll();
                }
              },
              child: Text("長按歸零"),
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all<Color>(Color.fromARGB(255, 80, 80, 80)),
                  foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
            ),
          ],),
      body: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () { setCounts(); },
        child: Container(
          width: double.infinity,
          height: double.infinity,
          color: Color.fromARGB(255, 0, 0, 0),
          child: Stack(
            children: <Widget>[
              Container(
                alignment: Alignment(0.0, -0.8),
                child: Text(
                    "日課 "+strKey.substring(4),
                    style: TextStyle(color: Colors.white)
                ),
              ),
              Container(
                alignment: Alignment(0.0, 0.0),
                child: Text(
                    iDailyCounter.toString(),
                    style: TextStyle(color: Colors.white, fontSize: 66)
                ),
              ),
            ],
          ),
        ),
      )
    );
  }
}

class pageDailyHistory extends StatefulWidget {
  @override
  pageDailyHistoryState createState() => new pageDailyHistoryState();
}

class pageDailyHistoryState extends State<pageDailyHistory> {

  bool booRead=false;
  int iTotal=0;
  var straItem=[];

  Future getHistory() async {

    if(booRead==true) { return; }

    straItem=[];
    booRead=true;
    SharedPreferences oSP=await SharedPreferences.getInstance();
    Set<String> ssKeys=await oSP.getKeys();
    iTotal=0;

    for(var i=0; i<ssKeys.length; i++) {
      if(ssKeys.elementAt(i).startsWith("day-")==true) {
        int iVal=await oSP.getInt(ssKeys.elementAt(i)) ?? 0;
        iTotal=iTotal+iVal;
        straItem.add(ssKeys.elementAt(i).substring(4)+"  "+iVal.toString());
      }
    }
    if(straItem.length>0) {
      straItem.sort((a, b) => b.compareTo(a));
    }

    straItem.insert(0, "------------------------------------");
    straItem.insert(0, "數目加總  "+iTotal.toString());
    setState(() { iTotal=iTotal+0; });
  }

  @override
  Widget build(BuildContext context) {
    getHistory();
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: myAppBar,
      body: ListView.builder(
          itemCount: straItem.length,
          itemBuilder: (BuildContext oContext, int iIndex) {
            return Text(straItem[iIndex], style: TextStyle(fontSize: 24, color: Colors.white));
          }
      ),
    );
  }

}

class pageTargetCounterList extends StatefulWidget {
  @override
  pageTargetCounterListState createState() => new pageTargetCounterListState();
}

class pageTargetCounterListState extends State<pageTargetCounterList> {

  bool booRead=false;
  int iTotal=0;
  var straItem=[];

  Future getTargets() async {

    if(booRead==true) { return; }

    straItem=[];
    booRead=true;
    SharedPreferences oSP=await SharedPreferences.getInstance();
    Set<String> ssKeys=await oSP.getKeys();
    iTotal=0;

    for(var i=0; i<ssKeys.length; i++) {
      if(ssKeys.elementAt(i).startsWith("tar-")==true) {
        int iVal=await oSP.getInt(ssKeys.elementAt(i)) ?? 0;
        iTotal=iTotal+iVal;
        straItem.add(ssKeys.elementAt(i).substring(4)+" - "+iVal.toString());
      }
    }
    if(straItem.length>0) { straItem.sort((a, b) => a.compareTo(b)); }
    //for(var i=0; i<40; i++) { straItem.add("----ajsdhjkasdjksldafhjksadhfjksadfhjsadfjksadfhjkhsdafjlkdsakjf"+i.toString()); }

    setState(() { iTotal=iTotal+0; });
  }

  Future addTarget(BuildContext context, String strTitle) async {

    SharedPreferences oSP=await SharedPreferences.getInstance();
    Set<String> ssKeys=await oSP.getKeys();
    String strKey="tar-"+strTitle;

    if(ssKeys.contains(strKey)==true) {
      await alert(
        context,
        title: Text("標的重複"),
        content: Text("您輸入的標的名稱已經重複,新增失敗!"),
        textOK: Text("確定"),
      );
      return;
    }

    await oSP.setInt(strKey, 0);

    booRead=false;
    getTargets();
  }

  String getStrTheKey(String str1) {
    int i=str1.indexOf(" ");
    if(i==-1) {
      return "tar-"+str1;
    }
    else {
      return "tar-"+str1.substring(0, i);
    }
  }

  Future resetTarget(BuildContext context, String strKeyToBeReset) async {

    if((await confirm(
        context,
        title: Text("標的歸零"),
        content: Text("請問您是否確定要將機碼 = "+strKeyToBeReset+" 的資料歸零?"),
        textOK: Text("確定"),
        textCancel: Text("取消")))==false) {
      return;
    }

    SharedPreferences oSP=await SharedPreferences.getInstance();
    await oSP.setInt(strKeyToBeReset, 0);

    booRead=false;
    getTargets();
  }

  Future renameTarget(BuildContext context, String strKeyToBeRenamed) async {

    // 先讀取新的標的名稱
    String strNewTitle=(await prompt(
      context,
      title: Text("請輸入新的標的名稱,舊標的名稱為:"+strKeyToBeRenamed.substring((4))),
      textOK: Text("確定"),
      textCancel: Text("取消"),
    ));
    if(strNewTitle==null) { return; }
    strNewTitle=strNewTitle.replaceAll(" ", "");
    if(strNewTitle.length==0) { return; }

    // 讀取既有的標的名稱
    SharedPreferences oSP=await SharedPreferences.getInstance();
    Set<String> ssKeys=await oSP.getKeys();
    String strNewKey="tar-"+strNewTitle;

    // 看看有無重複
    if(ssKeys.contains(strNewKey)==true) {
      await alert(
          context,
          title: Text("標的重複"),
          content: Text("抱歉,您輸入的標的名稱已經重複,更名失敗!"),
          textOK: Text("確定"));
      return;
    }

    int iOldValue=(await oSP.getInt(strKeyToBeRenamed)) ?? 0;

    await oSP.setInt(strNewKey, iOldValue+5);
    await oSP.remove(strKeyToBeRenamed);

    booRead=false;
    getTargets();
  }

  Future delTarget(BuildContext context, String strKeyToBeDeleted) async {

    if((await confirm(
      context,
      title: Text("標的刪除"),
      content: Text("請問您是否確定要刪除機碼 = "+strKeyToBeDeleted+" 的資料?"),
      textOK: Text("確定"),
      textCancel: Text("取消")))==false) {
      return;
    }

    SharedPreferences oSP=await SharedPreferences.getInstance();
    await oSP.remove(strKeyToBeDeleted);

    booRead=false;
    getTargets();
  }

  void goInside(BuildContext oContext, String strItem) {
    strTargetKey=getStrTheKey(strItem);
    Navigator.pushNamed(oContext, "/pageTargetCounter").then((value) {
      booRead=false;
      getTargets();
    });

  }

  @override
  Widget build(BuildContext context) {

    print("build");

    getTargets();

    return Scaffold(
      backgroundColor: Colors.black,
      appBar:  AppBar(
        backgroundColor: Color.fromARGB(255, 0, 0, 0),
        title: Text(strAppCaption),
        actions: [
          ElevatedButton(
            onPressed: () async {
              String strTitle=(await prompt(
                context,
                title: Text("請輸入標的名稱"),
                textOK: Text("確定"),
                textCancel: Text("取消"),
              ));
              if(strTitle!=null) {
                strTitle=strTitle.replaceAll(" ", "");
                if(strTitle.length>0) {
                  await addTarget(context, strTitle);
                }
              }
            },
            child: Text("新增標的"),
            style: ButtonStyle(
                backgroundColor: MaterialStateProperty.all<Color>(Color.fromARGB(255, 80, 80, 80)),
                foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
          ),
        ],
      ),
      body: Column(
        children: <Widget>[
          Text("總數:"+iTotal.toString(), style: TextStyle(color: Colors.white, fontSize: 24),),
          Expanded(
              child: ListView.builder(
                  itemCount: straItem.length,
                  itemBuilder: (BuildContext oContext, int iIndex) {
                    return GestureDetector(
                      onTap: () { goInside(oContext, straItem[iIndex]); },
                      child: Padding(
                        padding: EdgeInsets.only(top: 30),
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            ListTile(
                              leading: Icon(Icons.favorite, color: Colors.deepOrangeAccent, size:24.0),
                              title: Text(straItem[iIndex], style: TextStyle(fontSize: 24.0, color: Colors.white)),
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.end,
                              children: [
                                ElevatedButton(
                                  onPressed: () { goInside(oContext, straItem[iIndex]); },
                                  onLongPress: () {
                                    String strTheKey=getStrTheKey(straItem[iIndex]);
                                    renameTarget(context, strTheKey);
                                  },
                                  child: Text("長按更名", style: TextStyle(color: Colors.white)),
                                  style: ButtonStyle(
                                      backgroundColor: MaterialStateProperty.all<Color>(Color.fromARGB(255, 80, 80, 80)),
                                      foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
                                ),
                                Text(" "),
                                ElevatedButton(
                                  onPressed: () { goInside(oContext, straItem[iIndex]); },
                                  onLongPress: () {
                                    String strTheKey=getStrTheKey(straItem[iIndex]);
                                    resetTarget(context, strTheKey);
                                  },
                                  child: Text("長按歸零", style: TextStyle(color: Colors.white)),
                                  style: ButtonStyle(
                                      backgroundColor: MaterialStateProperty.all<Color>(Color.fromARGB(255, 80, 80, 80)),
                                      foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
                                ),
                                Text(" "),
                                ElevatedButton(
                                  onPressed: () { goInside(oContext, straItem[iIndex]); },
                                  onLongPress: () {
                                    String strKeyToBeDeleted=getStrTheKey(straItem[iIndex]);
                                    delTarget(context, strKeyToBeDeleted);
                                  },
                                  child: Text("長按刪除", style: TextStyle(color: Colors.white)),
                                  style: ButtonStyle(
                                      backgroundColor: MaterialStateProperty.all<Color>(Color.fromARGB(255, 80, 80, 80)),
                                      foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  }
              ),
          ),
        ],
      ),
    );
  }
}

class pageTargetCounter extends StatefulWidget {
  @override
  pageTargetCounterState createState() => new pageTargetCounterState();
}

class pageTargetCounterState extends State<pageTargetCounter> {

  bool booFirst=true;
  bool booCanCount=false;
  int iTargetCounter=0;

  Future getCounts() async {
    booCanCount=false;
    SharedPreferences oSP=await SharedPreferences.getInstance();
    int iVal=await oSP.getInt(strTargetKey);
    if(oSP==null || iVal==null) { iVal=0; }
    booCanCount=true;
    setState(() { iTargetCounter=iVal; });
  }

  Future setCounts() async {

    if(booCanCount==false) { return; }

    booCanCount=false;
    SharedPreferences oSP=await SharedPreferences.getInstance();
    iTargetCounter++;
    await oSP.setInt(strTargetKey, iTargetCounter);

    setState(() { iTargetCounter=iTargetCounter+0; });

    booCanCount=true;
  }

  @override
  Widget build(BuildContext context) {

    if(booFirst==true) { booFirst=false; getCounts(); }

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Color.fromARGB(255, 0, 0, 0),
        title: Text(strAppCaption),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () { setCounts(); },
        child: Container(
          width: double.infinity,
          height: double.infinity,
          color: Color.fromARGB(255, 0, 0, 0),
          child: Stack(
            children: <Widget>[
              Container(
                alignment: Alignment(0.0, -0.8),
                child: Text(
                    "標的 "+strTargetKey.substring(4),
                    style: TextStyle(color: Colors.white)
                ),
              ),
              Container(
                alignment: Alignment(0.0, 0.0),
                child: Text(
                    iTargetCounter.toString(),
                    style: TextStyle(color: Colors.white, fontSize: 66)
                ),
              ),
            ],
          ),
        ),
      )
    );
  }
}