decochのブログ

フリーランスのiOSエンジニア decoch のブログです

Flutterで書くiOSアプリハンズオン(QiitaClientを作ってみよう)

Flutterを使ったハンズオン用に記事を作成しました

使用するAPI

今回はQiitaAPiを題材とします。(ドキュメント)

登録不要で使えて、例えば、 https://qiita.com//api/v2/items で、投稿を取得できます。

[{
    rendered_body: " <h1> <span id="
    プロローグ " class="
    fragment "></span><a href="# % E3 % 83 % 97 % E3 % 83 % AD % E3 % 83 % AD % E3 % 83 % BC % E3 % 82 % B0 "><i class="
    fa fa - link "></i></a>プロローグ</h1> <p><a href="
    https: //camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" alt="20171109214610.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png"></a></p> <p>参照: <a href="http://r-dimension.xsrv.jp/classes_j/sine_cosine/" class="autolink" rel="nofollow noopener" target="_blank">http://r-dimension.xsrv.jp/classes_j/sine_cosine/</a></p> <p><strong>うげ〜。いやだこの図。。</strong>はい、こんにちは。。。<br> こちらの図見るとオエ〜ってなりますよね。僕はなります。<br> なのでお口直しに、小松菜奈さんの画像貼っときますわ。</p> <p><a href="https://camo.qiitausercontent.com/1ce1b5e53bb6405ccc06ed7fe0ce189cad70e816/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64383133376461362d656337362d633435322d663637392d6266306130646566343164642e6a706567" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/1ce1b5e53bb6405ccc06ed7fe0ce189cad70e816/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64383133376461362d656337362d633435322d663637392d6266306130646566343164642e6a706567" width="200" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/d8137da6-ec76-c452-f679-bf0a0def41dd.jpeg"></a></p> <p>最近ネバヤン(バンド)に胸熱で。。お別れの歌のMVやばい。<br> <a href="https://www.youtube.com/watch?v=ZFI-Hqeu_Ag" class="autolink" rel="nofollow noopener" target="_blank">https://www.youtube.com/watch?v=ZFI-Hqeu_Ag</a></p> <h1> <span id="本題" class="fragment"></span><a href="#%E6%9C%AC%E9%A1%8C"><i class="fa fa-link"></i></a>本題</h1> <p><strong>円運動とかゆらゆら上下運動とかってsin()とかcos()が必要じゃないですか?<br> メディアアート系のプログラミングって。oFとかprocessingとか、Unityも?。</strong></p> <p>なのでなるべく優し〜く入門勉強してきたいと思います。</p> <p>参考サイト<br> <a href="http://yoppa.org/ma2_10/1739.html" class="autolink" rel="nofollow noopener" target="_blank">http://yoppa.org/ma2_10/1739.html</a><br> <a href="http://r-dimension.xsrv.jp/classes_j/sine_cosine/" class="autolink" rel="nofollow noopener" target="_blank">http://r-dimension.xsrv.jp/classes_j/sine_cosine/</a></p> <h1> <span id="入門します" class="fragment"></span><a href="#%E5%85%A5%E9%96%80%E3%81%97%E3%81%BE%E3%81%99"><i class="fa fa-link"></i></a>入門します!</h1> <blockquote> <p>さて、プログラムによって図形を描く場合に避けて通れないのが三角関数(サイン、コサイン)です。<br> 円などに代表される曲線を描く際によく使われます。<br> タンジェントは使用頻度が低いので、ここでは省略します。</p> </blockquote> <p>だそうです。。。</p> <p>まず先ほどの図。<br> 大丈夫だから、ちょっとみてみてください。</p> <p><a href="https://camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" alt="20171109214610.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png"></a></p> <p>これを勇気を出して、単純に読み解くと、</p> <h2> <span id="r--円の半径" class="fragment"></span><a href="#r--%E5%86%86%E3%81%AE%E5%8D%8A%E5%BE%84"><i class="fa fa-link"></i></a>R → 円の半径</h2> <h2> <span id="x--y-円上の座標" class="fragment"></span><a href="#x--y-%E5%86%86%E4%B8%8A%E3%81%AE%E5%BA%A7%E6%A8%99"><i class="fa fa-link"></i></a>(x , y) →円上の座標</h2> <h2> <span id="θ--角度" class="fragment"></span><a href="#%CE%B8--%E8%A7%92%E5%BA%A6"><i class="fa fa-link"></i></a>θ → 角度</h2> <p>らしいです。</p> <p><a href="https://camo.qiitausercontent.com/67043a3190bfd14f3ed76443dc06bafe7c52a117/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31343937306263642d303762632d656361322d306138632d3730363130393662323235372e6a706567" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/67043a3190bfd14f3ed76443dc06bafe7c52a117/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31343937306263642d303762632d656361322d306138632d3730363130393662323235372e6a706567" alt="20171109214219.jpg" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/14970bcd-07bc-eca2-0a8c-7061096b2257.jpeg"></a></p> <p>雰囲気がある。いい。<br> 溺れるナイフは漫画最高だったけど映画最悪だったなー笑</p> <p>そして(x,y)の座標の求め方は、</p> <h2> <span id="x--r--cosθ" class="fragment"></span><a href="#x--r--cos%CE%B8"><i class="fa fa-link"></i></a>x = R * cos(θ)</h2> <h2> <span id="y--r--sinθ" class="fragment"></span><a href="#y--r--sin%CE%B8"><i class="fa fa-link"></i></a>y = R * sin(θ)</h2> <p>でOKだそうです。お、意外といけるのでは?<br> ではこの式を使ってoFで円を描いてみます。<br> まぁofDrawCircle()で描けるのですが、、、勉強に。</p> <p>できたぞ〜!!!!!<br> <a href="https://camo.qiitausercontent.com/af4ce1ca438fff2abb9967032271b63454ecd513/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f36316235636230612d346234642d326533322d353264342d3938303938663566303362622e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/af4ce1ca438fff2abb9967032271b63454ecd513/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f36316235636230612d346234642d326533322d353264342d3938303938663566303362622e706e67" alt="スクリーンショット 2017-11-09 22.37.08.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/61b5cb0a-4b4d-2e32-52d4-98098f5f03bb.png"></a></p> <p>動画<br> <a href="https://www.youtube.com/watch?v=JTVPKTFheS8" class="autolink" rel="nofollow noopener" target="_blank">https://www.youtube.com/watch?v=JTVPKTFheS8</a></p> <p>コード</p> <div class="code-frame" data-lang="cpp"> <div class="code-lang"><span class="bold">ofApp.cpp</span></div> <div class="highlight"><pre><span></span> <span class="cp">#include</span> <span class="cpf">"ofApp.h"</span><span class="cp"></span> <span class="kt">float</span> <span class="n">phase</span><span class="p">;</span> <span class="c1">// 段階の意味</span> <span class="k">const</span> <span class="kt">float</span> <span class="n">R</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span> <span class="c1">// 円の半径</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">setup</span><span class="p">(){</span> <span class="n">ofSetFrameRate</span><span class="p">(</span><span class="mi">60</span><span class="p">);</span> <span class="n">ofSetBackgroundAuto</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="c1">// 毎回背景を塗りつぶしてリセットしない!</span> <span class="n">ofBackground</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">update</span><span class="p">(){</span> <span class="c1">// 段階を更新</span> <span class="n">phase</span> <span class="o">+=</span> <span class="mf">0.01</span><span class="p">;</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">draw</span><span class="p">(){</span> <span class="c1">//原点を画面の中心点に</span> <span class="n">ofTranslate</span><span class="p">(</span><span class="n">ofGetWidth</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">ofGetHeight</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> <span class="c1">//点の座標を三角関数で計算するぞ x = R * cos(θ) y = R * sin(θ)</span> <span class="n">ofVec2f</span> <span class="n">pos</span><span class="p">;</span> <span class="c1">// 円の位置</span> <span class="n">pos</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">R</span> <span class="o">*</span> <span class="n">cos</span><span class="p">(</span><span class="n">phase</span><span class="p">);</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">R</span> <span class="o">*</span> <span class="n">sin</span><span class="p">(</span><span class="n">phase</span><span class="p">);</span> <span class="c1">//色を黄色に</span> <span class="n">ofSetColor</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span><span class="mi">256</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 4ポイントの円を描画</span> <span class="n">ofDrawCircle</span><span class="p">(</span><span class="n">pos</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> <span class="p">}</span> </pre></div> </div> <p><strong>どうやら 先ほどの公式のcos(θ)のθにあたる変数phase(段階)をupdateで増やしていくのがキモみたいっすね。</strong></p> <p>これに時間経過とか入れてやればいいのかな<br> そんくらいの理解でいいかな。。</p> <h1> <span id="浅くてすんません" class="fragment"></span><a href="#%E6%B5%85%E3%81%8F%E3%81%A6%E3%81%99%E3%82%93%E3%81%BE%E3%81%9B%E3%82%93"><i class="fa fa-link"></i></a>浅くてすんません。。</h1> <p>次は、</p> <h2> <span id="y--r--sinθ-1" class="fragment"></span><a href="#y--r--sin%CE%B8-1"><i class="fa fa-link"></i></a>y = R * sin(θ)</h2> <p>の公式を取得したので上下ゆらゆらやってみます!!</p> <p>おっできた</p> <p><a href="https://camo.qiitausercontent.com/8a931fa630600b58c095bbc10aae74e23045e908/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64373435653365632d633033382d303038662d653062322d6430366564386234336330642e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/8a931fa630600b58c095bbc10aae74e23045e908/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64373435653365632d633033382d303038662d653062322d6430366564386234336330642e706e67" alt="スクリーンショット 2017-11-09 22.43.45.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/d745e3ec-c038-008f-e0b2-d06ed8b43c0d.png"></a></p> <p>動画<br> <a href="https://www.youtube.com/watch?v=pQptfrukBRE" class="autolink" rel="nofollow noopener" target="_blank">https://www.youtube.com/watch?v=pQptfrukBRE</a></p> <p><strong>コードはこちら。今回はRがゆらゆらさせる範囲で、変数phase(段階)の増える値がゆらゆらする速度になってるんですね。</strong></p> <div class="code-frame" data-lang="cpp"> <div class="code-lang"><span class="bold">ofApp.cpp</span></div> <div class="highlight"><pre><span></span> <span class="kt">float</span> <span class="n">phase</span><span class="p">;</span> <span class="c1">// 段階の意味</span> <span class="k">const</span> <span class="kt">float</span> <span class="n">R</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">//ゆらゆらさせる範囲</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">setup</span><span class="p">(){</span> <span class="n">ofSetFrameRate</span><span class="p">(</span><span class="mi">60</span><span class="p">);</span> <span class="n">ofBackground</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">update</span><span class="p">(){</span> <span class="c1">// 段階を更新</span> <span class="n">phase</span> <span class="o">+=</span> <span class="mf">0.01</span><span class="p">;</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">draw</span><span class="p">(){</span> <span class="c1">//原点を画面の中心点に</span> <span class="n">ofTranslate</span><span class="p">(</span><span class="n">ofGetWidth</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">ofGetHeight</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> <span class="n">ofVec2f</span> <span class="n">pos</span><span class="p">;</span> <span class="n">pos</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 左右は運動なし</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">R</span> <span class="o">*</span> <span class="n">sin</span><span class="p">(</span><span class="n">phase</span><span class="p">);</span> <span class="c1">// 上下ゆらゆら</span> <span class="c1">//色を黄色に</span> <span class="n">ofSetColor</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span><span class="mi">256</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 30ポイントの円を描画</span> <span class="n">ofDrawCircle</span><span class="p">(</span><span class="n">pos</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span> <span class="p">}</span> </pre></div> </div> <h2> <span id="x--r--cosθ-1" class="fragment"></span><a href="#x--r--cos%CE%B8-1"><i class="fa fa-link"></i></a>x = R * cos(θ)</h2> <p>を使えば左右ゆらゆらも表現できると思います^^。<br> ちょっとは三角関数と仲良くなれた気がします!<br> 今回で基礎をかじった気がするから、sin波も学んでみたいです。</p> <p>完。</p> <p>本家のブログはこちら 超雑多です。w🍺<br> ホンキートンク・スーダラブルース<br> <a href="http://www.sudara-bluse.tokyo/entry/openframeworks_5" class="autolink" rel="nofollow noopener" target="_blank">http://www.sudara-bluse.tokyo/entry/openframeworks_5</a></p> ",
        body: " #プロローグ ![20171109214610.png](https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png) 参照: http://r-dimension.xsrv.jp/classes_j/sine_cosine/ **うげ〜。いやだこの図。。**はい、こんにちは。。。 こちらの図見るとオエ〜ってなりますよね。僕はなります。 なのでお口直しに、小松菜奈さんの画像貼っときますわ。 <img src="
    https: //qiita-image-store.s3.amazonaws.com/0/76843/d8137da6-ec76-c452-f679-bf0a0def41dd.jpeg" width="200"> 最近ネバヤン(バンド)に胸熱で。。お別れの歌のMVやばい。 https://www.youtube.com/watch?v=ZFI-Hqeu_Ag #本題 **円運動とかゆらゆら上下運動とかってsin()とかcos()が必要じゃないですか? メディアアート系のプログラミングって。oFとかprocessingとか、Unityも?。** なのでなるべく優し〜く入門勉強してきたいと思います。 参考サイト http://yoppa.org/ma2_10/1739.html http://r-dimension.xsrv.jp/classes_j/sine_cosine/ #入門します! >さて、プログラムによって図形を描く場合に避けて通れないのが三角関数(サイン、コサイン)です。 円などに代表される曲線を描く際によく使われます。 タンジェントは使用頻度が低いので、ここでは省略します。 だそうです。。。 まず先ほどの図。 大丈夫だから、ちょっとみてみてください。 ![20171109214610.png](https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png) これを勇気を出して、単純に読み解くと、 ##R → 円の半径 ##(x , y) →円上の座標 ##θ → 角度 らしいです。 ![20171109214219.jpg](https://qiita-image-store.s3.amazonaws.com/0/76843/14970bcd-07bc-eca2-0a8c-7061096b2257.jpeg) 雰囲気がある。いい。 溺れるナイフは漫画最高だったけど映画最悪だったなー笑 そして(x,y)の座標の求め方は、 ## x = R * cos(θ) ## y = R * sin(θ) でOKだそうです。お、意外といけるのでは? ではこの式を使ってoFで円を描いてみます。 まぁofDrawCircle()で描けるのですが、、、勉強に。 できたぞ〜!!!!! ![スクリーンショット 2017-11-09 22.37.08.png](https://qiita-image-store.s3.amazonaws.com/0/76843/61b5cb0a-4b4d-2e32-52d4-98098f5f03bb.png) 動画 https://www.youtube.com/watch?v=JTVPKTFheS8 コード ```cpp:ofApp.cpp #include "ofApp.h" float phase; // 段階の意味 const float R = 200; // 円の半径 //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); ofSetBackgroundAuto(false); // 毎回背景を塗りつぶしてリセットしない! ofBackground(255, 255, 255); } //-------------------------------------------------------------- void ofApp::update(){ // 段階を更新 phase += 0.01; } //-------------------------------------------------------------- void ofApp::draw(){ //原点を画面の中心点に ofTranslate(ofGetWidth()/2, ofGetHeight()/2); //点の座標を三角関数で計算するぞ x = R * cos(θ) y = R * sin(θ) ofVec2f pos; // 円の位置 pos.x = R * cos(phase); pos.y = R * sin(phase); //色を黄色に ofSetColor(256,256, 0); // 4ポイントの円を描画 ofDrawCircle(pos.x, pos.y, 4); } ``` **どうやら 先ほどの公式のcos(θ)のθにあたる変数phase(段階)をupdateで増やしていくのがキモみたいっすね。** これに時間経過とか入れてやればいいのかな そんくらいの理解でいいかな。。 #浅くてすんません。。 次は、 ## y = R * sin(θ) の公式を取得したので上下ゆらゆらやってみます!! おっできた ![スクリーンショット 2017-11-09 22.43.45.png](https://qiita-image-store.s3.amazonaws.com/0/76843/d745e3ec-c038-008f-e0b2-d06ed8b43c0d.png) 動画 https://www.youtube.com/watch?v=pQptfrukBRE **コードはこちら。今回はRがゆらゆらさせる範囲で、変数phase(段階)の増える値がゆらゆらする速度になってるんですね。** ```cpp:ofApp.cpp float phase; // 段階の意味 const float R = 100; //ゆらゆらさせる範囲 //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); ofBackground(255, 255, 255); } //-------------------------------------------------------------- void ofApp::update(){ // 段階を更新 phase += 0.01; } //-------------------------------------------------------------- void ofApp::draw(){ //原点を画面の中心点に ofTranslate(ofGetWidth()/2, ofGetHeight()/2); ofVec2f pos; pos.x = 0; // 左右は運動なし pos.y = R * sin(phase); // 上下ゆらゆら //色を黄色に ofSetColor(256,256, 0); // 30ポイントの円を描画 ofDrawCircle(pos.x, pos.y, 30); } ``` ## x = R * cos(θ) を使えば左右ゆらゆらも表現できると思います^^。 ちょっとは三角関数と仲良くなれた気がします! 今回で基礎をかじった気がするから、sin波も学んでみたいです。 完。 本家のブログはこちら 超雑多です。w🍺 ホンキートンク・スーダラブルース http://www.sudara-bluse.tokyo/entry/openframeworks_5 ",
        coediting: false,
    comments_count: 0,
    created_at: "2017-11-09T22:50:51+09:00",
    group: null,
    id: "fe412a67b64e793d138b",
    likes_count: 0,
    private: false,
    reactions_count: 0,
    tags: [{
            name: "processing",
            versions: []
        },
        {
            name: "openFrameworks",
            versions: []
        }
    ],
    title: "【openFrameworks 初心者冒険記5】逃げまくってた...三角関数の扉を再ノック。sin() cos() サインコサイン....。メディアアートで避けては通れない?最初の壁",
    updated_at: "2017-11-09T22:50:51+09:00",
    url: "http://qiita.com/39_isao/items/fe412a67b64e793d138b",
    user: {
        description: "鮭とメロンパンが好物な奥田民生になりたいボーイです。 最近はてなブログ始めました http://sudara-bluse.hatenablog.com/",
        facebook_id: "",
        followees_count: 52,
        followers_count: 44,
        github_login_name: null,
        id: "39_isao",
        items_count: 103,
        linkedin_id: "",
        location: "",
        name: "",
        organization: "",
        permanent_id: 76843,
        profile_image_url: "https://qiita-image-store.s3.amazonaws.com/0/76843/profile-images/1508030106",
        twitter_screen_name: "sudara_bluse",
        website_url: "http://sudara-bluse.hatenablog.com/"
    }
}, 
{,,,}
]

今回は、以下のデータを使ってiOSアプリを作ってみます。

  • 投稿タイトル
  • ユーザアイコン
  • URL

完成したアプリ

f:id:decoch:20200228170154g:plain

プロジェクトを作成する

f:id:decoch:20200228170522p:plain f:id:decoch:20200228170532p:plain f:id:decoch:20200228170539p:plain

使うライブラリをインストールする

dependencies:
  flutter:
    sdk: flutter

  provider: ^4.0.4
  http: ^0.12.0+4
  webview_flutter: ^0.3.19+8

通信周りのクラスを作成する

JSONを変換するための構造体を作成

class Item {
  final String id;
  final String title;
  final String body;
  final String url;
  final User user;

  Item({
    this.id,
    this.title,
    this.body,
    this.url,
    this.user,
  });

  static Item fromJson(dynamic json) {
    return Item(
      id: json['id'],
      title: json['title'],
      body: json['body'],
      url: json['url'],
      user: User.fromJson(json['user']),
    );
  }
}

class User {
  final String profileImageUrl;

  User({
    this.profileImageUrl,
  });

  static User fromJson(dynamic json) {
    return User(
      profileImageUrl: json['profile_image_url'],
    );
  }
}

APIからデータを取得するクラスを実装

import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

import 'item.dart';

class ItemModel extends ChangeNotifier {
  List<Item> get items => _items;

  List<Item> _items = [];

  void search(String query) async {
    final http.Response res =
    await http.get("https://qiita.com//api/v2/items?query=$query");
    if (res.statusCode != 200) {
      throw HTTPException(res.statusCode, "unable to fetch weather data");
    }
    final itemsJson = json.decode(res.body) as List;
    _items = itemsJson.map((data) => Item.fromJson(data)).toList();
    notifyListeners();
  }
}
class HTTPException implements Exception {
  final int code;
  final String message;

  HTTPException(this.code, this.message) : assert(code != null);

  @override
  String toString() {
    return 'HTTPException{code: $code, message: $message}';
  }
}

一覧画面を作る

一覧画面を作成する

新しく item_list_screen.dart というファイルを作成します

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:qiita_client/models/item.dart';

import 'item_detail_screen.dart';
import 'models/item_model.dart';

class ItemListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final itemModel = Provider.of<ItemModel>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Qiita Client'),
      ),
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.only(left: 8, right: 8),
            child: TextField(
              decoration: InputDecoration(labelText: "Search something"),
              onSubmitted: (text) {
                itemModel.search(text);
              },
            ),
          ),
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.all(8),
              itemCount: itemModel.items.length,
              itemBuilder: (BuildContext context, int index) {
                final item = itemModel.items[index];
                return ItemListRow(item: item);
              },
            ),
          )
        ],
      ),
    );
  }
}

class ItemListRow extends StatelessWidget {
  final Item item;

  const ItemListRow({Key key, this.item}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
      },
      child: Container(
        height: 60,
        child: Row(
          children: [
            Container(
              height: 44,
              width: 44,
              padding: const EdgeInsets.only(right: 8),
              child: Image.network(item.user.profileImageUrl),
            ),
            Expanded(
              child: Text('Entry ${item.title}'),
            ),
          ],
        ),
      ),
    );
  }
}

起動画面を一覧画面にする

もともと作成されている main.dart を修正して一覧画面を表示するようにします

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:qiita_client/item_list.dart';

import 'models/item_model.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<ItemModel>(
          create: (_) => ItemModel(),
        ),
      ],
      child: MaterialApp(
        title: 'Qiita client app',
        theme: ThemeData(
          primarySwatch: Colors.orange,
        ),
        home: ItemListScreen(),
      ),
    );
  }
}

起動する

ここまで作ったら一度起動して入力した文字で検索できることを確認してみましょう

詳細画面を作る

詳細画面は一覧でタップした投稿をWebViewで表示します 新しくitem_detail_screen.dartを作成します

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class ItemDetailScreen extends StatelessWidget {
  final String title;
  final String url;

  const ItemDetailScreen({
    Key key,
    this.title,
    this.url,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: WebView(
        initialUrl: url,
      ),
    );
  }
}

一覧から詳細画面に遷移できるようにする

item_list_screen.dart を以下のように修正して画面遷移できるようにします

      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) =>
                ItemDetailScreen(title: item.title, url: item.url),
          ),
        );
      },

実行してみましょう

最後に

完成はこちらにあります。 github.com

おつかれさまでした。

SchemaPolicyCheckでDB設計ルールを自動チェックしよう

目次

  • SchemaPolicyチェックとは
  • 設定方法

SchemaPolicyチェックとは

プログラミングをする際にはコードのチェックスタイルを使っていると思います。 Ruby では rubocop, JavaならIntelliJEclipseのフォーマッタが一般的ですね。

コードよりも気軽に変更できないDBに関してはこのようなチェックを行っているチームはあまりないと思います。 そこで今回は、SchemaPocilyCheckを使いBooleanなら末尾にFLGをつけると言った細かなルールを自動的にチェックする方法を紹介します。

dbflute.seasar.org

設定方法

DBFlute-introを使ってチェックを行うのでまだ設定をしていない人はこちらから設定を行ってください

decoch.hatenablog.com

まずDBFlute-introのSchemaPolicyCheckのページを開きます。 ここでは、

  • SchemaPolicyCheckの実行
  • DB設計ルールの設定

を行う事ができます。

f:id:decoch:20190831152556p:plain
schema-policy-check

では、実際に設定を変更していきます。

以下ルールの設定をして実行を押下しましたが成功していますね。

  • テーブルのPKの必須化
  • テーブル名を全て小文字

f:id:decoch:20190831152813p:plain
schema-policy-setting-valid

今後は失敗するように以下設定にして実行してみます

  • テーブルのPKの必須化
  • テーブル名を全て大文字

f:id:decoch:20190831153038p:plain
invalid

失敗すると失敗理由をリンクから確認できるようになります。

f:id:decoch:20190831153123p:plain
error messages

この機能を使うことで、単純なルールはDBFluteにまかせてレビュワーはもっと本質的なレビューに集中することができます。

まとめ

  • DB設計のルール確認は人がチェックしているケースが多い
  • DBFluteIntroを使えば、ルール設定を行い自動チェックができる

Elasticsaerchでサジェスト検索を作ってみよう

目次

  • Elasticsearch の説明
  • 検索の基礎知識
  • サジェスト検索をやってみる
  • まとめ

Elasticsearchとは

Elastic社が開発している全文検索エンジンで、リアルタイムデータ分析、ログ解析、全文検索など様々な用途に使われています。

検索の基礎知識

Elasticsearchでは保存されている文書の文字と、検索ワードで与えられた文字列がマッチしたら検索結果に含まれます

たとえば、

this is a test

というデータがあった場合、

this / is / a / test

と分解されるので、 thisisatest で検索するとこのデータが返ってきます。

サジェスト検索をやってみる

上の設定のままだと tth という検索ワードではデータが返ってこないためサジェスト検索を実現することができません。

そこで文章の解析方法を指定するTokenizerの設定を変更します。

今回は、thist th のように単語をより詳細に分割をする必要があります。 そのためには、ngramedge_ngram という Tokenizer があるので見ていきましょう。

ngram は、以下の様に単語を1~2文字に分割してくれます。

t th h hi i is s / i is s / a / t te e es s st t

edge_ngram は、以下の様に文字を分割してくれます。

t  th thi this / i is / a / t te tes test

こうすることで、tth と入力したタイミングで結果を返すサジェスト検索を実現することができます。

www.elastic.co

www.elastic.co

まとめ

  • Elasticsearch は様々な用途に使われている検索エンジン
  • Tokenizerが分割したTokenに当てはまる文字とInputが一致すると検索結果に含まれる
  • サジェスト検索をするためには単語単位ではなく、文字数ベースで文章の分割をする必要がある

DBFlute-IntroでDB変更履歴を管理する

目次

  • HistoryHTMLとは
  • 実際にHistoryHTMLを生成する
  • コメントを書こう
  • まとめ

HistoryHTMLとは

スキーマの変更履歴を人が見やすい形にまとめたドキュメントです。
DBFluteが自動生成のためにメタデータを取得するたびに、一つ前のメタデータと内容の比較をしてスキーマ構造に違いがあった部分だけをドキュメントにまとめます。

HistoryHTML | DBFlute

実際にHistoryHTMLを生成する

アプリケーションで、DBFluteを使っている場合は自動で生成されます。 もし使っていない場合は、DBFluteIntroの「Generate Documents」ボタンをクリックしましょう。

f:id:decoch:20190817120259p:plain

するとHistoryHTMLが更新されます

f:id:decoch:20190817120426p:plain
更新前

f:id:decoch:20190817120447p:plain
更新後

このように以前のDB変更からどのような変更がはいったのか確認をすることができます。

コメントを書こう

DBFlute-intro特有の機能として、diffNo のところをクリックするとHistoryHTMLにコメントを追記することができます。 この機能を使うことで、リリース単位でどんなDB変更をしたのか、意図はなんだったのかドキュメントにまとめることができます

f:id:decoch:20190817120516p:plain
クリック時

f:id:decoch:20190817120544p:plain
入力後

dbflute.seasar.org

まとめ

  • DBFluteはHistoryHTMLという機能でDBの履歴を管理できる
  • あとでコメントを追加し、どういった意図でDB変更したのかドキュメントにまとめられる

Railsで外部キー成約を追加した際のIndex名のまとめ

普段 Ruby on Rails でつくられたWebサービス開発をしています。 この前、既存カラムに外部キー成約(FK)を追加したとき普段と違う命名の index名になってしました。

FK追加時にどのような index 名になるのか気になったので、いろいろなパターンを試してまとめました!

Indexの貼り方

RailsアプリケーションではFKを貼る際に大きく以下の方法があります。

1. テーブル作成時に指定するカラム作成時にFKを貼る

class AddTable < ActiveRecord::Migration[5.2]
  def change
    create_table :parent1s
    create_table :parent2s
    create_table :parent3s

    create_table :children do |t|
      t.references :parent1, foreign_key: true
    end
  end
end

2.カラム追加時にFKを貼る

class AddChild < ActiveRecord::Migration[5.2]
  def change
    add_reference :children, :parent2, foreign_key: true
  end
end

3.後からFKを貼る

class AddFk < ActiveRecord::Migration[5.2]
  def change
    add_column :children, :parent3_id, :bigint
    add_foreign_key :children, :parent3s, column: "parent3_id"
  end
end

生成されたIndex

実行結果

schema.rb

ActiveRecord::Schema.define(version: 2019_08_10_050953) do

  create_table "children", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.bigint "parent1_id"
    t.bigint "parent2_id"
    t.bigint "parent3_id"
    t.index ["parent1_id"], name: "index_children_on_parent1_id"
    t.index ["parent2_id"], name: "index_children_on_parent2_id"
    t.index ["parent3_id"], name: "fk_rails_c04e2dbc44"
  end

  create_table "parent1s", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
  end

  create_table "parent2s", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
  end

  create_table "parent3s", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
  end

  add_foreign_key "children", "parent1s"
  add_foreign_key "children", "parent2s"
  add_foreign_key "children", "parent3s"
end

add_foreigin_key でFKを貼った場合、Indexの名前がFK成約と同じ名前になってしまいます。 fk_rails_c04e2dbc44

Indexは後からチューニングする際に名前の規約が決まっていた方が便利なので、この名前は使いたくないですよね。 対処方法は、以下のように際にIndexを貼り、あとでFKを貼ることで回避することができます

class AddFk < ActiveRecord::Migration[5.2]
  def change
    add_column :children, :parent3_id, :bigint
    add_index :children, :parent3_id
    add_foreign_key :children, :parent3s, column: "parent3_id"
  end
end

まとめ

  • FKの貼り方は大きく3種類ある
  • 既存カラムに add_foreign_key でFKを追加したときだけ Indexの名前が違うので注意する

Railsのスキーマ情報をDBFlute-Introで管理しよう

前回 DBFlute-Intro を使ってデータベースのドキュメントを管理する方法をまとめました。

decoch.hatenablog.com

私は普段、Ruby on RailsWebサービスの開発をしているのですが、その際にいくつかエラーが出てしまったのでその回避方法をまとめます。

DBFlute-IntroをRailsを使う時の問題

Ruby on Rails で開発するときは、ライブラリが自動生成するテーブルがあったり、カラム名に規約があり意図せずにMySQL予約語を使ってしまうケースが多々あります。 一方でDBFlute-Intro では、依存しているDBFluteが基本的に予約語は使わない想定で作られているため、ドキュメント生成時に以下のようなエラーが起きてしまいました。

[df-jdbc] /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
[df-jdbc] Failed to execute the SQL for getting auto-increment
[df-jdbc] 
[df-jdbc] [Advice]
[df-jdbc] DBFlute executes the SQL to get auto-increment meta data.
[df-jdbc] The table might not exist on your schema. Or the schema
[df-jdbc] to be set at 'dfprop' might be mistake in the first place.
[df-jdbc] 
[df-jdbc] And other points can be causes
[df-jdbc]  e.g. reservation word, authentication, ...
[df-jdbc] If your primary key of the table is reservation word in the DBMS,
[df-jdbc] set quatation settings 'quoteColumnNameList' of littleAdjustmentMap.dfprop.
[df-jdbc] 
[df-jdbc] So check your settings and environments.
[df-jdbc] 
[df-jdbc] [Table]
[df-jdbc] maihamadb.ar_internal_metadata
[df-jdbc] 
[df-jdbc] [PrimaryKey]
[df-jdbc] key
[df-jdbc] 
[df-jdbc] [SQL for getting]
[df-jdbc] select key from ar_internal_metadata where 0 = 1
[df-jdbc] * * * * * * * * * */

key という名前が予約語になっているようです...

解決策

DBFlute予約語を使いたい場合は、 自動で生成された dbflute_xxx のディレクトリに存在する、littleAdjustmentMap.dfprop を以下のように変更します。

ディレクト

dbflute_xxx
└ dfprop
    └ littleAdjustmentMap.dfprop
-#; quoteTableNameList = list:{}
+; quoteTableNameList = list:{$$ALL$$}

... 省略

-#; quoteColumnNameList = list:{}
+; quoteColumnNameList = list:{$$ALL$$}

この変更をすることで、テーブル名とカラム名をバッククオートで囲った状態で扱う事ができます。

まとめ

DBFlute Intro を使ってデータベースのドキュメント管理を楽にしよう

以前、開発人数の多いサービスで働いているメンバーから、「データベースにコメントは残したいんだけど、DB変更の手順が面倒だったり、レビューにはDBAの人が必要で気軽にできないんだよー」という悩みを耳にしました。
DBFlute Intro ではDB変更なしでドキュメントの更新、コメント追加が行える機能を開発しました。
今回はその手順をまとめました。

目次

  • そもそもSchemaHTML とは
  • ドキュメントの表示手順
  • ドキュメントの更新方法
  • チームでのドキュメントの共有方法
  • まとめ

そもそもSchemaHTML とは

DBFlute Intro では SchemaHTMLという、スキーマメタデータ(テーブル構成など)を人が見やすい形にまとめたドキュメントを閲覧することができます

f:id:decoch:20190803095140p:plain
schema html

詳しくはこちらをご覧ください dbflute.seasar.org

ドキュメントの表示手順

1.DBFlute Intro のダウンロード

Githubのリリースページ から最新の jar ファイルをダウンロードします

f:id:decoch:20190803103043p:plain
github release

2.ダウンロードした jar ファイルを「右クリック」をして「開く」を押すと数秒後にブラウザで自動的に立ち上がります。

f:id:decoch:20190803103129p:plain
download

3.立ち上がった画面にドキュメント生成したいDBの情報を入力します

f:id:decoch:20190803103351p:plain
dbflute intro welcome page

4.DBのセットアップが完了するとTOP画面遷移するので、生成したDB名をクリックします

f:id:decoch:20190803112158p:plain
client

すると以下画面が表示されるので、「Generate Documents」をクリックすると数秒後にドキュメントへのリンクが表示されます

f:id:decoch:20190803103501p:plain
dbflute intro document

f:id:decoch:20190803103701p:plain
クリック後

5.実際にドキュメントを開きます

f:id:decoch:20190803103759p:plain
schema document

ドキュメントの更新方法

SchemaHTMLの更新したいテーブルや、カラムのコメント部分をクリックします。(表の「Table Comment、Column Comment」)

ダイアログが立ち上がるのでコメントを変更して「OK」をクリックします

f:id:decoch:20190803110554p:plain
decomment

するとコメントが変更され、誰が書いたのか auther名が追加されます

変更前 f:id:decoch:20190803110945p:plain

変更後 f:id:decoch:20190803110948p:plain

またDB変更があったときには、「Generate Documents」をクリックすると作成したコメントを残したままDB変更をドキュメントに反映をすることができます

チームでのドキュメントの共有方法

jar ファイルをダウンロードしたディレクトリを見ると、いくつかディレクトリができています。 コメントはこのディレクトリでファイル管理されているので、これらまとめて開発チームの git レポジトリとして共有するとドキュメントの共有をすることができます。

※ 以下の様なディレクトリなら my-project を Git 管理する

f:id:decoch:20190803112645p:plain
finder

まとめ

  • DBコメントは管理したいが気軽に変更しづらい
  • DBFlute Intro では ドキュメントの自動生成とコメントの気軽な更新ができる
  • チーム内での共有は Git レポジトリを用意すれば簡単にできる