Help us understand the problem. What is going on with this article?

Flutterと云ふものを触った話を語らん

はじめに

江坂プロクラでは「狂人」と名高いハロウィンでマグロの仮装してた1年次の人間です。Slack名は「神」です。以前書いてた記事
13日目の記事書きました。今日は13日の金曜日です。私が厚切りジェイソンだ
今回は10月あたりからFlutter触ってアプリ開発の練習がてらリマインダー作ってた話をします。
12月15日にGDG神戸で発表するので発表する点の整理も兼ねて記事書いていきたいと思います。
見て欲しいところとか自慢したいところはコード貼ってくつもり

Flutterを選んだ理由

・Flutterであれば動いている言語が違うiOSとAndroidの両方で動かせるから別々で書く必要がない
・シンプルでいい感じのデザインが元から用意されている
・興味が湧いた

環境

APIはFlutterで、言語はDartで、エディタはIntelliJです。開発環境はiOSです。それと作業用にYoutube見てました。

できたもんの紹介

まだまだ途中ですが軽く紹介します。

全体の感じ

スクリーンショット 2019-12-03 14.52.17.png
スクリーンショット 2019-12-03 14.59.07.png
スクリーンショット 2019-12-06 10.13.17.png

UIはこんな感じです。特に詳しい指定しなくてもいい感じの出してくれるのがFlutterのいいところ。まああとあと変わるかもしれないけど
実装してないところもあるけど実装してるところはそれぞれ紹介します。

予定の追加画面

ぺ.png

①予定の名前入力欄
ここに予定の名前を入力します。

②予定の備考入力欄
ここに予定の備考を入力します。

③時間指定ボタン
ここで時間を指定すると、その指定した時間に通知を送ってくれます。

④繰り返し通知ボタン
これを「なし」以外のどれかにしておくと、例えば「1日後」であれば最初に指定された時間の1日後にもう一度、といったように決まった周期で繰り返して通知してくれます。

child:Column(
                  children: <Widget>[
                    Text(
                      '繰り返し通知',
                      style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold),
                    ),
                    RadioListTile(
                      activeColor: Colors.green,
                      title: Text(
                        'なし',
                        style: TextStyle(fontSize: 15,fontWeight: FontWeight.bold),
                      ),
                      value: "none",
                      groupValue: _type,
                      onChanged: repeatNotice,
                    ),
                    RadioListTile(
                      activeColor: Colors.green,
                      title: Text(
                        '1日後',
                        style: TextStyle(fontSize: 15,fontWeight: FontWeight.bold),
                      ),
                      value: "oneDayLater",
                      groupValue: _type,
                      onChanged: repeatNotice,
                    ),
                    RadioListTile(
                      activeColor: Colors.green,
                      title: Text(
                        '1週間後',
                        style: TextStyle(fontSize: 15,fontWeight: FontWeight.bold),
                      ),
                      value: "oneWeekLater",
                      groupValue: _type,
                      onChanged: repeatNotice,
                    ),
                    RadioListTile(
                      activeColor: Colors.green,
                      title: Text(
                        '1ヶ月後',
                        style: TextStyle(fontSize: 15,fontWeight: FontWeight.bold),
                      ),
                      value: "oneMonthLater",
                      groupValue: _type,
                      onChanged: repeatNotice,
                    ),
                  ],
                ),
void repeatNotice(String e)=>setState(() {
    _type = e;//選択されたボタンのvalueの値を設定する
  });

⑤ブックマークボタン
詳しくは後述しますが、ブックマークされた状態で予定を追加します。

⑥追加ボタン
ここを押すと、予定を追加できます。追加するときに、入力された情報をローカルストレージに保存したのち、まとめて一個のデータにしてリストに追加します。

floatingActionButton:FloatingActionButton(
        onPressed: () {
          var random = new math.Random(); //ここから
          var number = random.nextInt(1000000000).toString();
          var supplement = new math.Random();
          if (number.length < 9) {
            for (int i = 0; i <= 9 - number.length; i++) {
              number = number + supplement.nextInt(10).toString();
            }
          } //ここまでの処理についてはリストから削除する要素を特定するためのキーがかぶるとエラーが出るために9桁のIDを作ってそれを回避するための処理。
          // 一番最初に作られた乱数が9桁以下だと自動的に9桁にされる
          _addItem(myController.text, formatter.format(_myDateTime), number, _description.text);
          myController.clear();
          _description.clear();
        },//ここで予定のデータを追加するクラスを呼び出す
        backgroundColor:Colors.black54,
        child: Icon(Icons.add,color:Colors.white,),
      ),
void _addItem(String planName, String time, String number, String description) {
    if (planName != "") {
      setState(() {
        if (description=="") {
          description = "なし";
        }
        items.add({"number": number, "planName": planName, "time": time, "description": description,"bookMark":_onBookMark});//ここでデータをまとめてリストに追加してます。
        savePlanData(planName, time, number, description,_onBookMark,);//ここでローカルストレージにデータ保存してます。
        planTimer(time,planName,number,_type);//ここで繰り返し通知に指定があった時の処理のクラスを呼び出してます。
        _type="none";
        _bookMarkSwitch=false;
        _scaffoldKey.currentState.showSnackBar(
            SnackBar(
              content: Text('予定を追加しました',
              style: TextStyle(color: Colors.white, fontSize: 15,fontWeight: FontWeight.bold)),
              backgroundColor: Colors.black54,
            )
        );
        planNameFocusNode.unfocus();
        descriptionFocusNode.unfocus();
      });
    }
    else {
      _scaffoldKey.currentState.showSnackBar(
          SnackBar(content: Text('エラー:予定名が空白です',
              style: TextStyle(color: Colors.red, fontSize: 15,fontWeight: FontWeight.bold)),
              backgroundColor: Colors.black54
          )
      );
      planNameFocusNode.unfocus();
      descriptionFocusNode.unfocus();
    }
  }
