Flutterで書くアプリハンズオン

## これは何？

Flutterを使ったハンズオン用の記事です。
以前Flutterの勉強をした際に簡単なQiitaClientを作ったので、イベントなどで登壇したときに使えるようにハンズオン形式にまとめてみました。

## 使用するAPI
今回はQiitaAPiを題材とします。([ドキュメント](https://qiita.com/api/v2/docs))

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

```javascript
[{
    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

完成したアプリ

![完成](https://cdn-ak.f.st-hatena.com/images/fotolife/d/decoch/20200228/20200228170154.gif "完成")

## プロジェクトを作成する

![1](https://cdn-ak.f.st-hatena.com/images/fotolife/d/decoch/20200228/20200228170522.png "1")
![2](https://cdn-ak.f.st-hatena.com/images/fotolife/d/decoch/20200228/20200228170532.png "2")
![3](https://cdn-ak.f.st-hatena.com/images/fotolife/d/decoch/20200228/20200228170539.png "3")

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

```yaml
dependencies:
  flutter:
    sdk: flutter

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

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

### JSONを変換するためのクラスを作成

```dart
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からデータを取得するクラスを実装

```swift
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 というファイルを作成します

```swift
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 を修正して一覧画面を表示するようにします

```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を作成します

```swift
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 を以下のように修正して画面遷移できるようにします

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

## 実行してみましょう

## 最後に

完成は[こちら](https://github.com/decoch/flutter_example_qiita_client)にあります。



おつかれさまでした。