planTimer(String time, String planName,String id,String repeater) {//通知のコード
    var nowTime;
    timer = Timer.periodic(const Duration(seconds: 1), (timer) {//タイマー使って一秒ごとで実行
      nowTime = new DateTime.now();
      for(int i=0;i<items.length;i++){
        if(items[i]["number"]==id) {//予定一覧の中に同じID、つまりその予定があれば実行、なかったらタイマーを止める
          var item = items[i];
          if (time == formatter.format(nowTime)) {
            if(repeater=="none"){
              item["planName"]=item["planName"]+"(実行済み)";
              CommonClass().save();//予定を変更後、データをセーブ
            }
            _onNotification(planName,id,repeater); //通知を送るクラスへ
            timer.cancel();
          }
        }else{
          timer.cancel();
        }
      }
    });
  }
Future _onNotification(String planName,String id,String repeater) async {
    var _repeat;
    await flutterLocalNotificationsPlugin.schedule(
        0, '予定の時間です。', '$planName', DateTime.now().add(Duration(seconds: 1)),
        platformChannelSpecifics, payload: 'item id 2'
    );//通知を送る
    switch(repeater){//もし繰り返し通知が何かしら指定されていたら実行
      case "oneDayLater":
        _repeat=DateTime.now().add(Duration(days:1));//現在の時間の1日後を指定してもう一度上記の通知のクラスへ。あとも一緒
        planTimer(formatter.format(_repeat),planName,id,repeater);
        break;

      case "oneWeekLater":
        _repeat=DateTime.now().add(Duration(days:7));
        planTimer(formatter.format(_repeat),planName,id,repeater);
        break;

      case "oneMonthLater":
        _repeat=DateTime.now().add(Duration(days:30));
        planTimer(formatter.format(_repeat),planName,id,repeater);
        break;

      default:
        break;
    }
  }

追加する瞬間に予定ごとにIDを割り振ってます。これは予定の確認画面で予定を削除する時その予定を特定するためのキーがかぶるとエラーが出るので予定の名前や時刻なんかをキーとするとそのエラーが起きかねないので9桁のIDを割り振ることでそのIDをキーにすることで回避してます。THE★力技
ローカルストレージ保存は便利だけど結構型が決まっててちょうど良さそうなのがStringList型のやつしかなくてDynamic型が保存できねぇからわざわざデータ分解して保存してる
同じタイミングでデータを分解してそれぞれのリストに保存してるしロードする時も同じタイミングでリストから取ってきて合体させるから今現在データのズレは起きてないけど起きた時が怖すぎるしもっといいやり方あったら教えてください

予定の確認画面

スクリーンショット 2019-12-04 11.00.22.png

①予定
予定の追加画面で追加された予定がここに表示されます。
タップすることでブックマークすることができます。ブックマークされた予定は画像のように左端に緑色のマークが出てきます。予定の確認画面の⑤ブックマークボタンはオンにしておくとこのブックマークがされた状態で予定が追加される、というものです。また、スワイプすることで予定を削除できます。ブックマークされた予定は削除できません。

onTap:(){//ブックマークの判定。Cardに予定を表示して、タップされた時に発動
                      setState(() {
                        if(item["bookMark"]==""){
                          item["bookMark"]="bookMark";
                          bookMarkItems.add(items[index]);//ブックマークリストに追加
                          _scaffoldKey.currentState.showSnackBar(
                              SnackBar(content: Text('予定:${item["planName"]} をブックマークしました', style: TextStyle(color: Colors.white, fontSize: 15,fontWeight: FontWeight.bold,)), backgroundColor: Colors.black54)
                          );
                        }else{
                          item["bookMark"]="";
                          bookMarkItems.remove(items[index]);//ブックマークリストから削除
                          _scaffoldKey.currentState.showSnackBar(
                              SnackBar(content: Text('予定:${item["planName"]} のブックマークを外しました', style: TextStyle(color: Colors.white, fontSize: 15,fontWeight: FontWeight.bold,)), backgroundColor: Colors.black54)
                          );
                        }
                        CommonClass().save();//ローカルストレージに保存
                      });
                    },

②予定編集ボタン
押すとメニューが開き、メニューから予定の情報のそれぞれを編集できます。
スクリーンショット 2019-12-04 11.19.05.png
スクリーンショット 2019-12-04 11.19.16.png

③ブックマーク確認ボタン
ブックマークした予定のみ、ここで確認することができます。
スクリーンショット 2019-12-04 11.28.44.png
この画面では、ブックマークされているのは上の予定と下の予定です。すると…
スクリーンショット 2019-12-04 11.31.31.png
こうなります。ブックマークした予定のみを取得してきてここで表示してくれます。

ヘルプ

こっちは後回しにしてたので特に何もないです。アプリについての説明書いたくらい
スクリーンショット 2019-12-05 11.29.06.png

終わりに

習い事で通ってるプログラミングスクールのDiscordチャンネルで「Flutter存外に楽しい」って呟いたらなんやかんやあってGDG神戸で30分枠もらって発表することになりました。Discordで呟いただけなのに
元々はスマホアプリ開発→販売までの流れをある程度知るために練習がてら始めたFlutterだったけど本当に楽しいからみんなも是非触って見てね

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした