贾鹏辉的技术博客官网 关于移动端技术 | Android&iOS Development Manager | 这里是 CrazyCodeBoy@贾鹏辉 的个人博客,分享技术干货。 http://localhost:4000/ Mon, 22 Apr 2019 00:11:56 +0800 Mon, 22 Apr 2019 00:11:56 +0800 Jekyll v3.6.2 Flutter Hero动画开发实用教程 <p><img src="io/flutter_app/img/blog/flutter-animation-tutorial-hero.png" alt="Flutter 动画开发实用教程" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将向大家分享Flutter动画中的重要一员<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Hero动画</code></a>,以及一些<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Hero动画的开发技巧和经验</code></a>。</p> <blockquote> <ul> <li>在大家Flutter开发环境过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> </ul> </blockquote> <p>精心设计的动画会让用户界面感觉更直观、流畅,能改善用户体验。 Flutter的动画支持可以轻松实现各种动画类型。许多widget,特别是<a href="https://coding.imooc.com/class/321.html">Material Design widgets</a>, 都带有在其设计规范中定义的标准动画效果,但也可以自定义这些效果,在开始学习之前呢,我们先来快速过一下本篇文章的目录:</p> <h2 id="目录">目录</h2> <ul> <li>什么是<a href="https://coding.imooc.com/class/321.html">Hero</a>动画?</li> <li>如何实现标准<a href="https://coding.imooc.com/class/321.html">Hero</a>动画?</li> <li>Hero的函数原型的函数原型是什么?</li> <li>如何实现径向<a href="https://coding.imooc.com/class/321.html">Hero</a>动画?</li> </ul> <h3 id="什么是hero动画">什么是Hero动画?</h3> <p>在 Flutter中可以用 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Hero</code></a> widget创建这个动画。当 <a href="https://coding.imooc.com/class/321.html">Hero</a> 通过动画从源页面飞到目标页面时,目标页面逐渐淡入视野。通常, <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Hero</code></a> 是用户界面的一小部分,如图片,它通常在两个页面都有。从用户的角度来看, <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Hero</code></a> 在页面之间“飞翔”。接下来我们一起来学习如何创建<a href="https://coding.imooc.com/class/321.html">Hero</a>动画:</p> <h4 id="实现标准hero动画">实现标准Hero动画</h4> <p><img src="io/flutter_app/img/blog/Standard-Hero-Animation.gif" alt="Standard-Hero-Animation" /></p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="kd">class</span> <span class="nc">PhotoHero</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="kd">const</span> <span class="n">PhotoHero</span><span class="o">({</span> <span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">photo</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">onTap</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">width</span> <span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="kd">final</span> <span class="kt">String</span> <span class="n">photo</span><span class="o">;</span> <span class="kd">final</span> <span class="n">VoidCallback</span> <span class="n">onTap</span><span class="o">;</span> <span class="kd">final</span> <span class="kt">double</span> <span class="n">width</span><span class="o">;</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">SizedBox</span><span class="o">(</span> <span class="nl">width:</span> <span class="n">width</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Hero</span><span class="o">(</span> <span class="nl">tag:</span> <span class="n">photo</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Material</span><span class="o">(</span> <span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">transparent</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">InkWell</span><span class="o">(</span> <span class="nl">onTap:</span> <span class="n">onTap</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Image</span><span class="o">.</span><span class="na">network</span><span class="o">(</span> <span class="n">photo</span><span class="o">,</span> <span class="nl">fit:</span> <span class="n">BoxFit</span><span class="o">.</span><span class="na">contain</span><span class="o">,</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">HeroAnimation</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="n">timeDilation</span> <span class="o">=</span> <span class="mf">10.0</span><span class="o">;</span> <span class="c1">// 1.0 means normal animation speed.</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="kd">const</span> <span class="n">Text</span><span class="o">(</span><span class="s">'Basic Hero Animation'</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">PhotoHero</span><span class="o">(</span> <span class="nl">photo:</span> <span class="s">'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/Hero_animation/images/flippers-alpha.png'</span><span class="o">,</span> <span class="nl">width:</span> <span class="mf">300.0</span><span class="o">,</span> <span class="nl">onTap:</span> <span class="o">()</span> <span class="o">{</span> <span class="n">Navigator</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">context</span><span class="o">).</span><span class="na">push</span><span class="o">(</span><span class="n">MaterialPageRoute</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;(</span> <span class="nl">builder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="kd">const</span> <span class="n">Text</span><span class="o">(</span><span class="s">'Flippers Page'</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">Container</span><span class="o">(</span> <span class="c1">// Set background to blue to emphasize that it's a new route.</span> <span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">lightBlueAccent</span><span class="o">,</span> <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="o">(</span><span class="mf">16.0</span><span class="o">),</span> <span class="nl">alignment:</span> <span class="n">Alignment</span><span class="o">.</span><span class="na">topLeft</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">PhotoHero</span><span class="o">(</span> <span class="nl">photo:</span> <span class="s">'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/Hero_animation/images/flippers-alpha.png'</span><span class="o">,</span> <span class="nl">width:</span> <span class="mf">100.0</span><span class="o">,</span> <span class="nl">onTap:</span> <span class="o">()</span> <span class="o">{</span> <span class="n">Navigator</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">context</span><span class="o">).</span><span class="na">pop</span><span class="o">();</span> <span class="o">},</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">));</span> <span class="o">},</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="o">...</span> </code></pre></div></div> <h4 id="hero的函数原型">Hero的函数原型</h4> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">const</span> <span class="nf">Hero</span><span class="p">(</span><span class="o">{</span> <span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="nd">@required</span> <span class="k">this</span><span class="o">.</span><span class="na">tag</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">createRectTween</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">flightShuttleBuilder</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">placeholderBuilder</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">transitionOnUserGestures</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="nd">@required</span> <span class="k">this</span><span class="o">.</span><span class="na">child</span><span class="o">,</span> <span class="o">})</span> <span class="o">:</span> <span class="k">assert</span><span class="o">(</span><span class="n">tag</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">),</span> <span class="k">assert</span><span class="o">(</span><span class="n">transitionOnUserGestures</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">),</span> <span class="k">assert</span><span class="o">(</span><span class="n">child</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">),</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> </code></pre></div></div> <ul> <li><a href="https://coding.imooc.com/class/321.html">tag</a>:[必须]用于关联两个Hero动画的标识;</li> <li><a href="https://coding.imooc.com/class/321.html">createRectTween</a>:[可选]定义目标Hero的边界,在从起始位置到目的位置的“飞行”过程中该如何变化;</li> <li><a href="https://coding.imooc.com/class/321.html">child</a>:[必须]定义动画所呈现的widget;</li> </ul> <h4 id="实现径向hero动画">实现径向Hero动画</h4> <p><img src="io/flutter_app/img/blog/Radial-Hero-Animation.gif" alt="Radial-Hero-Animation" /></p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="kd">class</span> <span class="nc">RadialExpansionDemo</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="kd">static</span> <span class="kd">const</span> <span class="kt">double</span> <span class="n">kMinRadius</span> <span class="o">=</span> <span class="mf">32.0</span><span class="o">;</span> <span class="kd">static</span> <span class="kd">const</span> <span class="kt">double</span> <span class="n">kMaxRadius</span> <span class="o">=</span> <span class="mf">128.0</span><span class="o">;</span> <span class="kd">static</span> <span class="kd">const</span> <span class="n">opacityCurve</span> <span class="o">=</span> <span class="kd">const</span> <span class="n">Interval</span><span class="o">(</span><span class="mf">0.0</span><span class="o">,</span> <span class="mf">0.75</span><span class="o">,</span> <span class="nl">curve:</span> <span class="n">Curves</span><span class="o">.</span><span class="na">fastOutSlowIn</span><span class="o">);</span> <span class="kd">static</span> <span class="n">RectTween</span> <span class="n">_createRectTween</span><span class="o">(</span><span class="n">Rect</span> <span class="n">begin</span><span class="o">,</span> <span class="n">Rect</span> <span class="n">end</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">MaterialRectCenterArcTween</span><span class="o">(</span><span class="nl">begin:</span> <span class="n">begin</span><span class="o">,</span> <span class="nl">end:</span> <span class="n">end</span><span class="o">);</span> <span class="o">}</span> <span class="kd">static</span> <span class="n">Widget</span> <span class="n">_buildPage</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="kt">String</span> <span class="n">imageName</span><span class="o">,</span> <span class="kt">String</span> <span class="n">description</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Container</span><span class="o">(</span> <span class="nl">color:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">context</span><span class="o">).</span><span class="na">canvasColor</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">Card</span><span class="o">(</span> <span class="nl">elevation:</span> <span class="mf">8.0</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Column</span><span class="o">(</span> <span class="nl">mainAxisSize:</span> <span class="n">MainAxisSize</span><span class="o">.</span><span class="na">min</span><span class="o">,</span> <span class="nl">children:</span> <span class="o">[</span> <span class="n">SizedBox</span><span class="o">(</span> <span class="nl">width:</span> <span class="n">kMaxRadius</span> <span class="o">*</span> <span class="mf">2.0</span><span class="o">,</span> <span class="nl">height:</span> <span class="n">kMaxRadius</span> <span class="o">*</span> <span class="mf">2.0</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Hero</span><span class="o">(</span> <span class="nl">createRectTween:</span> <span class="n">_createRectTween</span><span class="o">,</span> <span class="nl">tag:</span> <span class="n">imageName</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">RadialExpansion</span><span class="o">(</span> <span class="nl">maxRadius:</span> <span class="n">kMaxRadius</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Photo</span><span class="o">(</span> <span class="nl">photo:</span> <span class="n">imageName</span><span class="o">,</span> <span class="nl">onTap:</span> <span class="o">()</span> <span class="o">{</span> <span class="n">Navigator</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">context</span><span class="o">).</span><span class="na">pop</span><span class="o">();</span> <span class="o">},</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="n">Text</span><span class="o">(</span> <span class="n">description</span><span class="o">,</span> <span class="nl">style:</span> <span class="n">TextStyle</span><span class="o">(</span><span class="nl">fontWeight:</span> <span class="n">FontWeight</span><span class="o">.</span><span class="na">bold</span><span class="o">),</span> <span class="nl">textScaleFactor:</span> <span class="mf">3.0</span><span class="o">,</span> <span class="o">),</span> <span class="kd">const</span> <span class="n">SizedBox</span><span class="o">(</span><span class="nl">height:</span> <span class="mf">16.0</span><span class="o">),</span> <span class="o">],</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="n">Widget</span> <span class="n">_buildHero</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="kt">String</span> <span class="n">imageName</span><span class="o">,</span> <span class="kt">String</span> <span class="n">description</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Container</span><span class="o">(</span> <span class="nl">width:</span> <span class="n">kMinRadius</span> <span class="o">*</span> <span class="mf">2.0</span><span class="o">,</span> <span class="nl">height:</span> <span class="n">kMinRadius</span> <span class="o">*</span> <span class="mf">2.0</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Hero</span><span class="o">(</span> <span class="nl">createRectTween:</span> <span class="n">_createRectTween</span><span class="o">,</span> <span class="nl">tag:</span> <span class="n">imageName</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">RadialExpansion</span><span class="o">(</span> <span class="nl">maxRadius:</span> <span class="n">kMaxRadius</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Photo</span><span class="o">(</span> <span class="nl">photo:</span> <span class="n">imageName</span><span class="o">,</span> <span class="nl">onTap:</span> <span class="o">()</span> <span class="o">{</span> <span class="n">Navigator</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">context</span><span class="o">).</span><span class="na">push</span><span class="o">(</span> <span class="n">PageRouteBuilder</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;(</span> <span class="nl">pageBuilder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">animation</span><span class="o">,</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">secondaryAnimation</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">AnimatedBuilder</span><span class="o">(</span> <span class="nl">animation:</span> <span class="n">animation</span><span class="o">,</span> <span class="nl">builder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Widget</span> <span class="n">child</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Opacity</span><span class="o">(</span> <span class="nl">opacity:</span> <span class="n">opacityCurve</span><span class="o">.</span><span class="na">transform</span><span class="o">(</span><span class="n">animation</span><span class="o">.</span><span class="na">value</span><span class="o">),</span> <span class="nl">child:</span> <span class="n">_buildPage</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">imageName</span><span class="o">,</span> <span class="n">description</span><span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">);</span> <span class="o">},</span> <span class="o">),</span> <span class="o">);</span> <span class="o">},</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="n">timeDilation</span> <span class="o">=</span> <span class="mf">5.0</span><span class="o">;</span> <span class="c1">// 1.0 is normal animation speed.</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="kd">const</span> <span class="n">Text</span><span class="o">(</span><span class="s">'Radial Transition Demo'</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">Container</span><span class="o">(</span> <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="o">(</span><span class="mf">32.0</span><span class="o">),</span> <span class="nl">alignment:</span> <span class="n">FractionalOffset</span><span class="o">.</span><span class="na">bottomLeft</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">Row</span><span class="o">(</span> <span class="nl">mainAxisAlignment:</span> <span class="n">MainAxisAlignment</span><span class="o">.</span><span class="na">spaceBetween</span><span class="o">,</span> <span class="nl">children:</span> <span class="o">[</span> <span class="n">_buildHero</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="s">'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/radial_Hero_animation/images/chair-alpha.png'</span><span class="o">,</span> <span class="s">'Chair'</span><span class="o">),</span> <span class="n">_buildHero</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="s">'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/radial_Hero_animation/images/binoculars-alpha.png'</span><span class="o">,</span> <span class="s">'Binoculars'</span><span class="o">),</span> <span class="n">_buildHero</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="s">'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/radial_Hero_animation/images/beachball-alpha.png'</span><span class="o">,</span> <span class="s">'Beach ball'</span><span class="o">),</span> <span class="o">],</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="o">...</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <blockquote> <ul> <li>本节学习过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> <li>欢迎加入课程官方群:795410523 和讲师以及其他师兄弟们一起学习交流;</li> </ul> </blockquote> <h2 id="参考资料"><a href="https://coding.imooc.com/class/321.html">参考资料</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Sun, 21 Apr 2019 00:00:00 +0800 http://localhost:4000/2019/04/21/flutter-animation-tutorial-hero/ http://localhost:4000/2019/04/21/flutter-animation-tutorial-hero/ Flutter Android iOS 带你轻松掌握Flutter 动画开发核心技能 <p><img src="io/flutter_app/img/blog/flutter-animation-tutorial-base.png" alt="带你轻松掌握Flutter 动画开发核心技能(上)" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将向大家分享Flutter动画开发的一些<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">核心技能</code></a>,以及一些<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">技巧和经验</code></a>。</p> <blockquote> <ul> <li>在大家Flutter开发环境过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> </ul> </blockquote> <p>精心设计的动画会让用户界面感觉更直观、流畅,能改善用户体验。 Flutter的动画支持可以轻松实现各种动画类型。许多widget,特别是<a href="https://coding.imooc.com/class/321.html">Material Design widgets</a>, 都带有在其设计规范中定义的标准动画效果,但也可以自定义这些效果,在开始学习之前呢,我们先来快速过一下本篇文章的目录:</p> <h2 id="目录">目录</h2> <ul> <li>在Flutter中有哪些类型的<a href="">动画</a>?</li> <li>如何使用动画库中的基础类给widget<a href="">添加动画</a>?</li> <li>如何为动画<a href="">添加监听器</a>?</li> <li>该什么时候使用<a href="">AnimatedWidget</a>与<a href="">AnimatedBuilder</a>?</li> </ul> <h2 id="在flutter中有哪些类型的动画">在Flutter中有哪些类型的动画?</h2> <p>在Flutter中动画分为两类:基于<a href="">tween</a>或<a href="">基于物理</a>的。</p> <blockquote> <p>推荐大家查阅我们上面课程中所讲到的Flutter gallery中的示例代码来学习动画。</p> </blockquote> <ul> <li><a href="">补间(Tween)动画</a>:在补间动画中,定义了开始点和结束点、时间线以及定义转换时间和速度的曲线。然后由框架计算如何从开始点过渡到结束点。</li> <li><a href="">基于物理的动画</a>:在基于物理的动画中,运动被模拟为与真实世界的行为相似。例如,当你掷球时,它在何处落地,取决于抛球速度有多快、球有多重、距离地面有多远。 类似地,将连接在弹簧上的球落下(并弹起)与连接到绳子上的球放下的方式也是不同。</li> </ul> <h2 id="如何使用动画库中的基础类给widget添加动画">如何使用动画库中的基础类给widget添加动画?</h2> <p>在为widget添加动画之前,先让我们认识下动画的几个朋友:</p> <ul> <li><a href="">Animation</a>:是Flutter动画库中的一个核心类,它生成指导动画的值;</li> <li><a href="">CurvedAnimation</a>:Animation<double>的一个子类,将过程抽象为一个非线性曲线;</double></li> <li><a href="">AnimationController</a>:Animation<double>的一个子类,用来管理Animation;</double></li> <li><a href="">Tween</a>:在正在执行动画的对象所使用的数据范围之间生成值。例如,Tween可生成从红到蓝之间的色值,或者从0到255;</li> </ul> <h3 id="animation">Animation</h3> <p>在Flutter中,<a href="">Animation</a>对象本身和UI渲染没有任何关系。<a href="">Animation</a>是一个抽象类,它拥有其当前值和状态(完成或停止)。其中一个比较常用的<a href="">Animation</a>类是[Animation<double>]()。</double></p> <p><strong>Flutter中的Animation对象是一个在一段时间内依次生成一个区间之间值的类</strong>。<a href="">Animation</a>对象的输出可以是线性的、曲线的、一个步进函数或者任何其他可以设计的映射。 根据<a href="">Animation</a>对象的控制方式,动画可以反向运行,甚至可以在中间切换方向。</p> <ul> <li><a href="">Animation</a>还可以生成除double之外的其他类型值,如:[Animation<Color>]() 或 [Animation<Size>]();</Size></Color></li> <li><a href="">Animation</a>对象有状态。可以通过访问其value属性获取动画的当前值;</li> <li><a href="">Animation</a>对象本身和UI渲染没有任何关系;</li> </ul> <h3 id="curvedanimation">CurvedAnimation</h3> <p><a href="">CurvedAnimation</a>将动画过程定义为一个非线性曲线。</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>final CurvedAnimation curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn); </code></pre></div></div> <blockquote> <p>注: <a href="">Curves</a> 类定义了许多常用的曲线,也可以创建自己的,例如:</p> </blockquote> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class ShakeCurve extends Curve { @override double transform(double t) { return math.sin(t * math.PI * 2); } } </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h3 id="animationcontroller">AnimationController</h3> <p><a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">AnimationController</code></a>是一个特殊的<a href=""><code class="highlighter-rouge">Animation</code></a>对象,在屏幕刷新的每一帧,就会生成一个新的值。默认情况下,<a href=""><code class="highlighter-rouge">AnimationController</code></a>在给定的时间段内会线性的生成从0.0到1.0的数字。 例如,下面代码创建一个Animation对象:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>final AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 2000), vsync: this); </code></pre></div></div> <p>AnimationController派生自<code class="highlighter-rouge">Animation&lt;double&gt;</code>,因此可以在需要Animation对象的任何地方使用。 但是,<code class="highlighter-rouge">AnimationController</code>具有控制动画的其他方法:</p> <ul> <li><a href=""><code class="highlighter-rouge">forward()</code></a>:启动动画;</li> <li><a href=""><code class="highlighter-rouge">reverse({double from})</code></a>:倒放动画;</li> <li><a href=""><code class="highlighter-rouge">reset()</code></a>:重置动画,将其设置到动画的开始位置;</li> <li><a href=""><code class="highlighter-rouge">stop({ bool canceled = true })</code></a>:停止动画;</li> </ul> <p>当创建一个<a href="">AnimationController</a>时,需要传递一个vsync参数,存在vsync时会防止屏幕外动画消耗不必要的资源,可以将<a href="">stateful</a>对象作为vsync的值。</p> <blockquote> <p>注意: 在某些情况下,值(position,值动画的当前值)可能会超出<a href="">AnimationController</a>的0.0-1.0的范围。例如,fling()函数允许您提供速度(velocity)、力量(force)、position(通过Force对象)。位置(position)可以是任何东西,因此可以在0.0到1.0范围之外。 <a href="">CurvedAnimation</a>生成的值也可以超出0.0到1.0的范围。根据选择的曲线,<a href="">CurvedAnimation</a>的输出可以具有比输入更大的范围。例如,<a href="">Curves.elasticIn</a>等弹性曲线会生成大于或小于默认范围的值。</p> </blockquote> <h3 id="tween">Tween</h3> <p>默认情况下,<a href="">AnimationController</a>对象的范围从0.0到1.0。如果您需要不同的范围或不同的数据类型,则可以使用<a href="">Tween</a>来配置动画以生成不同的范围或数据类型的值。例如,以下示例,Tween生成从-200.0到0.0的值:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>final Tween doubleTween = new Tween&lt;double&gt;(begin: -200.0, end: 0.0); </code></pre></div></div> <p><a href="">Tween</a>是一个无状态(stateless)对象,需要begin和end值。<a href="">Tween</a>的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为0.0到1.0,但这不是必须的。</p> <p>Tween继承自<a href=""><code class="highlighter-rouge">Animatable&lt;T&gt;</code></a>,而不是继承自<a href=""><code class="highlighter-rouge">Animation&lt;T&gt;</code></a>。<a href="">Animatable</a>与<a href="">Animation</a>相似,不是必须输出double值。例如,<a href="">ColorTween</a>指定两种颜色之间的过渡。</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>final Tween colorTween = new ColorTween(begin: Colors.transparent, end: Colors.black54); </code></pre></div></div> <p>Tween对象不存储任何状态。相反,它提供了<a href=""><code class="highlighter-rouge">evaluate(Animation&lt;double&gt; animation)</code></a>方法将映射函数应用于动画当前值。 <a href="">Animation</a>对象的当前值可以通过<a href=""><code class="highlighter-rouge">value()</code></a>方法取到。<a href="">evaluate</a>函数还执行一些其它处理,例如分别确保在动画值为0.0和1.0时返回开始和结束状态。</p> <h4 id="tweenanimate">Tween.animate</h4> <p>要使用<a href="">Tween</a>对象,可调用它的<a href=""><code class="highlighter-rouge">animate()</code></a>方法,传入一个控制器对象。例如,以下代码在500毫秒内生成从0到255的整数值。</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>final AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 500), vsync: this); Animation&lt;int&gt; alpha = new IntTween(begin: 0, end: 255).animate(controller); </code></pre></div></div> <p>注意<a href=""><code class="highlighter-rouge">animate()</code></a>返回的是一个<a href="">Animation</a>,而不是一个<a href="">Animatable</a>。</p> <p>以下示例构建了一个控制器、一条曲线和一个<a href="">Tween</a>:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>final AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 500), vsync: this); final Animation curve = new CurvedAnimation(parent: controller, curve: Curves.easeOut); Animation&lt;int&gt; alpha = new IntTween(begin: 0, end: 255).animate(curve); </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h3 id="为widget添加动画">为widget添加动画</h3> <p>在下面的实例中我们为一个logo添加了一个从小放大的动画: <img src="io/flutter_app/img/blog/zoom.gif" alt="zoom.gif" /></p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class _LogoAppState extends State&lt;LogoApp&gt; with SingleTickerProviderStateMixin { Animation&lt;double&gt; animation; AnimationController controller; AnimationStatus animationState; double animationValue; @override void initState() { super.initState(); controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); // #docregion addListener animation = Tween&lt;double&gt;(begin: 0, end: 300).animate(controller) ..addListener(() { // #enddocregion addListener setState(() { animationValue = animation.value; }); // #docregion addListener }) ..addStatusListener((AnimationStatus state) { setState(() { animationState = state; }); }); // #enddocregion addListener } @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(top: 50), child: Column( children: &lt;Widget&gt;[ GestureDetector( onTap: () { controller.reset(); controller.forward(); }, child: Text('Start', textDirection: TextDirection.ltr), ), Text('State:' + animationState.toString(), textDirection: TextDirection.ltr), Text('Value:' + animationValue.toString(), textDirection: TextDirection.ltr), Container( height: animation.value, width: animation.value, child: FlutterLogo(), ), ], ), ); } @override void dispose() { controller.dispose(); super.dispose(); } } </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <blockquote> <p>注意,在上述代码中要实现这个动画的关键一步是在<a href=""><code class="highlighter-rouge">addListener()</code></a>的回调中添加<a href=""><code class="highlighter-rouge">setState</code></a>的调用这样才能触发页面重新渲染,动画才能有效,另外也可以通过<a href="#AnimatedWidget">AnimatedWidget</a>来实现,在下文中会讲到。</p> </blockquote> <h2 id="如何为动画添加监听器">如何为动画添加监听器?</h2> <p>有时我们需要知道动画执行的进度和状态,在Flutter中我们可以通过Animation的<a href=""><code class="highlighter-rouge">addListener</code></a>与<a href=""><code class="highlighter-rouge">addStatusListener</code></a>方法为动画添加监听器:</p> <ul> <li><a href=""><code class="highlighter-rouge">addListener</code></a>:动画的值发生变化时被调用;</li> <li><a href=""><code class="highlighter-rouge">addStatusListener</code></a>:动画状态发生变化时被调用;</li> </ul> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">controller</span> <span class="o">=</span> <span class="n">AnimationController</span><span class="o">(</span><span class="nl">duration:</span> <span class="kd">const</span> <span class="n">Duration</span><span class="o">(</span><span class="nl">seconds:</span> <span class="mi">2</span><span class="o">),</span> <span class="nl">vsync:</span> <span class="k">this</span><span class="o">);</span> <span class="n">animation</span> <span class="o">=</span> <span class="n">Tween</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;(</span><span class="nl">begin:</span> <span class="mi">0</span><span class="o">,</span> <span class="nl">end:</span> <span class="mi">300</span><span class="o">).</span><span class="na">animate</span><span class="o">(</span><span class="n">controller</span><span class="o">)</span> <span class="c1">// #enddocregion print-state</span> <span class="o">..</span><span class="na">addStatusListener</span><span class="o">((</span><span class="n">status</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">AnimationStatus</span><span class="o">.</span><span class="na">completed</span><span class="o">)</span> <span class="o">{</span> <span class="n">controller</span><span class="o">.</span><span class="na">reverse</span><span class="o">();</span> <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">AnimationStatus</span><span class="o">.</span><span class="na">dismissed</span><span class="o">)</span> <span class="o">{</span> <span class="n">controller</span><span class="o">.</span><span class="na">forward</span><span class="o">();</span> <span class="o">}</span> <span class="o">})</span> <span class="c1">// #docregion print-state</span> <span class="o">..</span><span class="na">addStatusListener</span><span class="o">((</span><span class="n">state</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">print</span><span class="o">(</span><span class="s">'</span><span class="si">$state</span><span class="s">'</span><span class="o">));</span> <span class="o">..</span><span class="na">addListener</span><span class="o">(()</span> <span class="o">{</span> <span class="c1">// #enddocregion addListener</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="c1">// The state that has changed here is the animation object’s value.</span> <span class="o">});</span> <span class="c1">// #docregion addListener</span> <span class="o">});</span> <span class="n">controller</span><span class="o">.</span><span class="na">forward</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>可对照学习<a href="">为widget添加动画</a>的例子;</p> </blockquote> <h2 id="用animatedwidget与animatedbuilder简化和重构我们对动画的使用">用AnimatedWidget与AnimatedBuilder简化和重构我们对动画的使用</h2> <h3 id="什么是animatedwidget"><a href="">什么是AnimatedWidget?</a></h3> <p>我们可以将<a href=""><code class="highlighter-rouge">AnimatedWidget</code></a>理解为<a href="">Animation</a>的助手,使用它可以简化我们对动画的使用,在<a href="#为widget添加动画">为widget添加动画</a>的学习中我们不难发现,在不使用<a href=""><code class="highlighter-rouge">AnimatedWidget</code></a>的情况下需要手动调用动画的<a href=""><code class="highlighter-rouge">addListener()</code></a>并在回调中添加<a href=""><code class="highlighter-rouge">setState</code></a>才能看到动画效果,<a href=""><code class="highlighter-rouge">AnimatedWidget</code></a>将为我们简化这一操作。</p> <p>在下面的重构示例中,LogoApp现在继承自<a href=""><code class="highlighter-rouge">AnimatedWidget</code></a>而不是<a href=""><code class="highlighter-rouge">StatefulWidget</code></a>。<a href=""><code class="highlighter-rouge">AnimatedWidget</code></a>在绘制时使用动画的当前值。LogoApp仍然管理着<a href=""><code class="highlighter-rouge">AnimationController</code></a>和<a href=""><code class="highlighter-rouge">Tween</code></a>。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">AnimatedLogo</span> <span class="kd">extends</span> <span class="n">AnimatedWidget</span> <span class="o">{</span> <span class="n">AnimatedLogo</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">animation</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">,</span> <span class="nl">listenable:</span> <span class="n">animation</span><span class="o">);</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="kd">final</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">animation</span> <span class="o">=</span> <span class="n">listenable</span><span class="o">;</span> <span class="k">return</span> <span class="k">new</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="k">new</span> <span class="n">Container</span><span class="o">(</span> <span class="nl">margin:</span> <span class="k">new</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">symmetric</span><span class="o">(</span><span class="nl">vertical:</span> <span class="mf">10.0</span><span class="o">),</span> <span class="nl">height:</span> <span class="n">animation</span><span class="o">.</span><span class="na">value</span><span class="o">,</span> <span class="nl">width:</span> <span class="n">animation</span><span class="o">.</span><span class="na">value</span><span class="o">,</span> <span class="nl">child:</span> <span class="k">new</span> <span class="n">FlutterLogo</span><span class="o">(),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">LogoApp</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">_LogoAppState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="k">new</span> <span class="n">_LogoAppState</span><span class="o">();</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">_LogoAppState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">LogoApp</span><span class="o">&gt;</span> <span class="k">with</span> <span class="n">SingleTickerProviderStateMixin</span> <span class="o">{</span> <span class="n">AnimationController</span> <span class="n">controller</span><span class="o">;</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">animation</span><span class="o">;</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">controller</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AnimationController</span><span class="o">(</span> <span class="nl">duration:</span> <span class="kd">const</span> <span class="n">Duration</span><span class="o">(</span><span class="nl">milliseconds:</span> <span class="mi">2000</span><span class="o">),</span> <span class="nl">vsync:</span> <span class="k">this</span><span class="o">);</span> <span class="n">animation</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Tween</span><span class="o">(</span><span class="nl">begin:</span> <span class="mf">0.0</span><span class="o">,</span> <span class="nl">end:</span> <span class="mf">300.0</span><span class="o">).</span><span class="na">animate</span><span class="o">(</span><span class="n">controller</span><span class="o">);</span> <span class="n">controller</span><span class="o">.</span><span class="na">forward</span><span class="o">();</span> <span class="o">}</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="k">new</span> <span class="n">AnimatedLogo</span><span class="o">(</span><span class="nl">animation:</span> <span class="n">animation</span><span class="o">);</span> <span class="o">}</span> <span class="n">dispose</span><span class="o">()</span> <span class="o">{</span> <span class="n">controller</span><span class="o">.</span><span class="na">dispose</span><span class="o">();</span> <span class="k">super</span><span class="o">.</span><span class="na">dispose</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="o">...</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h3 id="什么是animatedbuilder">什么是AnimatedBuilder?</h3> <p><a href=""><code class="highlighter-rouge">AnimatedBuilder</code></a>是用于构建动画的通用widget,<a href="">AnimatedBuilder</a>对于希望将动画作为更大构建函数的一部分包含在内的更复杂的widget时非常有用,其实你可以这样理解:<a href="">AnimatedBuilder</a>是拆分动画的一个工具类,借助它我们可以将动画和widget进行分离:</p> <p>在上面的实例中我们的代码存在的一个问题: 更改动画需要更改显示logo的widget。更好的解决方案是将职责分离:</p> <ul> <li>显示logo</li> <li>定义Animation对象</li> <li>渲染过渡效果</li> </ul> <p>接下来我们就借助<a href=""><code class="highlighter-rouge">AnimatedBuilder]()</code>类来完成此分离。[<code class="highlighter-rouge">AnimatedBuilder</code>]()是渲染树中的一个独立的类, 与<code class="highlighter-rouge">[AnimatedWidget</code></a>类似,<a href=""><code class="highlighter-rouge">AnimatedBuilder</code></a>自动监听来自Animation对象的通知,不需要手动调用<a href=""><code class="highlighter-rouge">addListener()</code></a>。</p> <p>我们根据下图的 widget 树来创建我们的代码:</p> <p><img src="io/flutter_app/img/blog/AnimatedBuilder-WidgetTree.png" alt="AnimatedBuilder-WidgetTree" /></p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="c1">// #docregion LogoWidget</span> <span class="kd">class</span> <span class="nc">LogoWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="c1">// Leave out the height and width so it fills the animating parent</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">Container</span><span class="o">(</span> <span class="nl">margin:</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">symmetric</span><span class="o">(</span><span class="nl">vertical:</span> <span class="mi">10</span><span class="o">),</span> <span class="nl">child:</span> <span class="n">FlutterLogo</span><span class="o">(),</span> <span class="o">);</span> <span class="o">}</span> <span class="c1">// #enddocregion LogoWidget</span> <span class="c1">// #docregion GrowTransition</span> <span class="kd">class</span> <span class="nc">GrowTransition</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="n">GrowTransition</span><span class="o">({</span><span class="k">this</span><span class="o">.</span><span class="na">child</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">animation</span><span class="o">});</span> <span class="kd">final</span> <span class="n">Widget</span> <span class="n">child</span><span class="o">;</span> <span class="kd">final</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">animation</span><span class="o">;</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">AnimatedBuilder</span><span class="o">(</span> <span class="nl">animation:</span> <span class="n">animation</span><span class="o">,</span> <span class="nl">builder:</span> <span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">child</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">Container</span><span class="o">(</span> <span class="nl">height:</span> <span class="n">animation</span><span class="o">.</span><span class="na">value</span><span class="o">,</span> <span class="nl">width:</span> <span class="n">animation</span><span class="o">.</span><span class="na">value</span><span class="o">,</span> <span class="nl">child:</span> <span class="n">child</span><span class="o">,</span> <span class="o">),</span> <span class="nl">child:</span> <span class="n">child</span><span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="c1">// #enddocregion GrowTransition</span> <span class="kd">class</span> <span class="nc">LogoApp</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">_LogoAppState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_LogoAppState</span><span class="o">();</span> <span class="o">}</span> <span class="c1">// #docregion print-state</span> <span class="kd">class</span> <span class="nc">_LogoAppState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">LogoApp</span><span class="o">&gt;</span> <span class="k">with</span> <span class="n">SingleTickerProviderStateMixin</span> <span class="o">{</span> <span class="n">Animation</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">animation</span><span class="o">;</span> <span class="n">AnimationController</span> <span class="n">controller</span><span class="o">;</span> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">controller</span> <span class="o">=</span> <span class="n">AnimationController</span><span class="o">(</span><span class="nl">duration:</span> <span class="kd">const</span> <span class="n">Duration</span><span class="o">(</span><span class="nl">seconds:</span> <span class="mi">2</span><span class="o">),</span> <span class="nl">vsync:</span> <span class="k">this</span><span class="o">);</span> <span class="n">animation</span> <span class="o">=</span> <span class="n">Tween</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;(</span><span class="nl">begin:</span> <span class="mi">0</span><span class="o">,</span> <span class="nl">end:</span> <span class="mi">300</span><span class="o">).</span><span class="na">animate</span><span class="o">(</span><span class="n">controller</span><span class="o">);</span> <span class="n">controller</span><span class="o">.</span><span class="na">forward</span><span class="o">();</span> <span class="o">}</span> <span class="c1">// #enddocregion print-state</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">GrowTransition</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">LogoWidget</span><span class="o">(),</span> <span class="nl">animation:</span> <span class="n">animation</span><span class="o">,</span> <span class="o">);</span> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">dispose</span><span class="o">()</span> <span class="o">{</span> <span class="n">controller</span><span class="o">.</span><span class="na">dispose</span><span class="o">();</span> <span class="k">super</span><span class="o">.</span><span class="na">dispose</span><span class="o">();</span> <span class="o">}</span> <span class="c1">// #docregion print-state</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <blockquote> <ul> <li>本节学习过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> <li>欢迎加入课程官方群:795410523 和讲师以及其他师兄弟们一起学习交流;</li> </ul> </blockquote> <h2 id="参考资料"><a href="https://coding.imooc.com/class/321.html">参考资料</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Sat, 20 Apr 2019 00:00:00 +0800 http://localhost:4000/2019/04/20/flutter-animation-tutorial-base/ http://localhost:4000/2019/04/20/flutter-animation-tutorial-base/ Flutter Android iOS 带你快速掌握Flutter图片开发核心技能 <p><img src="io/flutter_app/img/blog/flutter-image-widget.png" alt="带你快速掌握Flutter图片开发核心技能" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将带着大家一起学习在<a href="https://coding.imooc.com/class/321.html">Flutter中图片开发</a>以及应用场景中的必备技能以及一些<a href="https://coding.imooc.com/class/321.html">经验技巧</a>。</p> <blockquote> <ul> <li>本文学习过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> <li>欢迎加入课程官方群:795410523 和讲师以及其他师兄弟们一起学习交流;</li> </ul> </blockquote> <h2 id="目录">目录</h2> <ul> <li>什么是<a href="https://coding.imooc.com/learn/qa/321.html">Image widget</a>?</li> <li>如何加载网络图片?</li> <li>如何加载静态图片?</li> <li>如何加载本地图片?</li> <li>如何设置<a href="https://coding.imooc.com/learn/qa/321.html">Placeholder</a>?</li> <li>如何配置图片缓存?</li> <li>如何加载<a href="https://coding.imooc.com/learn/qa/321.html">Icon</a>?</li> </ul> <h2 id="什么是image-widget">什么是Image widget?</h2> <p>Flutter中一个用来展示图片的widget。</p> <h3 id="image支持如下几种类型的构造函数">Image支持如下几种类型的构造函数:</h3> <ul> <li><a href="https://docs.flutter.io/flutter/widgets/Image/Image.html">new Image</a> - 用于从ImageProvider获取图像;</li> <li><a href="https://docs.flutter.io/flutter/widgets/Image/Image.asset.html">new Image.asset</a> - 使用key 从AssetBundle获得的图像;</li> <li><a href="https://docs.flutter.io/flutter/widgets/Image/Image.network.html">new Image.network</a> - 从网络URL中获取图片;</li> <li><a href="https://docs.flutter.io/flutter/widgets/Image/Image.file.html">new Image.file</a> - 从本地文件中获取图片;</li> <li><a href="https://docs.flutter.io/flutter/widgets/Image/Image.memory.html">new Image.memory</a> - 用于从Uint8List获取图像;</li> </ul> <blockquote> <p>在加载项目中的图片资源时,为了让Image能够根据像素密度自动适配不同分辨率的图片,请使用<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">AssetImage</code></a>指定图像,并确保在widget树中的“Image” widget上方存在<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">MaterialApp</code></a>,<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">WidgetsApp</code></a>或<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">MediaQuery</code></a>窗口widget。</p> </blockquote> <h3 id="image支持的图片格式">Image支持的图片格式</h3> <p>Image 支持以下类型的图片:JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, 和 WBMP。</p> <h2 id="如何加载网络图片">如何加载网络图片?</h2> <p>要加载网络图片,我们需要使用<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">Image.network</code></a>构造方法:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Image</span><span class="o">.</span><span class="na">network</span><span class="o">(</span> <span class="s">'img/avatar.png'</span><span class="o">,</span> <span class="o">)</span> </code></pre></div></div> <blockquote> <p>demo:</p> </blockquote> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="kd">class</span> <span class="nc">MyApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="k">new</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="s">'Flutter bottomNavigationBar'</span><span class="o">,</span> <span class="nl">theme:</span> <span class="k">new</span> <span class="n">ThemeData</span><span class="o">.</span><span class="na">fallback</span><span class="o">(),</span> <span class="nl">home:</span> <span class="n">Image</span><span class="o">.</span><span class="na">network</span><span class="o">(</span> <span class="s">'img/avatar.png'</span><span class="o">,</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h2 id="如何加载静态图片以及处理不同分辨率的图片">如何加载静态图片,以及处理不同分辨率的图片?</h2> <p>要加载项目中的静态图片,需要一些两步:</p> <ul> <li>在 <a href="https://coding.imooc.com/learn/qa/321.html">pubspec.yaml</a> 文件中声明图片资源的路径;</li> <li>使用<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">AssetImage</code></a>访问图片;</li> </ul> <blockquote> <p>我们在<a href="https://coding.imooc.com/class/321.html">《快速上手Flutter开发》</a>的<a href="https://coding.imooc.com/class/321.html">《项目结构、资源、依赖和本地化》</a>一节中详细讲解过如何归档图片资源以及处理不同分辨率的图片,在这里就不重复了。</p> </blockquote> <p><a href="https://coding.imooc.com/learn/qa/321.html">pubspec.yaml</a>声明图片路径:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">assets:</span> <span class="o">-</span> <span class="n">images</span><span class="o">/</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span> </code></pre></div></div> <p>使用<code class="highlighter-rouge">AssetImage</code>访问图片图片:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Image</span><span class="o">(</span> <span class="nl">height:</span> <span class="mi">26</span><span class="o">,</span> <span class="nl">width:</span> <span class="mi">26</span><span class="o">,</span> <span class="nl">image:</span> <span class="n">AssetImage</span><span class="o">(</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span><span class="o">),</span> <span class="o">),</span> </code></pre></div></div> <p>除了我们使用Image的构造方法手动指定<a href="https://coding.imooc.com/learn/qa/321.html">AssetImage</a>之外,还可通过<a href="https://coding.imooc.com/learn/qa/321.html"><code class="highlighter-rouge">Image.asset</code></a>来加载静态图片:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Image</span><span class="o">.</span><span class="na">asset</span><span class="o">(</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span><span class="o">,</span> <span class="nl">width:</span> <span class="mi">26</span><span class="o">,</span> <span class="nl">height:</span> <span class="mi">26</span><span class="o">,</span> <span class="o">)</span> </code></pre></div></div> <p>两者是等效的。</p> <p>/sdcard/Download/Stack.png</p> <h2 id="如何加载本地图片">如何加载本地图片?</h2> <h3 id="加载完整路径的本地图片">加载完整路径的本地图片</h3> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:io'</span><span class="o">;</span> <span class="n">Image</span><span class="o">.</span><span class="na">file</span><span class="o">(</span><span class="n">File</span><span class="o">(</span><span class="s">'/sdcard/Download/Stack.png'</span><span class="o">)),</span> </code></pre></div></div> <h3 id="加载相对路径的本地图片">加载相对路径的本地图片</h3> <blockquote> <p>第一步:</p> </blockquote> <p>在<a href="https://coding.imooc.com/learn/qa/321.html">pubspec.yaml</a>中添加<a href="https://pub.dartlang.org/packages/path_provider">path_provider</a>插件;</p> <blockquote> <p>第二步:导入头文件</p> </blockquote> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:io'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:path_provider/path_provider.dart'</span><span class="o">;</span> <span class="c1">//Image.file(File('/sdcard/Download/Stack.png')),</span> <span class="n">FutureBuilder</span><span class="o">(</span><span class="nl">future:</span> <span class="n">_getLocalFile</span><span class="o">(</span><span class="s">"Download/Stack.png"</span><span class="o">),</span> <span class="nl">builder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">AsyncSnapshot</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span> <span class="n">snapshot</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">data</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">Image</span><span class="o">.</span><span class="na">file</span><span class="o">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">data</span><span class="o">)</span> <span class="o">:</span> <span class="n">Container</span><span class="o">();</span> <span class="o">})</span> <span class="o">)</span> <span class="c1">//获取SDCard的路径:</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span> <span class="n">_getLocalFile</span><span class="o">(</span><span class="kt">String</span> <span class="n">filename</span><span class="o">)</span> <span class="n">async</span> <span class="o">{</span> <span class="kt">String</span> <span class="n">dir</span> <span class="o">=</span> <span class="o">(</span><span class="n">await</span> <span class="n">getExternalStorageDirectory</span><span class="o">()).</span><span class="na">path</span><span class="o">;</span> <span class="n">File</span> <span class="n">f</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="s">'</span><span class="si">$dir</span><span class="s">/</span><span class="si">$filename</span><span class="s">'</span><span class="o">);</span> <span class="k">return</span> <span class="n">f</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h2 id="如何设置placeholder">如何设置<a href="https://coding.imooc.com/learn/qa/321.html">Placeholder</a>?</h2> <p>为了设置<a href="https://coding.imooc.com/learn/qa/321.html">Placeholder</a>我们需要借助<a href="https://docs.flutter.io/flutter/widgets/FadeInImage-class.html">FadeInImage</a>,它能够从内存,本地资源中加载placeholder。</p> <h3 id="从内存中加载placeholder">从内存中加载<a href="https://coding.imooc.com/learn/qa/321.html">Placeholder</a></h3> <blockquote> <p>第一步:</p> </blockquote> <p>安装<a href="https://pub.dartlang.org/packages/transparent_image">transparent_image</a>插件。</p> <blockquote> <p>第二步:</p> </blockquote> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="kd">class</span> <span class="nc">MyApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="kd">final</span> <span class="n">title</span> <span class="o">=</span> <span class="s">'Fade in images'</span><span class="o">;</span> <span class="k">return</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">title</span><span class="o">,</span> <span class="nl">home:</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">Text</span><span class="o">(</span><span class="n">title</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">Stack</span><span class="o">(</span> <span class="nl">children:</span> <span class="o">&lt;</span><span class="n">Widget</span><span class="o">&gt;[</span> <span class="n">Center</span><span class="o">(</span><span class="nl">child:</span> <span class="n">CircularProgressIndicator</span><span class="o">()),</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">FadeInImage</span><span class="o">.</span><span class="na">memoryNetwork</span><span class="o">(</span> <span class="nl">placeholder:</span> <span class="n">kTransparentImage</span><span class="o">,</span> <span class="nl">image:</span> <span class="s">'img/avatar.png'</span><span class="o">,</span> <span class="o">),</span> <span class="o">),</span> <span class="o">],</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h3 id="从本地资源中加载placeholder">从本地资源中加载<a href="https://coding.imooc.com/learn/qa/321.html">Placeholder</a></h3> <blockquote> <p>第一步</p> </blockquote> <p>配置本地资源图片:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nl">flutter:</span> <span class="nl">assets:</span> <span class="o">+</span> <span class="o">-</span> <span class="n">assets</span><span class="o">/</span><span class="n">loading</span><span class="o">.</span><span class="na">gif</span> </code></pre></div></div> <blockquote> <p>第二步</p> </blockquote> <p>加载本地资源图片作为<a href="https://coding.imooc.com/learn/qa/321.html">Placeholder</a>:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">FadeInImage</span><span class="o">.</span><span class="na">assetNetwork</span><span class="o">(</span> <span class="nl">placeholder:</span> <span class="s">'assets/loading.gif'</span><span class="o">,</span> <span class="nl">image:</span> <span class="s">'img/avatar.png'</span><span class="o">,</span> <span class="o">);</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h2 id="如何配置图片缓存">如何配置图片缓存?</h2> <p>在Flutter中我们可以借助<a href="https://pub.dartlang.org/packages/cached_network_image">cached_network_image</a>插件,来从网络上加载图片,并且将其缓存到本地,以供下次使用。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="kd">final</span> <span class="n">title</span> <span class="o">=</span> <span class="s">'Cached Images'</span><span class="o">;</span> <span class="k">return</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">title</span><span class="o">,</span> <span class="nl">home:</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">Text</span><span class="o">(</span><span class="n">title</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">CachedNetworkImage</span><span class="o">(</span> <span class="nl">placeholder:</span> <span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">url</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="k">new</span> <span class="n">CircularProgressIndicator</span><span class="o">(),</span> <span class="nl">imageUrl:</span> <span class="s">'https://picsum.photos/250?image=9'</span><span class="o">,</span> <span class="o">),</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>关于创建<code class="highlighter-rouge">图片控件开发详解</code>的更多实战技巧与最佳实践可学习<a href="https://coding.imooc.com/class/321.html">《基于Flutter1.x开发携程网App》</a>。</p> </blockquote> <h2 id="如何加载icon">如何加载Icon?</h2> <p>在Flutter中我们可以借助<a href="https://docs.flutter.io/flutter/widgets/Icon-class.html">Icon</a>来加载icon:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nf">Icon</span><span class="p">(</span><span class="k">this</span><span class="o">.</span><span class="na">icon</span><span class="c1">//IconDate, {</span> <span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">,</span><span class="c1">//大小</span> <span class="k">this</span><span class="o">.</span><span class="na">color</span><span class="o">,</span><span class="c1">//颜色</span> <span class="k">this</span><span class="o">.</span><span class="na">semanticLabel</span><span class="o">,</span><span class="c1">//标志位</span> <span class="k">this</span><span class="o">.</span><span class="na">textDirection</span><span class="o">,</span><span class="c1">//绘制方向,一般使用不到</span> <span class="o">})</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <p>从Icon的构造方法可以很清楚的看出Icon构造方法需要一个默认的类型为IconData类型的参数,我们可以构造一个自己的IconData,也可以使用Flutter提供的<a href="https://design.google.com/icons/">material_fonts</a>。</p> <h2 id="使用icons">使用Icons</h2> <p>通过如下代码我们可以使用Flutter内置的<a href="https://design.google.com/icons/">material_fonts</a>:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="kd">class</span> <span class="nc">MyApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// TODO: implement build</span> <span class="k">return</span> <span class="k">new</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="k">new</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="k">new</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Icons"</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="k">new</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="k">new</span> <span class="n">Icon</span><span class="o">(</span><span class="n">Icons</span><span class="o">.</span><span class="na">android</span><span class="o">,</span><span class="nl">size:</span> <span class="mf">100.0</span><span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">点我查看全部完整代码</a>。</p> </blockquote> <h2 id="使用自定义的icon">使用自定义的Icon</h2> <p>使用自定义的我们需要构造一个:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nf">IconData</span><span class="p">(</span> <span class="k">this</span><span class="o">.</span><span class="na">codePoint</span><span class="o">,</span><span class="c1">//必填参数,fonticon对应的16进制Unicode {</span> <span class="k">this</span><span class="o">.</span><span class="na">fontFamily</span><span class="o">,</span><span class="c1">//字体库系列</span> <span class="k">this</span><span class="o">.</span><span class="na">fontPackage</span><span class="o">,</span><span class="c1">//字体在那个包中,不填仅在自己程序包中查找</span> <span class="k">this</span><span class="o">.</span><span class="na">matchTextDirection</span><span class="o">:</span> <span class="kc">false</span><span class="o">,</span><span class="err">图标是否按照图标绘制方向显示</span> <span class="o">});</span> </code></pre></div></div> <p>首先我我们需要向使用字体一样,在pubspec.yaml中配置我们的icon:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">fonts:</span> <span class="o">-</span> <span class="nl">family:</span> <span class="n">devio</span> <span class="nl">fonts:</span> <span class="o">-</span> <span class="nl">asset:</span> <span class="n">fonts</span><span class="o">/</span><span class="n">devio</span><span class="o">.</span><span class="na">ttf</span> </code></pre></div></div> <p>接下来就可以使用了:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">child:</span> <span class="k">new</span> <span class="n">Icon</span><span class="o">(</span><span class="k">new</span> <span class="n">IconData</span><span class="o">(</span><span class="mh">0xf5566</span><span class="o">,</span><span class="nl">fontFamily:</span> <span class="s">"devio"</span><span class="o">),</span><span class="nl">size:</span> <span class="mf">100.0</span><span class="o">,</span><span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">blueAccent</span><span class="o">,)</span> </code></pre></div></div> <blockquote> <ul> <li>本节学习过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> <li>欢迎加入课程官方群:795410523 和讲师以及其他师兄弟们一起学习交流;</li> </ul> </blockquote> <h2 id="参考资料"><a href="https://coding.imooc.com/class/321.html">参考资料</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Thu, 11 Apr 2019 00:00:00 +0800 http://localhost:4000/2019/04/11/flutter-image-widget/ http://localhost:4000/2019/04/11/flutter-image-widget/ Flutter Android iOS 两分钟带你快速搭建Flutter开发环境(Windows) <p><img src="io/flutter_app/img/blog/development-environment-windows.png" alt="两分钟带你快速搭建Flutter开发环境(Windows)" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将带着大家一起在Windows平台上<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">快速搭建Flutter的开发环境</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">同时会将搭建Flutter开发环境中的一些技巧和经验</code></a>分享给大家。</p> <blockquote> <ul> <li>在大家Flutter开发环境过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> </ul> </blockquote> <h2 id="目录"><a href="https://coding.imooc.com/learn/qa/321.html">目录</a></h2> <hr /> <ul> <li>系统要求</li> <li>设置FLutter镜像(非必须)</li> <li>获取Flutter SDK</li> <li>Android开发环境设置</li> <li>安装Flutter插件</li> </ul> <h2 id="系统要求"><a href="https://coding.imooc.com/learn/qa/321.html">系统要求</a></h2> <p>在Windows上要安装并运行Flutter要满足以下最低要求:</p> <ul> <li>操作系统: Windows 7 SP1或更新版本</li> <li>磁盘空间: 400 MB (Android Studio的磁盘空间).</li> <li>工具: Flutter 依赖下面这些命令行工具: <ul> <li><a href="https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell">Windows PowerShell 5.0</a> Windows 10已经预装了这个工具;</li> <li><a href="https://git-scm.com/download/win">Git for Windows 2.x</a>确保Windows电脑下载并安装了Git工具;</li> </ul> </li> </ul> <h2 id="设置flutter镜像非必须"><a href="https://coding.imooc.com/learn/qa/321.html">设置FLutter镜像(非必须)</a></h2> <p>由于在国内访问Flutter可能会受到限制,Flutter官方为中国开发者搭建了临时镜像,大家可以将如下环境变量加入到用户环境变量中:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PUB_HOSTED_URL</span><span class="o">=</span>https://pub.flutter-io.cn <span class="nv">FLUTTER_STORAGE_BASE_URL</span><span class="o">=</span>https://storage.flutter-io.cn </code></pre></div></div> <p>注意:此镜像为临时镜像,并不能保证一直可用,大家可以从 <a href="https://flutter.dev/community/china">Using Flutter in China</a> 上获得有关镜像服务器的最新动态。</p> <h2 id="获取flutter-sdk"><a href="https://coding.imooc.com/learn/qa/321.html">获取Flutter SDK</a></h2> <p><strong>1.点<a href="https://flutter.dev/docs/development/tools/sdk/archive">Flutter官网</a>下载其最新可用的安装包。</strong></p> <p><strong>2.解压安装包到你想安装的目录,如:<code class="highlighter-rouge">C:\flutter</code>;</strong></p> <blockquote> <p>注意,不要将flutter安装到需要一些高权限的路径如<code class="highlighter-rouge">C:\Program Files\</code>等。</p> </blockquote> <p><strong>3.在Flutter安装目录的flutter文件下找到<code class="highlighter-rouge">flutter_console.bat</code>,双击运行并启动flutter命令行;</strong></p> <p>接下来,你就可以在Flutter命令行运行flutter命令了。</p> <h3 id="设置环境变量"><a href="https://coding.imooc.com/learn/qa/321.html">设置环境变量</a></h3> <p>要在终端运行 flutter 命令, 你需要添加以下环境变量到系统<code class="highlighter-rouge">PATH</code>:</p> <ul> <li>在Windows的Start 的搜索条中搜索<code class="highlighter-rouge">env</code>,选择<code class="highlighter-rouge">编辑帐户的环境变量</code>;</li> <li>在“用户变量”下检查是否有名为“Path”的条目: <ul> <li>如果该条目存在, 追加 <code class="highlighter-rouge">flutter\bin</code>的全路径,使用 ; 作为分隔符.</li> <li>如果条目不存在, 创建一个新用户变量 Path ,然后将 <code class="highlighter-rouge">flutter\bin</code>的全路径作为它的值.</li> </ul> </li> </ul> <p>在“用户变量”下检查是否有名为”PUB_HOSTED_URL”和”FLUTTER_STORAGE_BASE_URL”的条目,如果没有,也添加它们。</p> <ul> <li>重启Windows以应用此更改;</li> </ul> <p><img src="io/flutter_app/img/blog/Flutter-SDK-Windows-path.png" alt="" /></p> <h3 id="运行-flutter-doctor"><a href="https://coding.imooc.com/learn/qa/321.html">运行 flutter doctor</a></h3> <p>上面path配置完成之后,打开一个新的命令提示符或<code class="highlighter-rouge">PowerShell</code>窗口并运行以下命令以查看是否需要安装任何依赖项来完成安装:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter doctor </code></pre></div></div> <p>该命令检查你的环境并在终端窗口中显示报告。Dart SDK已经在捆绑在Flutter里了,没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务(以粗体显示):</p> <p>例如:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[-] Android toolchain - develop for Android devices • Android SDK at /Users/obiwan/Library/Android/sdk ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ • Try re-installing or updating your Android SDK, visit https://flutter.dev/setup/#android-setup for detailed instructions. </code></pre></div></div> <p>一般的错误会是Android Studio版本太低、或者没有<code class="highlighter-rouge">ANDROID_HOME</code>环境变量等</p> <blockquote> <p>第一次运行一个flutter命令(如flutter doctor)时,它会下载它自己的依赖项并自行编译。以后再运行就会快得多。</p> </blockquote> <h2 id="android开发环境设置"><a href="https://coding.imooc.com/learn/qa/321.html">Android开发环境设置</a></h2> <h3 id="安装android-studio"><a href="https://coding.imooc.com/learn/qa/321.html">安装Android Studio</a></h3> <p><strong>1.下载并安装 <a href="https://developer.android.com/studio">Android Studio</a></strong></p> <ul> <li><a href="https://developer.android.com/studio">https://developer.android.com/studio</a></li> <li><a href="https://developer.android.google.cn/studio">https://developer.android.google.cn/studio</a></li> </ul> <blockquote> <p>因为Android网站设在国外,如果你的网络无法访问第一个地址,可以选择使用Google为中国开发者提供的中国网址进行访问。</p> </blockquote> <p>另外,关于Android Studio的安装和配置,Android官方有比较详细的说明文档<a href="https://developer.android.google.cn/studio/intro">https://developer.android.google.cn/studio/intro</a>,大家可以根据需要进行查阅;</p> <blockquote> <p>大家在安装过程中遇到问题无法解决的,可以在我们课程的<a href="https://coding.imooc.com/learn/qa/321.html">问答区提问</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>;</p> </blockquote> <p><strong>2.启动Android Studio,然后执行“Android Studio安装向导”。这将安装最新的Android SDK,Android SDK平台工具和Android SDK构建工具</strong></p> <h3 id="flutter插件安装"><a href="https://coding.imooc.com/learn/qa/321.html">Flutter插件安装</a></h3> <ul> <li>打开Android Studio</li> <li>打开Preferences &gt; Plugins (macOS), File &gt; Settings &gt; Plugins (Windows &amp; Linux)</li> <li>选择 Browse repositories, 搜索 Flutter plugin</li> <li>然后点击安装,然后安装Dart插件</li> <li>完成之后选择重启Android Studio</li> </ul> <h3 id="如何在android模拟器上运行flutter"><a href="https://coding.imooc.com/learn/qa/321.html">如何在Android模拟器上运行Flutter?</a></h3> <p>要准备在Android模拟器上运行并测试您的Flutter应用,需要按照以下步骤操作:</p> <ul> <li>在你的机器上启用 <a href="https://developer.android.com/studio/run/emulator-acceleration.html#vm-mac">VM acceleration</a>;</li> <li>启动 Android Studio&gt;Tools&gt;Android&gt;AVD Manager 并选择 <code class="highlighter-rouge">Create Virtual Device</code>;</li> <li>选择一个设备并选择 Next;</li> <li>为要模拟的Android版本选择一个或多个系统映像,然后选择 Next. 建议使用 x86 或 x86_64 的镜像;</li> <li>在 Emulated Performance下, 选择 Hardware - GLES 2.0 以启用硬件加速;</li> <li> <p>验证AVD配置是否正确,然后选择 Finish;</p> <p>如果对以上步骤还有不清楚的可以参阅Android官方的 <a href="https://developer.android.com/studio/run/managing-avds.html">Managing AVDs</a>文档。</p> <blockquote> <p>大家在安装过程中遇到问题无法解决的,可以在我们课程的<a href="https://coding.imooc.com/learn/qa/321.html">问答区提问</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>;</p> </blockquote> </li> <li>在 Android Virtual Device Manager中, 点击工具栏的 <code class="highlighter-rouge">Run</code>,模拟器启动并显示所选操作系统版本或设备的启动画面;</li> <li>通过<code class="highlighter-rouge">flutter run</code>运行启动项目;</li> </ul> <h3 id="如何在android真机运行"><a href="https://coding.imooc.com/learn/qa/321.html">如何在Android真机运行?</a></h3> <p>要准备在Android设备上运行并测试您的Flutter应用,您需要安装Android 4.1(API level 16)或更高版本的Android设备</p> <ul> <li>在你的设备上启用 <code class="highlighter-rouge">开发人员选项</code> 和 <code class="highlighter-rouge">USB调试</code> 。详细说明可在<a href="https://developer.android.com/studio/debug/dev-options.html">Android文档</a>中找到;</li> <li>使用USB将手机插入电脑,如果有授权提示需要同意授权;</li> <li>在终端中,运行<code class="highlighter-rouge"> flutter devices</code> 命令以验证Flutter是否识别你连接的Android设备;</li> <li>通过<code class="highlighter-rouge">flutter run</code>运行启动项目;</li> </ul> <p>默认情况下,Flutter使用的Android SDK版本是基于你的 <code class="highlighter-rouge">adb</code> 工具版本, 如果你想让Flutter使用不同版本的Android SDK,则必须将该 <code class="highlighter-rouge">ANDROID_HOME</code> 环境变量修改SDK的目录。</p> <h3 id="创建和运行一个简单的flutter项目"><a href="https://coding.imooc.com/learn/qa/321.html">创建和运行一个简单的Flutter项目</a></h3> <p><strong>1.通过如下命令创建一个Flutter项目</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter create my_app </code></pre></div></div> <p><strong>2.命令运行完成之后会在当前目录下创建一个名为<code class="highlighter-rouge">my_app</code>的Flutter项目,然后通过一下命令可以运行它:</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>my_app <span class="nv">$ </span>flutter run </code></pre></div></div> <h2 id="faq"><a href="https://coding.imooc.com/learn/qa/321.html">FAQ</a></h2> <h3 id="无法启动模拟器"><a href="https://coding.imooc.com/learn/qa/321.html">无法启动模拟器</a></h3> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>emulator: ERROR: x86 emulation currently requires hardware acceleration! Please ensure Windows Hypervisor Platform (WHPX) is properly installed and usable. CPU acceleration status: HAXM is not installed on this machine </code></pre></div></div> <blockquote> <p>解决方案:选择 Tools &gt; SDK Manager &gt; SDK Tools , 安装 HAXM 即可</p> </blockquote> <p><img src="io/flutter_app/img/blog/android-emulator-acceleration.png" alt="android-emulator-acceleration" /></p> <blockquote> <p>关于开发环境搭建更多实战技巧与最佳实践可学习<a href="https://coding.imooc.com/class/321.html">《基于Flutter1.x开发携程网App-开发环境搭建》</a>部分的课程。</p> </blockquote> <blockquote> <ul> <li>本节学习过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> <li>欢迎加入课程官方群:<a href="https://coding.imooc.com/class/321.html">687196170</a> 和讲师以及其他师兄弟们一起学习交流;</li> </ul> </blockquote> Sun, 07 Apr 2019 00:00:00 +0800 http://localhost:4000/2019/04/07/development-environment-windows/ http://localhost:4000/2019/04/07/development-environment-windows/ Flutter Android iOS 两分钟带你快速搭建Flutter开发环境(Mac) <p><img src="io/flutter_app/img/blog/development-environment-mac.png" alt="两分钟带你快速搭建Flutter开发环境(Mac)" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将带着大家一起在Mac平台上<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">快速搭建Flutter的开发环境</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">同时会将搭建Flutter开发环境中的一些技巧和经验</code></a>分享给大家。</p> <blockquote> <ul> <li>在大家Flutter开发环境过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> </ul> </blockquote> <h2 id="目录"><a href="https://coding.imooc.com/learn/qa/321.html">目录</a></h2> <hr /> <ul> <li>系统要求</li> <li>设置FLutter镜像(非必须)</li> <li>获取Flutter SDK</li> <li>iOS开发环境设置</li> <li>Android开发环境设置</li> <li>安装Flutter插件</li> </ul> <h2 id="系统要求"><a href="https://coding.imooc.com/learn/qa/321.html">系统要求</a></h2> <p>在Mac上要安装并运行Flutter要满足以下最低要求:</p> <ul> <li>操作系统: macOS (64-bit)</li> <li>磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间).</li> <li>工具: Flutter 依赖下面这些命令行工具:<code class="highlighter-rouge">bash curl git 2.x mkdir rm unzip which</code></li> </ul> <h2 id="设置flutter镜像非必须"><a href="https://coding.imooc.com/learn/qa/321.html">设置FLutter镜像(非必须)</a></h2> <p>由于在国内访问Flutter可能会受到限制,Flutter官方为中国开发者搭建了临时镜像,大家可以将如下环境变量加入到用户环境变量中:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//Macintosh HD⁩ ▸ ⁨Users⁩ ▸ ⁨你的用户名 ▸ ⁨.bash_profile <span class="nb">export </span><span class="nv">PUB_HOSTED_URL</span><span class="o">=</span>https://pub.flutter-io.cn <span class="nb">export </span><span class="nv">FLUTTER_STORAGE_BASE_URL</span><span class="o">=</span>https://storage.flutter-io.cn </code></pre></div></div> <p>注意:此镜像为临时镜像,并不能保证一直可用,大家可以从 <a href="https://flutter.dev/community/china">Using Flutter in China</a> 上获得有关镜像服务器的最新动态。</p> <h2 id="获取flutter-sdk"><a href="https://coding.imooc.com/learn/qa/321.html">获取Flutter SDK</a></h2> <p><strong>1.点<a href="https://flutter.dev/docs/development/tools/sdk/archive">Flutter官网</a>下载其最新可用的安装包。</strong></p> <p><strong>2.解压安装包到你想安装的目录,如:</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> ~/development <span class="nv">$ </span>unzip ~/Downloads/flutter_macos_v1.2.1-stable.zip </code></pre></div></div> <p><strong>3.添加flutter相关工具到path中:</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PATH</span><span class="s2">:</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span><span class="s2">/flutter/bin"</span> </code></pre></div></div> <p>此代码只能暂时针对当前命令行窗口设置PATH环境变量,要想永久将Flutter添加到PATH中请参考下面做法:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> ~ <span class="nv">$ </span>vim .bash_profile </code></pre></div></div> <p>然后添加:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span>/Users/jph/Documents/flutter/bin:<span class="nv">$PATH</span> </code></pre></div></div> <p>之后记得保存文件。</p> <h3 id="运行-flutter-doctor"><a href="https://coding.imooc.com/learn/qa/321.html">运行 flutter doctor</a></h3> <p>上面path配置完成之后,需要关闭终端重新打开,然后运行:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter doctor </code></pre></div></div> <p>该命令检查你的环境并在终端窗口中显示报告。Dart SDK已经在捆绑在Flutter里了,没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务(以粗体显示):</p> <p>例如:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>-] Android toolchain - develop <span class="k">for </span>Android devices • Android SDK at /Users/obiwan/Library/Android/sdk ✗ Android SDK is missing <span class="nb">command </span>line tools<span class="p">;</span> download from https://goo.gl/XxQghQ • Try re-installing or updating your Android SDK, visit https://flutter.dev/setup/#android-setup <span class="k">for </span>detailed instructions. </code></pre></div></div> <p>一般的错误会是XCode或Android Studio版本太低、或者没有<code class="highlighter-rouge">ANDROID_HOME</code>环境变量等,可参考一下环境变量的配置来检查你的环境变量:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//Macintosh HD⁩ ▸ ⁨Users⁩ ▸ ⁨你的用户名 ▸ ⁨.bash_profile <span class="c">#Android 环境变量</span> <span class="nb">export </span><span class="nv">ANDROID_HOME</span><span class="o">=</span>/Users/你的用户名/Library/Android/sdk <span class="c">#Android 模拟器路径</span> <span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="k">${</span><span class="nv">PATH</span><span class="k">}</span>:<span class="k">${</span><span class="nv">ANDROID_HOME</span><span class="k">}</span>/emulator <span class="c">#Android tools 路径</span> <span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="k">${</span><span class="nv">PATH</span><span class="k">}</span>:<span class="k">${</span><span class="nv">ANDROID_HOME</span><span class="k">}</span>/tools <span class="c">#Android 平台工具路径</span> <span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="k">${</span><span class="nv">PATH</span><span class="k">}</span>:<span class="k">${</span><span class="nv">ANDROID_HOME</span><span class="k">}</span>/platform-tools <span class="c">#Android NDK路径</span> <span class="nv">ANDROID_NDK_HOME</span><span class="o">=</span>/Users/你的用户名/Library/Android/ndk/android-ndk-r10e <span class="c">#FLutter镜像</span> <span class="nb">export </span><span class="nv">PUB_HOSTED_URL</span><span class="o">=</span>https://pub.flutter-io.cn <span class="nb">export </span><span class="nv">FLUTTER_STORAGE_BASE_URL</span><span class="o">=</span>https://storage.flutter-io.cn <span class="c">#Flutter环境变量</span> <span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span>/Users/jph/Documents/flutter/bin:<span class="nv">$PATH</span> </code></pre></div></div> <blockquote> <p>第一次运行一个flutter命令(如flutter doctor)时,它会下载它自己的依赖项并自行编译。以后再运行就会快得多。</p> </blockquote> <h2 id="ios开发环境设置"><a href="https://coding.imooc.com/learn/qa/321.html">iOS开发环境设置</a></h2> <h3 id="安装-xcode"><a href="https://coding.imooc.com/learn/qa/321.html">安装 Xcode</a></h3> <p>要用Flutter开发iOS App需要Xcode 9.0 或更高版本:</p> <p><strong>1.安装Xcode 9.0或更新版本(通过<a href="https://developer.apple.com/xcode/">链接下载</a>或<a href="https://itunes.apple.com/us/app/xcode/id497799835">苹果应用商店</a>)</strong></p> <p><strong>2.配置Xcode命令行工具以使用新安装的Xcode版本</strong> s</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>xcode-select <span class="nt">--switch</span> /Applications/Xcode.app/Contents/Developer </code></pre></div></div> <p>以上路径时对于最新版Xcode的路径。如果你需要使用不同的Xcode版本,需要指定相应路径。</p> <p><strong>3.确保Xcode许可协议是通过打开一次Xcode或通过命令sudo xcodebuild -license同意过了</strong></p> <p>接下来就可以使用Xcode,在iOS设备或模拟器上运行Flutter App了。</p> <h3 id="设置ios模拟器"><a href="https://coding.imooc.com/learn/qa/321.html">设置iOS模拟器</a></h3> <p>要准备在iOS模拟器上运行并测试您的Flutter应用,请按以下步骤操作:</p> <p><strong>1.在终端输入如下命令打开一个iOS模拟器:</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>open <span class="nt">-a</span> Simulator </code></pre></div></div> <p><strong>2.通过模拟器菜单栏的 <code class="highlighter-rouge">硬件&gt;设备</code> ,确保你打开是64位 iPhone 5s或更新的模拟器</strong></p> <p><strong>3.如果模拟器过大,可以通过模拟器的 Window&gt; Scale 菜单下设置设备比例</strong></p> <h3 id="创建和运行一个简单的flutter项目"><a href="https://coding.imooc.com/learn/qa/321.html">创建和运行一个简单的Flutter项目</a></h3> <p><strong>1.通过如下命令创建一个Flutter项目</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter create my_app </code></pre></div></div> <p><strong>2.命令运行完成之后会在当前目录下创建一个名为<code class="highlighter-rouge">my_app</code>的Flutter项目,然后通过一下命令可以运行它:</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>my_app <span class="nv">$ </span>flutter run </code></pre></div></div> <h3 id="如何将flutter安装到ios真机上"><a href="https://coding.imooc.com/learn/qa/321.html">如何将Flutter安装到iOS真机上?</a></h3> <p>要通过<code class="highlighter-rouge">lutter run</code>将Flutter应用安装到iOS真机设备,需要一些额外的工具和一个Apple帐户,还需要在Xcode中进行设置:</p> <blockquote> <p>当然,用XCode来将Flutter运行在真机上更简单,只需要点一下<code class="highlighter-rouge">run</code>按钮即可,可以根据需要进行选择这两种不同的运行方式;</p> </blockquote> <p><strong>1.安装 <a href="https://brew.sh/">Homebrew</a> (如果已经安装了brew,跳过此步骤).</strong></p> <p><strong>2.确保homebrew已更新</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew update </code></pre></div></div> <p><strong>3.打开终端并运行这些命令来安装用于将Flutter应用安装到iOS设备的工具</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew install <span class="nt">--HEAD</span> usbmuxd <span class="nv">$ </span>brew link usbmuxd <span class="nv">$ </span>brew install <span class="nt">--HEAD</span> libimobiledevice <span class="nv">$ </span>brew install ideviceinstaller ios-deploy cocoapods <span class="nv">$ </span>pod setup </code></pre></div></div> <p>如果这些命令中的任何一个失败并出现错误,可运行<code class="highlighter-rouge">brew doctor</code>并按照说明解决问题。</p> <p><strong>4.遵循Xcode签名流程来配置您的项目:</strong></p> <ul> <li>在你Flutter项目目录中通过 <code class="highlighter-rouge">open ios/Runner.xcworkspace</code> 打开默认的Xcode workspace</li> <li>在Xcode中,选择导航面板左侧中的Runner项目</li> <li>在Runner target设置页面中,确保在 常规&gt;签名&gt;团队 下选择了您的开发团队。当您选择一个团队时,Xcode会创建并下载开发证书,向您的设备注册您的帐户,并创建和下载配置文件(如果需要) <ul> <li>要开始您的第一个iOS开发项目,您可能需要使用您的Apple ID登录Xcode <img src="io/flutter_app/img/blog/xcode-account.png" alt="xcode-account" /> 任何Apple ID都支持开发和测试,但如果要将应用发布到App Store则需要一个99美刀的开发者账号。</li> </ul> </li> <li> <p>当你第一次attach真机设备进行iOS开发时,需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时,请在对话框中选择 <code class="highlighter-rouge">Trust</code>。</p> <p><img src="io/flutter_app/img/blog/trust-computer.png" alt="trust-computer" /></p> </li> </ul> <p>然后,转到iOS设备上的设置应用程序,选择 常规&gt;设备管理 并信任您的证书。</p> <ul> <li>如果Xcode中的自动签名失败,请验证项目的 General &gt; Identity &gt; Bundle Identifier 值是否唯一。</li> </ul> <p><strong>5.通过<code class="highlighter-rouge">flutter run</code>运行启动项目</strong></p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter run </code></pre></div></div> <h2 id="android开发环境设置flutter插件安装"><a href="https://coding.imooc.com/learn/qa/321.html">Android开发环境设置&amp;Flutter插件安装</a></h2> <h3 id="安装android-studio"><a href="https://coding.imooc.com/learn/qa/321.html">安装Android Studio</a></h3> <p><strong>1.下载并安装 <a href="https://developer.android.com/studio">Android Studio</a></strong></p> <ul> <li><a href="https://developer.android.com/studio">https://developer.android.com/studio</a></li> <li><a href="https://developer.android.google.cn/studio">https://developer.android.google.cn/studio</a></li> </ul> <blockquote> <p>因为Android网站设在国外,如果你的网络无法访问第一个地址,可以选择使用Google为中国开发者提供的中国网址进行访问。</p> </blockquote> <p>另外,关于Android Studio的安装和配置,Android官方有比较详细的说明文档<a href="https://developer.android.google.cn/studio/intro">https://developer.android.google.cn/studio/intro</a>,大家可以根据需要进行查阅;</p> <blockquote> <p>大家在安装过程中遇到问题无法解决的,可以在我们课程的<a href="https://coding.imooc.com/learn/qa/321.html">问答区提问</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>;</p> </blockquote> <p><strong>2.启动Android Studio,然后执行“Android Studio安装向导”。这将安装最新的Android SDK,Android SDK平台工具和Android SDK构建工具</strong></p> <h3 id="flutter插件安装"><a href="https://coding.imooc.com/learn/qa/321.html">Flutter插件安装</a></h3> <ul> <li>打开Android Studio</li> <li>打开Preferences &gt; Plugins (macOS), File &gt; Settings &gt; Plugins (Windows &amp; Linux)</li> <li>选择 Browse repositories, 搜索 Flutter plugin</li> <li>然后点击安装,然后安装Dart插件</li> <li>完成之后选择重启Android Studio</li> </ul> <h3 id="如何在android模拟器上运行flutter"><a href="https://coding.imooc.com/learn/qa/321.html">如何在Android模拟器上运行Flutter?</a></h3> <p>要准备在Android模拟器上运行并测试您的Flutter应用,需要按照以下步骤操作:</p> <ul> <li>在你的机器上启用 <a href="https://developer.android.com/studio/run/emulator-acceleration.html#vm-mac">VM acceleration</a>;</li> <li>启动 Android Studio&gt;Tools&gt;Android&gt;AVD Manager 并选择 <code class="highlighter-rouge">Create Virtual Device</code>;</li> <li>选择一个设备并选择 Next;</li> <li>为要模拟的Android版本选择一个或多个系统映像,然后选择 Next. 建议使用 x86 或 x86_64 的镜像;</li> <li>在 Emulated Performance下, 选择 Hardware - GLES 2.0 以启用硬件加速;</li> <li> <p>验证AVD配置是否正确,然后选择 Finish;</p> <p>如果对以上步骤还有不清楚的可以参阅Android官方的 <a href="https://developer.android.com/studio/run/managing-avds.html">Managing AVDs</a>文档。</p> <blockquote> <p>大家在安装过程中遇到问题无法解决的,可以在我们课程的<a href="https://coding.imooc.com/learn/qa/321.html">问答区提问</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>;</p> </blockquote> </li> <li>在 Android Virtual Device Manager中, 点击工具栏的 <code class="highlighter-rouge">Run</code>,模拟器启动并显示所选操作系统版本或设备的启动画面;</li> <li>通过<code class="highlighter-rouge">flutter run</code>运行启动项目;</li> </ul> <h3 id="如何在android真机运行"><a href="https://coding.imooc.com/learn/qa/321.html">如何在Android真机运行?</a></h3> <p>要准备在Android设备上运行并测试您的Flutter应用,您需要安装Android 4.1(API level 16)或更高版本的Android设备</p> <ul> <li>在你的设备上启用 <code class="highlighter-rouge">开发人员选项</code> 和 <code class="highlighter-rouge">USB调试</code> 。详细说明可在<a href="https://developer.android.com/studio/debug/dev-options.html">Android文档</a>中找到;</li> <li>使用USB将手机插入电脑,如果有授权提示需要同意授权;</li> <li>在终端中,运行<code class="highlighter-rouge"> flutter devices</code> 命令以验证Flutter是否识别你连接的Android设备;</li> <li>通过<code class="highlighter-rouge">flutter run</code>运行启动项目;</li> </ul> <p>默认情况下,Flutter使用的Android SDK版本是基于你的 <code class="highlighter-rouge">adb</code> 工具版本, 如果你想让Flutter使用不同版本的Android SDK,则必须将该 <code class="highlighter-rouge">ANDROID_HOME</code> 环境变量修改SDK的目录。</p> <blockquote> <p>关于开发环境搭建更多实战技巧与最佳实践可学习<a href="https://coding.imooc.com/class/321.html">《基于Flutter1.x开发携程网App-开发环境搭建》</a>部分的课程。</p> </blockquote> <blockquote> <ul> <li>本节学习过程中遇到无法解决的问题可以在<a href="https://coding.imooc.com/learn/qa/321.html">课程问答区</a>进行<a href="https://coding.imooc.com/learn/qa/321.html">提问</a>,课程老师会对你进行辅导和帮助;</li> </ul> </blockquote> <h2 id="参考"><a href="https://coding.imooc.com/class/321.html">参考</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Wed, 03 Apr 2019 00:00:00 +0800 http://localhost:4000/2019/04/03/development-environment-mac/ http://localhost:4000/2019/04/03/development-environment-mac/ Flutter Android iOS 两分钟带你快速掌握Flutter的项目结构、资源、依赖和本地化 <p><img src="io/flutter_app/img/blog/flutter-project-structure-resources-dependencies-and-localization.png" alt="flutter-project-structure-resources-dependencies-and-localization" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将带着大家一起认识<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Flutter的项目文件结构是怎样子的?</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">在哪里归档图片资源以及如何处理不同分辨率?</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">如何归档strings资源,以及如何处理不同语言?也就是我们通常说的国际化</code></a>,以及<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">如何添加Flutter项目所需的依赖?</code></a></p> <p>首先我们来学习<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Flutter的项目文件结构是怎样子的?</code></a></p> <h2 id="项目文件结构是怎样子的"><a href="https://coding.imooc.com/class/321.html">项目文件结构是怎样子的?</a></h2> <p>一个Flutter项目的通常文件结构是这样子的:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">┬</span> <span class="err">└</span> <span class="n">projectname</span> <span class="err">┬</span> <span class="err">├</span> <span class="n">android</span> <span class="o">-</span> <span class="n">Android</span><span class="err">部分的工程文件</span> <span class="err">├</span> <span class="n">build</span> <span class="o">-</span> <span class="err">项目的构建输出目录</span> <span class="err">├</span> <span class="n">ios</span> <span class="o">-</span> <span class="n">iOS</span><span class="err">部分的工程文件</span> <span class="err">├</span> <span class="n">lib</span> <span class="o">-</span> <span class="err">项目中的</span><span class="n">Dart</span><span class="err">源文件</span> <span class="err">┬</span> <span class="err">└</span> <span class="n">src</span> <span class="o">-</span> <span class="err">包含其他源文件</span> <span class="err">└</span> <span class="n">main</span><span class="o">.</span><span class="na">dart</span> <span class="o">-</span> <span class="err">自动生成的项目入口文件,类似</span><span class="n">RN</span><span class="err">的</span><span class="n">index</span><span class="o">.</span><span class="na">js</span><span class="err">文件</span> <span class="err">├</span> <span class="n">test</span> <span class="o">-</span> <span class="err">测试相关文件</span> <span class="err">└</span> <span class="n">pubspec</span><span class="o">.</span><span class="na">yaml</span> <span class="o">-</span> <span class="err">项目依赖配置文件类似于</span><span class="n">RN</span><span class="err">的</span> <span class="n">package</span><span class="o">.</span><span class="na">json</span> </code></pre></div></div> <blockquote> <p>当然大家也可以根据需要进行调整。</p> </blockquote> <h2 id="在哪里归档图片资源以及如何处理不同分辨率"><a href="https://coding.imooc.com/class/321.html">在哪里归档图片资源以及如何处理不同分辨率?</a></h2> <ul> <li>虽然Android将resources 和 assets 区别对待,但在Flutter中它们都会被作为assets处理, 所有存在于Android上<code class="highlighter-rouge">res / drawable- *</code>文件夹中的资源都放在Flutter的assets文件夹中。</li> <li>与Android类似,iOS 同样将 images 和 assets 作为不同的东西,而 Flutter 中只有 assets。被放到 iOS 中 Images.xcasset 文件夹下的资源在 Flutter 中被放到了 assets 文件夹中。</li> </ul> <p>在Flutter中<code class="highlighter-rouge">assets</code> 可以是任意类型的文件,而不仅仅是图片。例如,你可以把 json 文件放置到 my-assets 文件夹中。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">my</span><span class="o">-</span><span class="n">assets</span><span class="o">/</span><span class="n">data</span><span class="o">.</span><span class="na">json</span> </code></pre></div></div> <p>记得在 <code class="highlighter-rouge">pubspec.yaml</code> 文件中声明 <code class="highlighter-rouge">assets</code>:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">assets:</span> <span class="o">-</span> <span class="n">my</span><span class="o">-</span><span class="n">assets</span><span class="o">/</span><span class="n">data</span><span class="o">.</span><span class="na">json</span> </code></pre></div></div> <p>然后在代码中我们可以通过 <a href="https://docs.flutter.io/flutter/services/AssetBundle-class.html">AssetBundle</a> 来访问它:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:async'</span> <span class="kd">show</span> <span class="n">Future</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:flutter/services.dart'</span> <span class="kd">show</span> <span class="n">rootBundle</span><span class="o">;</span> <span class="n">Future</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;</span> <span class="n">loadAsset</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="k">return</span> <span class="n">await</span> <span class="n">rootBundle</span><span class="o">.</span><span class="na">loadString</span><span class="o">(</span><span class="s">'my-assets/data.json'</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>对于图片,Flutter 像 iOS 一样,遵循了一个简单的基于像素密度的格式。Image assets 可能是 1.0x 2.0x 3.0x 或是其他的任何倍数。这个 <a href="https://docs.flutter.io/flutter/dart-ui/Window/devicePixelRatio.html">devicePixelRatio</a> 表示了物理像素到单个逻辑像素的比率。</p> <p><strong>Android不同像素密度的图片和Flutter的像素比率的对应关系</strong></p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ldpi</span> <span class="mf">0.75</span><span class="n">x</span> <span class="n">mdpi</span> <span class="mf">1.0</span><span class="n">x</span> <span class="n">hdpi</span> <span class="mf">1.5</span><span class="n">x</span> <span class="n">xhdpi</span> <span class="mf">2.0</span><span class="n">x</span> <span class="n">xxhdpi</span> <span class="mf">3.0</span><span class="n">x</span> <span class="n">xxxhdpi</span> <span class="mf">4.0</span><span class="n">x</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>Assets 可以被放置到任何属性文件夹中——Flutter 并没有预先定义的文件结构。我们需要在 <code class="highlighter-rouge">pubspec.yaml</code> 文件中声明 assets 的位置,然后 Flutter 会把他们识别出来。</p> <p>举个例子,要把一个名为 <code class="highlighter-rouge">my_icon.png</code> 的图片放到 Flutter 工程中,你可能想要把它放到images文件夹中。把图片(1.0x)放置到 images 文件夹中,并把其它分辨率的图片放在对应的子文件夹中,并接上合适的比例系数,就像这样:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">images</span><span class="o">/</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span> <span class="c1">// Base: 1.0x image</span> <span class="n">images</span><span class="o">/</span><span class="mf">2.0</span><span class="n">x</span><span class="o">/</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span> <span class="c1">// 2.0x image</span> <span class="n">images</span><span class="o">/</span><span class="mf">3.0</span><span class="n">x</span><span class="o">/</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span> <span class="c1">// 3.0x image</span> </code></pre></div></div> <p>接下来就可以在<code class="highlighter-rouge">pubspec.yaml</code>文件中这样声明这个图片资源:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">assets:</span> <span class="o">-</span> <span class="n">images</span><span class="o">/</span><span class="n">my_icon</span><span class="o">.</span><span class="na">png</span> </code></pre></div></div> <p>现在,我们就可以借助<a href="https://docs.flutter.io/flutter/painting/AssetImage-class.html">AssetImage</a>来访问它了。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">return</span> <span class="nf">AssetImage</span><span class="p">(</span><span class="s">"images/a_dot_burr.jpeg"</span><span class="o">);</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>也可通过 <code class="highlighter-rouge">Image</code> widget 直接使用:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@override</span> <span class="n">Widget</span> <span class="nf">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Image</span><span class="o">.</span><span class="na">asset</span><span class="o">(</span><span class="s">"images/my_image.png"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>更多内容,可参考<a href="https://flutter.io/docs/development/ui/assets-and-images">在Flutter中添加assets 和 images</a>。</p> </blockquote> <h2 id="如何归档strings资源以及如何处理不同语言"><a href="https://coding.imooc.com/class/321.html">如何归档strings资源,以及如何处理不同语言?</a></h2> <p>不像 iOS 拥有一个 <code class="highlighter-rouge">Localizable.strings</code> 文件,Flutter目前没有专门的字符串资源系统。 目前,最佳做法是将strings资源作为静态字段保存在类中。 例如:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Strings</span> <span class="o">{</span> <span class="kd">static</span> <span class="kt">String</span> <span class="n">welcomeMessage</span> <span class="o">=</span> <span class="s">"Welcome To Flutter"</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>然后像如下方式来访问它:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Text</span><span class="o">(</span><span class="n">Strings</span><span class="o">.</span><span class="na">welcomeMessage</span><span class="o">)</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>默认情况下,Flutter 只支持美式英语字符串。如果你要支持其他语言,请引入 <code class="highlighter-rouge">flutter_localizations</code> 包。你可能也要引入 <a href="https://pub.dartlang.org/packages/intl">intl</a> 包来支持其他的 i10n 机制,比如日期/时间格式化。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">dependencies:</span> <span class="ss">#</span> <span class="o">...</span> <span class="nl">flutter_localizations:</span> <span class="nl">sdk:</span> <span class="n">flutter</span> <span class="nl">intl:</span> <span class="s">"^0.15.6"</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>要使用 <code class="highlighter-rouge">flutter_localizations</code> 包,还需要在 app widget 中指定 <code class="highlighter-rouge">localizationsDelegates</code> 和 <code class="highlighter-rouge">supportedLocales</code>。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'package:flutter_localizations/flutter_localizations.dart'</span><span class="o">;</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">localizationsDelegates:</span> <span class="o">[</span> <span class="c1">// Add app-specific localization delegate[s] here</span> <span class="n">GlobalMaterialLocalizations</span><span class="o">.</span><span class="na">delegate</span><span class="o">,</span> <span class="n">GlobalWidgetsLocalizations</span><span class="o">.</span><span class="na">delegate</span><span class="o">,</span> <span class="o">],</span> <span class="nl">supportedLocales:</span> <span class="o">[</span> <span class="kd">const</span> <span class="n">Locale</span><span class="o">(</span><span class="s">'en'</span><span class="o">,</span> <span class="s">'US'</span><span class="o">),</span> <span class="c1">// English</span> <span class="kd">const</span> <span class="n">Locale</span><span class="o">(</span><span class="s">'he'</span><span class="o">,</span> <span class="s">'IL'</span><span class="o">),</span> <span class="c1">// Hebrew</span> <span class="c1">// ... other locales the app supports</span> <span class="o">],</span> <span class="c1">// ...</span> <span class="o">)</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>这些代理包括了实际的本地化值,并且 <code class="highlighter-rouge">supportedLocales</code> 定义了 App 支持哪些地区。上面的例子使用了一个 <code class="highlighter-rouge">MaterialApp</code> ,所以它既有 <code class="highlighter-rouge">GlobalWidgetsLocalizations</code> 用于基础 widgets,也有 <code class="highlighter-rouge">MaterialWidgetsLocalizations</code> 用于 <code class="highlighter-rouge">Material</code> wigets 的本地化。如果你使用 <code class="highlighter-rouge">WidgetsApp</code> ,则无需包括后者。注意,这两个代理虽然包括了“默认”值,但如果你想让你的 App 本地化,你仍需要提供一或多个代理作为你的 App 本地化副本。</p> <p>当初始化时,<code class="highlighter-rouge">WidgetsApp</code> 或 <code class="highlighter-rouge">MaterialApp</code> 会使用你指定的代理为你创建一个 <code class="highlighter-rouge">Localizations</code> widget。<a href="https://docs.flutter.io/flutter/widgets/Localizations-class.html">Localizations</a> widget 可以随时从当前上下文中访问设备的地点,或者使用 <a href="https://docs.flutter.io/flutter/dart-ui/Window/locale.html">Window.locale</a>。</p> <p>要访问本地化文件,使用 <code class="highlighter-rouge">Localizations.of()</code> 方法来访问提供代理的特定本地化类。如需翻译,使用 <a href="https://pub.dartlang.org/packages/intl_translation">intl_translation</a> 包来取出翻译副本到 <a href="https://github.com/googlei18n/app-resource-bundle">arb</a> 文件中。把它们引入 App 中,并用 intl 来使用它们。</p> <p>更多 Flutter 中国际化和本地化的细节,请访问 <a href="https://flutter.io/docs/development/accessibility-and-localization/internationalization">internationalization guide</a> ,里面有不使用 intl 包的示例代码。</p> <blockquote> <p>注意,在 Flutter 1.0 beta 2 之前,在 Flutter 中定义的 assets 不能在原生一侧被访问。原生定义的资源在 Flutter 中也不可用,因为它们在独立的文件夹中。</p> </blockquote> <h2 id="如何添加flutter项目所需的依赖"><a href="https://coding.imooc.com/class/321.html">如何添加Flutter项目所需的依赖?</a></h2> <ul> <li>在Android中,你可以在Gradle文件来添加依赖项;</li> <li>在 iOS 中,通常把依赖添加到 Podfile 中;</li> <li>在RN中,通常是由package.json来管理项目依赖;</li> </ul> <p>Flutter 使用 Dart 构建系统和 Pub 包管理器来处理依赖。这些工具将Android 和 iOS native 包装应用程序的构建委派给相应的构建系统。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">dependencies:</span> <span class="nl">flutter:</span> <span class="nl">sdk:</span> <span class="n">flutter</span> <span class="nl">google_sign_in:</span> <span class="o">^</span><span class="mf">3.0</span><span class="o">.</span><span class="mi">3</span> </code></pre></div></div> <blockquote> <p>在Flutter中,虽然在Flutter项目中的Android文件夹下有Gradle文件,<strong>但只有在添加平台相关所需的依赖关系时才使用这些文件</strong>。 否则,应该使用<code class="highlighter-rouge">pubspec.yaml</code>来声明用于Flutter的外部依赖项。</p> </blockquote> <blockquote> <p>iOS也是一样,如果你的 Flutter 工程中的 iOS 文件夹中有 Podfile,请仅在添加iOS平台相关的依赖时使用它。否则,应该使用<code class="highlighter-rouge">pubspec.yaml</code>来声明用于Flutter的外部依赖项。</p> </blockquote> <p>推荐一个用于查找Flutter插件的网站:<a href="https://pub.dartlang.org/flutter/packages">Pub site</a>。</p> <h2 id="参考-"><a href="https://coding.imooc.com/class/321.html">参考 </a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> <h2 id="未完待续"><a href="https://coding.imooc.com/class/321.html">未完待续</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter入门基础知识</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter主题和文字处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter什么是声明式UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter布局与列表</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter手势检测及触摸事件处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter状态管理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter线程和异步UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter表单输入与富文本</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter认识视图(Views)</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter调用硬件、第三方服务以及平台交互、通知</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter路由与导航</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter项目结构、资源、依赖和本地化</a></li> </ul> Tue, 02 Apr 2019 00:00:00 +0800 http://localhost:4000/2019/04/02/flutter-project-structure-resources-dependencies-and-localization/ http://localhost:4000/2019/04/02/flutter-project-structure-resources-dependencies-and-localization/ Flutter Android iOS Dart 两分钟带你掌握Flutter的路由与导航 <p><img src="io/flutter_app/img/blog/flutter-router-navigator.png" alt="StatelessWidget-StatefulWidget" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将带着大家一起认识<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">什么是Flutter的路由与导航</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">如何完成不同页面跳转?</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">如何获取路由跳转的返回记过?</code></a>,以及<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">如何跳转到其他APP?</code></a></p> <p>首先我们来学习在Flutter中如何实现不同页面跳转(导航)?</p> <h2 id="在flutter中如何实现不同页面跳转导航"><a href="https://coding.imooc.com/class/321.html">在Flutter中如何实现不同页面跳转(导航)?</a></h2> <blockquote> <p>Android:</p> </blockquote> <p>要在Flutter中切换屏幕,我们可以访问路由以绘制新的Widget。 管理多个屏幕有两个核心概念和类:Route 和 <a href="https://coding.imooc.com/class/321.html">Navigator</a>。Route是应用程序的“屏幕”或“页面”的抽象(可以认为是Activity), <a href="https://coding.imooc.com/class/321.html">Navigator</a>是管理Route的Widget。<a href="https://coding.imooc.com/class/321.html">Navigator</a>可以通过<a href="https://coding.imooc.com/class/321.html">push</a>和<a href="https://coding.imooc.com/class/321.html">pop</a> route以实现页面切换。</p> <p>和Android相似,我们可以在AndroidManifest.xml中声明Activities,在Flutter中,我们可以将具有指定Route的Map传递到顶层MaterialApp实例,但这不是必须的。</p> <blockquote> <p>iOS:</p> </blockquote> <p>在 iOS 中,可以使用管理了 view controller 栈的 UINavigationController 来在不同的 view controller 之间跳转。</p> <blockquote> <p>React Native:</p> </blockquote> <p>在React Native中,可以使用react-navigation来实现页面之间的导航。</p> <p><a href="https://coding.imooc.com/class/321.html">Flutter</a> 也有类似的实现,使用了 <a href="https://coding.imooc.com/class/321.html">Navigator</a> 和 <a href="https://coding.imooc.com/class/321.html">Routes</a>。一个路由是 App 中“屏幕”或“页面”的抽象,而一个 <a href="https://coding.imooc.com/class/321.html">Navigator</a> 是管理多个路由的 widget 。你可以粗略地把一个路由对应到一个 UIViewController。<a href="https://coding.imooc.com/class/321.html">Navigator</a> 的工作原理和 iOS 中 UINavigationController 非常相似,当你想跳转到新页面或者从新页面返回时,它可以 push() 和 pop() 路由。</p> <p>在Flutter中,有两个主要的widget用于在页面之间导航:</p> <ul> <li><a href="https://docs.flutter.io/flutter/widgets/Route-class.html">Route</a> 是一个应用程序抽象的屏幕或页面;</li> <li><a href="https://docs.flutter.io/flutter/widgets/Route-class.html">Navigator</a> 是一个管理路由的widget;</li> </ul> <p>以上两种widget对应Flutter中实现页面导航的有两种选择:</p> <ul> <li>具体指定一个由路由名构成的 Map。(MaterialApp)</li> <li>直接跳转到一个路由。(WidgetApp)</li> </ul> <p>下面是构建一个 Map 的例子:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="o">)</span> <span class="o">{</span> <span class="n">runApp</span><span class="o">(</span><span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">home:</span> <span class="n">MyAppHome</span><span class="o">(),</span> <span class="c1">// becomes the route named '/'</span> <span class="nl">routes:</span> <span class="o">&lt;</span><span class="kt">String</span><span class="o">,</span> <span class="n">WidgetBuilder</span><span class="o">&gt;</span> <span class="o">{</span> <span class="s">'/a'</span><span class="o">:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">MyPage</span><span class="o">(</span><span class="nl">title:</span> <span class="s">'page A'</span><span class="o">),</span> <span class="s">'/b'</span><span class="o">:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">MyPage</span><span class="o">(</span><span class="nl">title:</span> <span class="s">'page B'</span><span class="o">),</span> <span class="s">'/c'</span><span class="o">:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">MyPage</span><span class="o">(</span><span class="nl">title:</span> <span class="s">'page C'</span><span class="o">),</span> <span class="o">},</span> <span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>通过把路由的名字 <code class="highlighter-rouge">push</code> 给一个 <code class="highlighter-rouge">Navigator</code> 来跳转:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Navigator.of(context).pushNamed('/b'); </code></pre></div></div> <p>您还可以使用<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Navigator</code></a>的<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">push</code></a>方法,该方法将给定<code class="highlighter-rouge">route</code>添加到导航器的历史记录中。 在以下示例中,MaterialPageRoute widget是一种模版路由,它根据平台自适应替换整个页面。 在以下示例中,widget是一种模版路由,它使用平台自适应替换整个页面。它需要一个<a href="https://coding.imooc.com/class/321.html">WidgetBuilder</a>作为必需参数。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Navigator</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">MaterialPageRoute</span><span class="o">(</span><span class="nl">builder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">UsualNavscreen</span><span class="o">()));</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <h2 id="如何获取路由跳转返回的结果"><a href="https://coding.imooc.com/class/321.html">如何获取路由跳转返回的结果?</a></h2> <p>在Android中有<code class="highlighter-rouge">startActivityForResult</code>来获取跳转页面后返回的结果,那么在<a href="https://coding.imooc.com/class/321.html">Flutter</a>中<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Navigator</code></a> 类不仅用来处理 <a href="https://coding.imooc.com/class/321.html">Flutter</a> 中的路由,还被用来获取你刚 <code class="highlighter-rouge">push</code> 到栈中的路由返回的结果。通过 <code class="highlighter-rouge">await</code>等待路由返回的结果来达到这点。</p> <p>举个例子,要跳转到“位置”路由来让用户选择一个地点,你可能要这么做:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Map</span> <span class="n">coordinates</span> <span class="o">=</span> <span class="n">await</span> <span class="n">Navigator</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">context</span><span class="o">).</span><span class="na">pushNamed</span><span class="o">(</span><span class="s">'/location'</span><span class="o">);</span> </code></pre></div></div> <p>之后,在 location 路由中,一旦用户选择了地点,携带结果一起 pop() 出栈:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Navigator.of(context).pop({"lat":43.821757,"long":-79.226392}); </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <h2 id="如何在flutter中处理来自外部应用程序传入的intentsandroid"><a href="https://coding.imooc.com/class/321.html">如何在Flutter中处理来自外部应用程序传入的Intents?(Android)</a></h2> <p>Flutter可以通过直接与Android层通信并请求共享的数据来处理来自Android的Intents</p> <p>在这个例子中,我们注册文本共享<code class="highlighter-rouge">Intent</code>,所以其他应用程序可以共享文本到我们的Flutter应用程序</p> <p>这个应用程序的基本流程是我们首先处理Android端的共享文本数据,然后等待Flutter请求数据,然后通过<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">MethodChannel</code></a>发送。</p> <blockquote> <p>如果你对<a href="https://coding.imooc.com/class/321.html">MethodChannel</a>还不熟悉的话可以通过<a href="https://coding.imooc.com/learn/list/321.html">第8章 Flutter进阶提升:Flutter混合开发</a>教程进行详细的学习</p> </blockquote> <p>首先在在<code class="highlighter-rouge">AndroidManifest.xml</code>中注册我们想要处理的<code class="highlighter-rouge">Intent</code>:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&lt;</span><span class="n">activity</span> <span class="nl">android:</span><span class="n">name</span><span class="o">=</span><span class="s">".MainActivity"</span> <span class="nl">android:</span><span class="n">launchMode</span><span class="o">=</span><span class="s">"singleTop"</span> <span class="nl">android:</span><span class="n">theme</span><span class="o">=</span><span class="s">"@style/LaunchTheme"</span> <span class="nl">android:</span><span class="n">configChanges</span><span class="o">=</span><span class="s">"orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"</span> <span class="nl">android:</span><span class="n">hardwareAccelerated</span><span class="o">=</span><span class="s">"true"</span> <span class="nl">android:</span><span class="n">windowSoftInputMode</span><span class="o">=</span><span class="s">"adjustResize"</span><span class="o">&gt;</span> <span class="o">&lt;!--</span> <span class="o">...</span> <span class="o">--&gt;</span> <span class="o">&lt;</span><span class="n">intent</span><span class="o">-</span><span class="n">filter</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="n">action</span> <span class="nl">android:</span><span class="n">name</span><span class="o">=</span><span class="s">"android.intent.action.SEND"</span> <span class="o">/&gt;</span> <span class="o">&lt;</span><span class="n">category</span> <span class="nl">android:</span><span class="n">name</span><span class="o">=</span><span class="s">"android.intent.category.DEFAULT"</span> <span class="o">/&gt;</span> <span class="o">&lt;</span><span class="n">data</span> <span class="nl">android:</span><span class="n">mimeType</span><span class="o">=</span><span class="s">"text/plain"</span> <span class="o">/&gt;</span> <span class="o">&lt;/</span><span class="n">intent</span><span class="o">-</span><span class="n">filter</span><span class="o">&gt;</span> <span class="o">&lt;/</span><span class="n">activity</span><span class="o">&gt;</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>然后,在MainActivity中,您可以处理intent,一旦我们从intent中获得共享文本数据,我们就会持有它,直到Flutter在完成准备就绪时请求它。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="n">public</span> <span class="kd">class</span> <span class="nc">MainActivity</span> <span class="kd">extends</span> <span class="n">FlutterActivity</span> <span class="o">{</span> <span class="n">private</span> <span class="kt">String</span> <span class="n">sharedText</span><span class="o">;</span> <span class="nd">@Override</span> <span class="n">protected</span> <span class="kt">void</span> <span class="n">onCreate</span><span class="o">(</span><span class="n">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">onCreate</span><span class="o">(</span><span class="n">savedInstanceState</span><span class="o">);</span> <span class="n">GeneratedPluginRegistrant</span><span class="o">.</span><span class="na">registerWith</span><span class="o">(</span><span class="k">this</span><span class="o">);</span> <span class="n">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="n">getIntent</span><span class="o">();</span> <span class="kt">String</span> <span class="n">action</span> <span class="o">=</span> <span class="n">intent</span><span class="o">.</span><span class="na">getAction</span><span class="o">();</span> <span class="kt">String</span> <span class="n">type</span> <span class="o">=</span> <span class="n">intent</span><span class="o">.</span><span class="na">getType</span><span class="o">();</span> <span class="k">if</span> <span class="o">(</span><span class="n">Intent</span><span class="o">.</span><span class="na">ACTION_SEND</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">action</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="s">"text/plain"</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">type</span><span class="o">))</span> <span class="o">{</span> <span class="n">handleSendText</span><span class="o">(</span><span class="n">intent</span><span class="o">);</span> <span class="c1">// Handle text being sent</span> <span class="o">}</span> <span class="o">}</span> <span class="k">new</span> <span class="n">MethodChannel</span><span class="o">(</span><span class="n">getFlutterView</span><span class="o">(),</span> <span class="s">"app.channel.shared.data"</span><span class="o">).</span><span class="na">setMethodCallHandler</span><span class="o">(</span> <span class="k">new</span> <span class="n">MethodCallHandler</span><span class="o">()</span> <span class="o">{</span> <span class="nd">@Override</span> <span class="n">public</span> <span class="kt">void</span> <span class="n">onMethodCall</span><span class="o">(</span><span class="n">MethodCall</span> <span class="n">call</span><span class="o">,</span> <span class="n">MethodChannel</span><span class="o">.</span><span class="na">Result</span> <span class="n">result</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">call</span><span class="o">.</span><span class="na">method</span><span class="o">.</span><span class="na">contentEquals</span><span class="o">(</span><span class="s">"getSharedText"</span><span class="o">))</span> <span class="o">{</span> <span class="n">result</span><span class="o">.</span><span class="na">success</span><span class="o">(</span><span class="n">sharedText</span><span class="o">);</span> <span class="n">sharedText</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> <span class="o">});</span> <span class="o">}</span> <span class="kt">void</span> <span class="n">handleSendText</span><span class="o">(</span><span class="n">Intent</span> <span class="n">intent</span><span class="o">)</span> <span class="o">{</span> <span class="n">sharedText</span> <span class="o">=</span> <span class="n">intent</span><span class="o">.</span><span class="na">getStringExtra</span><span class="o">(</span><span class="n">Intent</span><span class="o">.</span><span class="na">EXTRA_TEXT</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>最后,在Flutter中,您可以在渲染Flutter视图时请求数据。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="kd">class</span> <span class="nc">SampleAppPage</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">SampleAppPage</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="nd">@override</span> <span class="n">_SampleAppPageState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_SampleAppPageState</span><span class="o">();</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">_SampleAppPageState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">SampleAppPage</span><span class="o">&gt;</span> <span class="o">{</span> <span class="kd">static</span> <span class="kd">const</span> <span class="n">platform</span> <span class="o">=</span> <span class="kd">const</span> <span class="n">MethodChannel</span><span class="o">(</span><span class="s">'app.channel.shared.data'</span><span class="o">);</span> <span class="kt">String</span> <span class="n">dataShared</span> <span class="o">=</span> <span class="s">"No data"</span><span class="o">;</span> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">getSharedText</span><span class="o">();</span> <span class="o">}</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span><span class="nl">body:</span> <span class="n">Center</span><span class="o">(</span><span class="nl">child:</span> <span class="n">Text</span><span class="o">(</span><span class="n">dataShared</span><span class="o">)));</span> <span class="o">}</span> <span class="n">getSharedText</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="kd">var</span> <span class="n">sharedData</span> <span class="o">=</span> <span class="n">await</span> <span class="n">platform</span><span class="o">.</span><span class="na">invokeMethod</span><span class="o">(</span><span class="s">"getSharedText"</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">sharedData</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">dataShared</span> <span class="o">=</span> <span class="n">sharedData</span><span class="o">;</span> <span class="o">});</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <h2 id="怎么跳转到其他-app"><a href="https://coding.imooc.com/class/321.html">怎么跳转到其他 App?</a></h2> <p>在 iOS 中,要跳转到其他 App,你需要一个特定的 URL Scheme。对系统级别的 App 来说,这个 scheme 取决于 App。为了在 Flutter 中实现这个功能,你可以创建一个原生平台的整合层,或者使用现有的 plugin,例如 <a href="https://pub.dartlang.org/packages/url_launcher">url_launcher</a>。</p> <blockquote> <p>大家可以通过<a href="https://coding.imooc.com/class/chapter/321.html#Anchor">《路由、Navigator与页面导航开发指南》</a>来学习Flutter页面导航与路由的更多技巧和实战经验。</p> </blockquote> <h2 id="未完待续"><a href="https://coding.imooc.com/class/321.html">未完待续</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter入门基础知识</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter主题和文字处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter什么是声明式UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter布局与列表</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter手势检测及触摸事件处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter状态管理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter线程和异步UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter表单输入与富文本</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter认识视图(Views)</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter调用硬件、第三方服务以及平台交互、通知</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter路由与导航</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter项目结构、资源、依赖和本地化</a></li> </ul> <h2 id="参考-"><a href="https://coding.imooc.com/class/321.html">参考 </a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Sun, 31 Mar 2019 00:00:00 +0800 http://localhost:4000/2019/03/31/flutter-router-navigator/ http://localhost:4000/2019/03/31/flutter-router-navigator/ Flutter Android iOS Dart React Native+React Navigation+Redux开发实用教程 <blockquote> <p><a href="">期待已久的新教程上线啦!解锁React Native开发新姿势,一网打尽React Native最新与最热技术,点我Get!!!</a></p> </blockquote> <p><img src="img/other/react-native-redux.png" alt="React Native+Redux开发实用教程" /></p> <p>为了帮助大家快速上手在React Native与Redux开发,在这本文中将向大家介绍<a href="">如何在React Native中使用Redux?</a>,以及一些<a href="">必备基础以及高级知识</a>。</p> <blockquote> <p>本参考了<a href="">《新版React Native+Redux打造高质量上线App》</a>课程的部分讲解,更多关于React Native与Redux的实战技巧可在<a href="">《新版React Native+Redux打造高质量上线App》</a>中查看。</p> </blockquote> <blockquote> <p>那么<a href="http://coding.imooc.com/class/304.html">如何在React Native中使用Redux和react-navigation组合?</a>呢?</p> </blockquote> <p>在使用 React Navigation 的项目中,想要集成 redux 就必须要引入 <code class="highlighter-rouge">react-navigation-redux-helpers</code> 这个库。</p> <h3 id="第一步安装react-navigation-redux-helpers"><a href="">第一步:安装react-navigation-redux-helpers</a></h3> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install --save react-navigation-redux-helpers </code></pre></div></div> <h3 id="第二步配置navigation"><a href="">第二步:配置Navigation</a></h3> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="s1">'react'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">createStackNavigator</span><span class="p">,</span> <span class="nx">createSwitchNavigator</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-navigation'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">connect</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-redux'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">createReactNavigationReduxMiddleware</span><span class="p">,</span> <span class="nx">reduxifyNavigator</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-navigation-redux-helpers'</span><span class="p">;</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">rootCom</span> <span class="o">=</span> <span class="s1">'Init'</span><span class="p">;</span><span class="c1">//设置根路由</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">RootNavigator</span> <span class="o">=</span> <span class="nx">createSwitchNavigator</span><span class="p">({</span> <span class="p">...</span> <span class="p">});</span> <span class="cm">/** * 1.初始化react-navigation与redux的中间件, * 该方法的一个很大的作用就是为reduxifyNavigator的key设置actionSubscribers(行为订阅者) * 设置订阅者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29 * 检测订阅者是否存在@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L97 * @type {Middleware} */</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">middleware</span> <span class="o">=</span> <span class="nx">createReactNavigationReduxMiddleware</span><span class="p">(</span> <span class="s1">'root'</span><span class="p">,</span> <span class="nx">state</span> <span class="o">=&gt;</span> <span class="nx">state</span><span class="p">.</span><span class="nx">nav</span> <span class="p">);</span> <span class="cm">/** * 2.将根导航器组件传递给 reduxifyNavigator 函数, * 并返回一个将navigation state 和 dispatch 函数作为 props的新组件; * 注意:要在createReactNavigationReduxMiddleware之后执行 */</span> <span class="kd">const</span> <span class="nx">AppWithNavigationState</span> <span class="o">=</span> <span class="nx">reduxifyNavigator</span><span class="p">(</span><span class="nx">RootNavigator</span><span class="p">,</span> <span class="s1">'root'</span><span class="p">);</span> <span class="cm">/** * State到Props的映射关系 * @param state */</span> <span class="kd">const</span> <span class="nx">mapStateToProps</span> <span class="o">=</span> <span class="nx">state</span> <span class="o">=&gt;</span> <span class="p">({</span> <span class="na">state</span><span class="p">:</span> <span class="nx">state</span><span class="p">.</span><span class="nx">nav</span><span class="p">,</span><span class="c1">//v2</span> <span class="p">});</span> <span class="cm">/** * 3.连接 React 组件与 Redux store */</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">mapStateToProps</span><span class="p">)(</span><span class="nx">AppWithNavigationState</span><span class="p">);</span> </code></pre></div></div> <blockquote> <p>提示:createReactNavigationReduxMiddleware方法要放到reduxifyNavigator之前执行否则会报错,以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> </blockquote> <h3 id="第二步配置reducer"><a href="">第二步:配置Reducer</a></h3> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">combineReducers</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'redux'</span> <span class="k">import</span> <span class="nx">theme</span> <span class="k">from</span> <span class="s1">'./theme'</span> <span class="k">import</span> <span class="p">{</span><span class="nx">rootCom</span><span class="p">,</span> <span class="nx">RootNavigator</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'../navigator/AppNavigators'</span><span class="p">;</span> <span class="c1">//1.指定默认state</span> <span class="kd">const</span> <span class="nx">navState</span> <span class="o">=</span> <span class="nx">RootNavigator</span><span class="p">.</span><span class="nx">router</span><span class="p">.</span><span class="nx">getStateForAction</span><span class="p">(</span><span class="nx">RootNavigator</span><span class="p">.</span><span class="nx">router</span><span class="p">.</span><span class="nx">getActionForPathAndParams</span><span class="p">(</span><span class="nx">rootCom</span><span class="p">));</span> <span class="cm">/** * 2.创建自己的 navigation reducer, */</span> <span class="kd">const</span> <span class="nx">navReducer</span> <span class="o">=</span> <span class="p">(</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">navState</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">nextState</span> <span class="o">=</span> <span class="nx">RootNavigator</span><span class="p">.</span><span class="nx">router</span><span class="p">.</span><span class="nx">getStateForAction</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">state</span><span class="p">);</span> <span class="c1">// 如果`nextState`为null或未定义,只需返回原始`state`</span> <span class="k">return</span> <span class="nx">nextState</span> <span class="o">||</span> <span class="nx">state</span><span class="p">;</span> <span class="p">};</span> <span class="cm">/** * 3.合并reducer * @type {Reducer&lt;any&gt; | Reducer&lt;any, AnyAction&gt;} */</span> <span class="kd">const</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">combineReducers</span><span class="p">({</span> <span class="na">nav</span><span class="p">:</span> <span class="nx">navReducer</span><span class="p">,</span> <span class="na">theme</span><span class="p">:</span> <span class="nx">theme</span><span class="p">,</span> <span class="p">});</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">index</span><span class="p">;</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <h3 id="第三步配置store"><a href="">第三步:配置store</a></h3> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">applyMiddleware</span><span class="p">,</span> <span class="nx">createStore</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'redux'</span> <span class="k">import</span> <span class="nx">thunk</span> <span class="k">from</span> <span class="s1">'redux-thunk'</span> <span class="k">import</span> <span class="nx">reducers</span> <span class="k">from</span> <span class="s1">'../reducer'</span> <span class="k">import</span> <span class="p">{</span><span class="nx">middleware</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'../navigator/AppNavigators'</span> <span class="kd">const</span> <span class="nx">middlewares</span> <span class="o">=</span> <span class="p">[</span> <span class="nx">middleware</span><span class="p">,</span> <span class="p">];</span> <span class="cm">/** * 创建store */</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">createStore</span><span class="p">(</span><span class="nx">reducers</span><span class="p">,</span> <span class="nx">applyMiddleware</span><span class="p">(...</span><span class="nx">middlewares</span><span class="p">));</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <h3 id="第四步在组件中应用"><a href="">第四步:在组件中应用</a></h3> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span><span class="nx">Component</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">Provider</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-redux'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">AppNavigator</span> <span class="k">from</span> <span class="s1">'./navigator/AppNavigators'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">store</span> <span class="k">from</span> <span class="s1">'./store'</span> <span class="nx">type</span> <span class="nx">Props</span> <span class="o">=</span> <span class="p">{};</span> <span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">App</span> <span class="kd">extends</span> <span class="nx">Component</span><span class="o">&lt;</span><span class="nx">Props</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/** * 将store传递给App框架 */</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">Provider</span> <span class="nx">store</span><span class="o">=</span><span class="p">{</span><span class="nx">store</span><span class="p">}</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">AppNavigator</span><span class="o">/&gt;</span> <span class="o">&lt;</span><span class="sr">/Provider</span><span class="err">&gt; </span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <p>经过上述4步呢,我们已经完成了react-navigaton+redux的集成,那么如何使用它呢?</p> <h3 id="使用react-navigatonredux"><a href="">使用react-navigaton+redux</a></h3> <blockquote> <p>1.订阅state</p> </blockquote> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="s1">'react'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">connect</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-redux'</span><span class="p">;</span> <span class="kd">class</span> <span class="nx">TabBarComponent</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span> <span class="o">&lt;</span><span class="nx">BottomTabBar</span> <span class="p">{...</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">}</span> <span class="nx">activeTintColor</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">theme</span><span class="p">}</span> <span class="sr">/</span><span class="err">&gt; </span> <span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">mapStateToProps</span> <span class="o">=</span> <span class="nx">state</span> <span class="o">=&gt;</span> <span class="p">({</span> <span class="na">theme</span><span class="p">:</span> <span class="nx">state</span><span class="p">.</span><span class="nx">theme</span><span class="p">.</span><span class="nx">theme</span><span class="p">,</span> <span class="p">});</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">mapStateToProps</span><span class="p">)(</span><span class="nx">TabBarComponent</span><span class="p">);</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <p>在上述代码中我们订阅了store中的theme state,然后该组件就可以通过<code class="highlighter-rouge">this.props.theme</code>获取到所订阅的theme state了。</p> <blockquote> <p>2.触发action改变state</p> </blockquote> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span><span class="nx">Component</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">connect</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-redux'</span> <span class="k">import</span> <span class="p">{</span><span class="nx">onThemeChange</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'../action/theme'</span> <span class="k">import</span> <span class="p">{</span><span class="nx">StyleSheet</span><span class="p">,</span> <span class="nx">Text</span><span class="p">,</span> <span class="nx">View</span><span class="p">,</span> <span class="nx">Button</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-native'</span><span class="p">;</span> <span class="nx">type</span> <span class="nx">Props</span> <span class="o">=</span> <span class="p">{};</span> <span class="kd">class</span> <span class="nx">FavoritePage</span> <span class="kd">extends</span> <span class="nx">Component</span><span class="o">&lt;</span><span class="nx">Props</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span> <span class="o">&lt;</span><span class="nx">View</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="p">}</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">Text</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">welcome</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">FavoritePage</span><span class="o">&lt;</span><span class="sr">/Text</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">Button</span> <span class="nx">title</span><span class="o">=</span><span class="s2">"改变主题色"</span> <span class="nx">onPress</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// let {dispatch} = this.props.navigation;</span> <span class="c1">// dispatch(onThemeChange('red'))</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">onThemeChange</span><span class="p">(</span><span class="s1">'#096'</span><span class="p">);</span> <span class="p">}}</span> <span class="sr">/</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/View</span><span class="err">&gt; </span> <span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">mapStateToProps</span> <span class="o">=</span> <span class="nx">state</span> <span class="o">=&gt;</span> <span class="p">({});</span> <span class="kd">const</span> <span class="nx">mapDispatchToProps</span> <span class="o">=</span> <span class="nx">dispatch</span> <span class="o">=&gt;</span> <span class="p">({</span> <span class="na">onThemeChange</span><span class="p">:</span> <span class="p">(</span><span class="nx">theme</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">onThemeChange</span><span class="p">(</span><span class="nx">theme</span><span class="p">)),</span> <span class="p">});</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">mapStateToProps</span><span class="p">,</span> <span class="nx">mapDispatchToProps</span><span class="p">)(</span><span class="nx">FavoritePage</span><span class="p">);</span> <span class="p">...</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <p>触发action有两种方式:</p> <ul> <li>一种是通过mapDispatchToProps将dispatch创建函数和props绑定,这样就可以通过<code class="highlighter-rouge">this.props.onThemeChange('#096')</code>调用这个dispatch创建函数来触发<code class="highlighter-rouge">onThemeChange</code> action了;</li> <li> <p>另外一种方式是通过this.props中的navigation来获取dispatch,然后通过这个dispatch手动触发一个action:</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">let</span> <span class="p">{</span><span class="nx">dispatch</span><span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">navigation</span><span class="p">;</span> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">onThemeChange</span><span class="p">(</span><span class="s1">'red'</span><span class="p">))</span> </code></pre></div> </div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> </li> </ul> <h2 id="在reduxreact-navigation场景中处理-android-中的物理返回键-"><a href="">在Redux+react-navigation场景中处理 Android 中的物理返回键 </a></h2> <p>在Redux+react-navigation场景中处理Android的物理返回键需要注意当前路由的所以位置,然后根据指定路由的索引位置来进行操作,这里需要用到<a href="https://facebook.github.io/react-native/docs/backhandler#docsNav">BackHandler</a>。</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span><span class="nx">Component</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">BackHandler</span><span class="p">}</span> <span class="k">from</span> <span class="s2">"react-native"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">NavigationActions</span><span class="p">}</span> <span class="k">from</span> <span class="s2">"react-navigation"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span><span class="nx">connect</span><span class="p">}</span> <span class="k">from</span> <span class="s1">'react-redux'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">DynamicTabNavigator</span> <span class="k">from</span> <span class="s1">'../navigator/DynamicTabNavigator'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">NavigatorUtil</span> <span class="k">from</span> <span class="s1">'../navigator/NavigatorUtil'</span><span class="p">;</span> <span class="nx">type</span> <span class="nx">Props</span> <span class="o">=</span> <span class="p">{};</span> <span class="kd">class</span> <span class="nx">HomePage</span> <span class="kd">extends</span> <span class="nx">Component</span><span class="o">&lt;</span><span class="nx">Props</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span> <span class="nx">BackHandler</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">"hardwareBackPress"</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">onBackPress</span><span class="p">);</span> <span class="p">}</span> <span class="nx">componentWillUnmount</span><span class="p">()</span> <span class="p">{</span> <span class="nx">BackHandler</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">(</span><span class="s2">"hardwareBackPress"</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">onBackPress</span><span class="p">);</span> <span class="p">}</span> <span class="cm">/** * 处理 Android 中的物理返回键 * https://reactnavigation.org/docs/en/redux-integration.html#handling-the-hardware-back-button-in-android * @returns {boolean} */</span> <span class="nx">onBackPress</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">{</span><span class="nx">dispatch</span><span class="p">,</span> <span class="nx">nav</span><span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span> <span class="c1">//if (nav.index === 0) {</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nav</span><span class="p">.</span><span class="nx">routes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">index</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span><span class="c1">//如果RootNavigator中的MainNavigator的index为0,则不处理返回事件</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="p">}</span> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">NavigationActions</span><span class="p">.</span><span class="nx">back</span><span class="p">());</span> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="p">};</span> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">DynamicTabNavigator</span><span class="o">/&gt;</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">mapStateToProps</span> <span class="o">=</span> <span class="nx">state</span> <span class="o">=&gt;</span> <span class="p">({</span> <span class="na">nav</span><span class="p">:</span> <span class="nx">state</span><span class="p">.</span><span class="nx">nav</span><span class="p">,</span> <span class="p">});</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">mapStateToProps</span><span class="p">)(</span><span class="nx">HomePage</span><span class="p">);</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <blockquote> <p>2end</p> </blockquote> <h2 id="api-"><a href="">API </a></h2> <h3 id="combinereducersreducers"><a href="">combineReducers(reducers)</a></h3> <p>随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。</p> <blockquote> <p>函数原型:<code class="highlighter-rouge">combineReducers(reducers)</code></p> </blockquote> <ul> <li> <p>参数:reducers (Object): 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数需要满足的规则。</p> </li> <li> <p>每个传入 combineReducers 的 reducer 都需满足以下规则:</p> <ul> <li>所有未匹配到的 action,必须把它接收到的第一个参数也就是那个 state 原封不动返回。</li> <li>永远不能返回 undefined。当过早 return 时非常容易犯这个错误,为了避免错误扩散,遇到这种情况时 combineReducers 会抛异常。</li> <li>如果传入的 state 就是 undefined,一定要返回对应 reducer 的初始 state。根据上一条规则,初始 state 禁止使用 undefined。使用 ES6 的默认参数值语法来设置初始 state 很容易,但你也可以手动检查第一个参数是否为 undefined。</li> </ul> </li> </ul> <p><strong>返回值</strong></p> <p>(Function):一个调用 reducers 对象里所有 reducer 的 reducer,并且构造一个与 reducers 对象结构相同的 state 对象。</p> <p><code class="highlighter-rouge">combineReducers</code> 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。</p> <p>合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 <code class="highlighter-rouge">combineReducers()</code> 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 <code class="highlighter-rouge">combineReducers()</code> 时对应的 key 进行命名。</p> <blockquote> <p>提示:在 reducer 层级的任何一级都可以调用 combineReducers。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer,甚至更多层。</p> </blockquote> <h3 id="createstore"><a href="">createStore</a></h3> <blockquote> <p>函数原型:<code class="highlighter-rouge">createStore(reducer, [preloadedState], enhancer)</code></p> </blockquote> <p><strong>参数</strong></p> <ul> <li><code class="highlighter-rouge">reducer (Function):</code>:项目的根reducer。</li> <li><code class="highlighter-rouge">[preloadedState] (any)</code>:这个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。</li> <li><code class="highlighter-rouge">enhancer (Function)</code>: Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。</li> </ul> <p><strong>返回值</strong></p> <ul> <li><code class="highlighter-rouge">(Store)</code>: 保存了应用所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe 监听 state 的变化,然后更新 UI。</li> </ul> <p><strong>示例</strong></p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">createStore</span> <span class="p">}</span> <span class="k">from</span> <span class="s1">'redux'</span> <span class="kd">function</span> <span class="nx">todos</span><span class="p">(</span><span class="nx">state</span> <span class="o">=</span> <span class="p">[],</span> <span class="nx">action</span><span class="p">)</span> <span class="p">{</span> <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="s1">'ADD_TODO'</span><span class="p">:</span> <span class="k">return</span> <span class="nx">state</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span><span class="nx">action</span><span class="p">.</span><span class="nx">text</span><span class="p">])</span> <span class="k">default</span><span class="p">:</span> <span class="k">return</span> <span class="nx">state</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">let</span> <span class="nx">store</span> <span class="o">=</span> <span class="nx">createStore</span><span class="p">(</span><span class="nx">todos</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Use Redux'</span><span class="p">])</span> <span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span><span class="p">({</span> <span class="na">type</span><span class="p">:</span> <span class="s1">'ADD_TODO'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="s1">'Read the docs'</span> <span class="p">})</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">store</span><span class="p">.</span><span class="nx">getState</span><span class="p">())</span> <span class="c1">// [ 'Use Redux', 'Read the docs' ]</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <blockquote> <p>注意事项</p> </blockquote> <ul> <li>应用中不要创建多个 store!相反,使用 combineReducers 来把多个 reducer 创建成一个根 reducer。</li> <li>你可以决定 state 的格式。你可以使用普通对象或者 Immutable 这类的实现。如果你不知道如何做,刚开始可以使用普通对象。</li> <li>如果 state 是普通对象,永远不要修改它!比如,reducer 里不要使用 Object.assign(state, newData),应该使用 Object.assign({}, state, newData)。这样才不会覆盖旧的 state。如果可以的话,也可以使用 对象拓展操作符(object spread spread operator 特性中的 return { …state, …newData }。</li> <li>对于服务端运行的同构应用,为每一个请求创建一个 store 实例,以此让 store 相隔离。dispatch 一系列请求数据的 action 到 store 实例上,等待请求完成后再在服务端渲染应用。</li> <li>当 store 创建后,Redux 会 dispatch 一个 action 到 reducer 上,来用初始的 state 来填充 store。你不需要处理这个 action。但要记住,如果第一个参数也就是传入的 state 是 undefined 的话,reducer 应该返回初始的 state 值。</li> <li>要使用多个 store 增强器的时候,你可能需要使用 compose</li> </ul> <h3 id="applymiddleware"><a href="">applyMiddleware</a></h3> <blockquote> <p>函数原型:<code class="highlighter-rouge">applyMiddleware(...middleware)</code></p> </blockquote> <p>使用包含自定义功能的 middleware 来扩展 Redux。</p> <h2 id="技巧"><a href="">技巧</a></h2> <ul> <li>react-navigation+redux;</li> <li>如何防止重复创建实例: <ul> <li>方式一:单例+Map+工厂;</li> <li>方式二:页面保存实例变量,传递给,Action使用;</li> <li>方式三:在action中创建实例;</li> </ul> </li> <li>如何动态的设置store,和动态获取store(难点:storekey不固定);</li> <li>如何实现可取消的redux action:可参考SearchPage的设计;</li> </ul> <p>上述的实战技巧可在<a href="">新版React Native+Redux打造高质量上线App</a>中获取;</p> <h2 id="问答"><a href="">问答</a></h2> <ul> <li>Redux是如何实现JS的可预测状态的管理? <ul> <li>单一数据源;</li> <li>所有数据都是只读的,要想修改数据,必须 dispatch 一个 action 来描述什么发生了改变;</li> <li>当处理 action 时,必须生成一个新的 state,不得直接修改原始对象;</li> </ul> </li> <li>Redux的核心思想是什么? <ul> <li>单向数据流的是Redux架构的设计核心;</li> </ul> </li> </ul> <h3 id="如何做到从不直接修改-state-"><a href="">如何做到从不直接修改 state ?</a></h3> <p>从不直接修改 state 是 Redux 的核心理念之一:为实现这一理念,可以通过一下两种方式:</p> <h4 id="1通过objectassignhttpsdevelopermozillaorgendocswebjavascriptreferenceglobal_objectsobjectassign创建对象拷贝-而拷贝中会包含新创建或更新过的属性值"><a href="">1.通过[Object.assign()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)创建对象拷贝, 而拷贝中会包含新创建或更新过的属性值</a></h4> <p>在下面的 todoApp 示例中, Object.assign() 将会返回一个新的 state 对象, 而其中的 visibilityFilter 属性被更新了:</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">todoApp</span><span class="p">(</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">initialState</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="p">{</span> <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="nx">SET_VISIBILITY_FILTER</span><span class="p">:</span> <span class="k">return</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span> <span class="nx">state</span><span class="p">,</span> <span class="p">{</span> <span class="na">visibilityFilter</span><span class="p">:</span> <span class="nx">action</span><span class="p">.</span><span class="nx">filter</span> <span class="p">})</span> <span class="k">default</span><span class="p">:</span> <span class="k">return</span> <span class="nx">state</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <h4 id="2-通过通过es7的新特性对象展开运算符object-spread-operatorhttpcnreduxjsorgdocsrecipesusingobjectspreadoperatorhtml"><a href="">2. 通过通过ES7的新特性[对象展开运算符(Object Spread Operator)](http://cn.redux.js.org/docs/recipes/UsingObjectSpreadOperator.html)</a></h4> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">todoApp</span><span class="p">(</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">initialState</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="p">{</span> <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="nx">SET_VISIBILITY_FILTER</span><span class="p">:</span> <span class="k">return</span> <span class="p">{</span> <span class="p">...</span><span class="nx">state</span><span class="p">,</span> <span class="na">visibilityFilter</span><span class="p">:</span> <span class="nx">action</span><span class="p">.</span><span class="nx">filter</span> <span class="p">}</span> <span class="nl">default</span><span class="p">:</span> <span class="k">return</span> <span class="nx">state</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>以上代码片段的完整部分可以在<a href="">课程源码</a>中查找。</p> <p>这样你就能轻松的跳回到这个对象之前的某个状态(想象一个撤销功能)。</p> <h2 id="总结"><a href="">总结</a></h2> <ul> <li>Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store;</li> <li>redux一个特点是:状态共享,所有的状态都放在一个store中,任何component都可以订阅store中的数据;</li> <li>并不是所有的state都适合放在store中,这样会让store变得非常庞大,如某个状态只被一个组件使用,不存在状态共享,可以不放在store中;</li> </ul> <h2 id="未完待续"><a href="">未完待续</a></h2> <ul> <li><a href="http://www.imooc.com/article/281446">Redux开发实用教程</a></li> <li><a href="http://www.imooc.com/article/283047">React Native+Redux开发实用教程</a></li> </ul> <h2 id="参考-"><a href="">参考 </a></h2> <ul> <li><a href="http://coding.imooc.com/class/304.html">新版React Native+Redux打造高质量上线App</a></li> <li><a href="https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367">你也许不需要redux</a></li> <li><a href="http://slides.com/dabit3/deck-11-12#/">React Native Redux Thunk vs Saga vs Observable</a></li> <li><a href="https://github.com/xgrommx/awesome-redux">awesome-redux</a></li> </ul> Sat, 30 Mar 2019 00:00:00 +0800 http://localhost:4000/2019/03/30/react-native-redux-react-navigation/ http://localhost:4000/2019/03/30/react-native-redux-react-navigation/ Redux开发实用教程 React Native Android iOS 两分钟带你掌握Flutter的StatelessWidget与StatefulWidget <p><img src="io/flutter_app/img/blog/StatelessWidget-StatefulWidget.png" alt="StatelessWidget-StatefulWidget" /></p> <blockquote> <p><a href="https://coding.imooc.com/class/321.html?mc_marking=f0d19be86650c0e77daa6fe37e140ded&amp;mc_channel=shouji">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,点我Get!!!</a></p> </blockquote> <p>在这篇文章中,将带着大家一起认识<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">什么是StatelessWidget?</code></a>,<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">什么是StatefulWidget?</code></a>,以及<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">StatefulWidget和StatelessWidget都有哪些最佳实践?</code></a></p> <p>状态是在构建widget时可以同步读取的信息,或者在widget的生命周期中可能更改的信息,在Flutter中如果要管理状态需要用到<a href="https://docs.flutter.io/flutter/widgets/StatefulWidget-class.html"> StatefulWidget</a>。</p> <h2 id="什么是statelesswidget"><a href="https://coding.imooc.com/class/321.html">什么是StatelessWidget?</a></h2> <p>Flutter中的<code class="highlighter-rouge">StatelessWidget</code>是一个不需要状态更改的widget - 它没有要管理的内部状态。</p> <p>当您描述的用户界面部分不依赖于对象本身中的配置信息以及widget的BuildContext 时,无状态widget非常有用。</p> <p><code class="highlighter-rouge">AboutDialog</code>, <code class="highlighter-rouge">CircleAvatar</code>和 <code class="highlighter-rouge">Text</code> 都是<code class="highlighter-rouge">StatelessWidget</code>的子类。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Flutter</span> <span class="kn">import</span> <span class="s">'package:flutter/material.dart'</span><span class="o">;</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="o">)</span> <span class="o">=&gt;</span> <span class="n">runApp</span><span class="o">(</span><span class="n">MyStatelessWidget</span><span class="o">(</span><span class="nl">text:</span> <span class="s">"StatelessWidget Example to show immutable data"</span><span class="o">));</span> <span class="kd">class</span> <span class="nc">MyStatelessWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="kd">final</span> <span class="kt">String</span> <span class="n">text</span><span class="o">;</span> <span class="n">MyStatelessWidget</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">text</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">Text</span><span class="o">(</span> <span class="n">text</span><span class="o">,</span> <span class="nl">textDirection:</span> <span class="n">TextDirection</span><span class="o">.</span><span class="na">ltr</span><span class="o">,</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>在前面的示例中,您使用了MyStatelessWidget类的构造函数 传递标记为final的text。这个类继承了StatelessWidget-它包含不可变数据</p> <p>无状态widget的build方法通常只会在以下三种情况调用:</p> <ul> <li>将widget插入树中时</li> <li>当widget的父级更改其配置时</li> <li>当它依赖的<a href="https://docs.flutter.io/flutter/widgets/InheritedWidget-class.html">InheritedWidget</a>发生变化时</li> </ul> <h2 id="什么是statefulwidget"><a href="https://coding.imooc.com/class/321.html">什么是StatefulWidget?</a></h2> <p><a href="https://docs.flutter.io/flutter/widgets/StatefulWidget-class.html">StatefulWidget</a> 是可变状态的widget。 使用<code class="highlighter-rouge">setState</code>方法管理StatefulWidget的状态的改变。调用<code class="highlighter-rouge">setState</code>告诉Flutter框架,某个状态发生了变化,Flutter会重新运行build方法,以便应用程序可以应用最新状态。</p> <p>状态是在构建widget时可以同步读取的信息可能会在widget的生命周期中发生变化。确保在状态改变时及时通知状态 变化是widget实现者的责任。当widget可以动态更改时,需要使用StatefulWidget。 例如, 通过键入表单或移动滑块来更改widget的状态. 或者, 它可以随时间变化 - 或者数据推送更新UI</p> <p><code class="highlighter-rouge">Checkbox</code>, <code class="highlighter-rouge">Radio</code>, <code class="highlighter-rouge">Slider</code>, <code class="highlighter-rouge">InkWell</code>, <code class="highlighter-rouge">Form</code>, 和 <code class="highlighter-rouge">TextField</code> 都是有状态的widget,也是StatefulWidget的子类。</p> <p>下面的示例声明了一个StatefulWidget,它需要一个<code class="highlighter-rouge">createState()</code>方法。此方法创建管理widget状态的状态对象_MyStatefulWidgetState。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyStatefulWidget</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">MyStatefulWidget</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">title</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="o">;</span> <span class="nd">@override</span> <span class="n">_MyStatefulWidgetState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_MyStatefulWidgetState</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>以下状态类_MyStatefulWidgetState实现widget的<code class="highlighter-rouge">build()</code>方法。当状态改变时,例如,当用户切换按钮时,使用新的切换值调用<code class="highlighter-rouge">setState</code>。这会导致框架在UI中重建此widget。</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">_MyStatefulWidgetState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">MyStatefulWidget</span><span class="o">&gt;</span> <span class="o">{</span> <span class="kt">bool</span> <span class="n">showtext</span><span class="o">=</span><span class="kc">true</span><span class="o">;</span> <span class="kt">bool</span> <span class="n">toggleState</span><span class="o">=</span><span class="kc">true</span><span class="o">;</span> <span class="n">Timer</span> <span class="n">t2</span><span class="o">;</span> <span class="kt">void</span> <span class="n">toggleBlinkState</span><span class="o">(){</span> <span class="n">setState</span><span class="o">((){</span> <span class="n">toggleState</span><span class="o">=!</span><span class="n">toggleState</span><span class="o">;</span> <span class="o">});</span> <span class="kd">var</span> <span class="n">twenty</span> <span class="o">=</span> <span class="kd">const</span> <span class="n">Duration</span><span class="o">(</span><span class="nl">milliseconds:</span> <span class="mi">1000</span><span class="o">);</span> <span class="k">if</span><span class="o">(</span><span class="n">toggleState</span><span class="o">==</span><span class="kc">false</span><span class="o">)</span> <span class="o">{</span> <span class="n">t2</span> <span class="o">=</span> <span class="n">Timer</span><span class="o">.</span><span class="na">periodic</span><span class="o">(</span><span class="n">twenty</span><span class="o">,</span> <span class="o">(</span><span class="n">Timer</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span> <span class="n">toggleShowText</span><span class="o">();</span> <span class="o">});</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="n">t2</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="kt">void</span> <span class="n">toggleShowText</span><span class="o">(){</span> <span class="n">setState</span><span class="o">((){</span> <span class="n">showtext</span><span class="o">=!</span><span class="n">showtext</span><span class="o">;</span> <span class="o">});</span> <span class="o">}</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">body:</span> <span class="n">Center</span><span class="o">(</span> <span class="nl">child:</span> <span class="n">Column</span><span class="o">(</span> <span class="nl">children:</span> <span class="o">&lt;</span><span class="n">Widget</span><span class="o">&gt;[</span> <span class="o">(</span><span class="n">showtext</span> <span class="o">?(</span><span class="n">Text</span><span class="o">(</span><span class="s">'This execution will be done before you can blink.'</span><span class="o">))</span> <span class="o">:(</span><span class="n">Container</span><span class="o">())</span> <span class="o">),</span> <span class="n">Padding</span><span class="o">(</span> <span class="nl">padding:</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">only</span><span class="o">(</span><span class="nl">top:</span> <span class="mf">70.0</span><span class="o">),</span> <span class="nl">child:</span> <span class="n">RaisedButton</span><span class="o">(</span> <span class="nl">onPressed:</span> <span class="n">toggleBlinkState</span><span class="o">,</span> <span class="nl">child:</span> <span class="o">(</span><span class="n">toggleState</span> <span class="o">?(</span> <span class="n">Text</span><span class="o">(</span><span class="s">'Blink'</span><span class="o">))</span> <span class="o">:(</span><span class="n">Text</span><span class="o">(</span><span class="s">'Stop Blinking'</span><span class="o">))</span> <span class="o">)</span> <span class="o">)</span> <span class="o">)</span> <span class="o">],</span> <span class="o">),</span> <span class="o">),</span> <span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <h2 id="statefulwidget和statelesswidget有哪些最佳实践"><a href="https://coding.imooc.com/class/321.html">StatefulWidget和StatelessWidget有哪些最佳实践?</a></h2> <p>在设计widget时,需要考虑以下几点。</p> <blockquote> <ol> <li>确定widget应该使用StatefulWidget还是StatelessWidget</li> </ol> </blockquote> <p><strong>在Flutter中,widget是有状态的还是无状态的 - 取决于是否 他们依赖于状态的变化</strong></p> <ul> <li>如果用户交互或数据改变导致widget改变,那么它就是有状态的。</li> <li>如果一个widget是最终的或不可变的,那么它就是无状态。</li> </ul> <blockquote> <ol> <li>确定哪个对象管理widget的状态(对于StatefulWidget) 在Flutter中,管理状态有三种主要方式:</li> </ol> </blockquote> <ul> <li>每个widget管理自己的状态</li> <li>父widget管理widget的状态</li> <li>混合搭配管理的方法</li> </ul> <p>如何决定使用哪种方式时,可以参考以下原则:</p> <ul> <li>如果所讨论的状态是用户数据,例如复选框的已选中或未选中状态,或滑块的位置,则状态最好由父widget管理;</li> <li>如果widget的状态取决于动作,例如动画,那么最好是由widget自身来管理状态;</li> <li>如有还是不确定谁管理状态,请让父widget管理子widget的状态;</li> </ul> <blockquote> <ol> <li>StatefulWidget 和 Stated的子类</li> </ol> </blockquote> <p>MyStatefulWidget类管理自己的状态 - 它扩展了StatefulWidget,它覆盖<code class="highlighter-rouge">createState()</code>方法来创建State对象,框架调用<code class="highlighter-rouge">createState()</code>来构建widget。在这个例子中,<code class="highlighter-rouge">createState()</code>创建了一个_MyStatefulWidgetState的实例 在下一个最佳实践中实现:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyStatefulWidget</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">MyStatefulWidget</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">title</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="o">;</span> <span class="nd">@override</span> <span class="n">_MyStatefulWidgetState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_MyStatefulWidgetState</span><span class="o">();</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">_MyStatefulWidgetState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">MyStatefulWidget</span><span class="o">&gt;</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="o">...</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <blockquote> <ol> <li>将StatefulWidget添加到widget树中</li> </ol> </blockquote> <p>将自定义的StatefulWidget添加到应用程序构建方法中的widget树中:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyStatelessWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="c1">// This widget is the root of your application.</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="s">'Flutter Demo'</span><span class="o">,</span> <span class="nl">theme:</span> <span class="n">ThemeData</span><span class="o">(</span> <span class="nl">primarySwatch:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">blue</span><span class="o">,</span> <span class="o">),</span> <span class="nl">home:</span> <span class="n">MyStatefulWidget</span><span class="o">(</span><span class="nl">title:</span> <span class="s">'State Change Demo'</span><span class="o">),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p><img src="io/flutter_app/img/blog/state-change-ios.gif" alt="state-change-ios" /> <img src="io/flutter_app/img/blog/state-change-android.gif" alt="state-change-ios" /></p> <h2 id="未完待续"><a href="https://coding.imooc.com/class/321.html">未完待续</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter入门基础知识</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter主题和文字处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter什么是声明式UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter布局与列表</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter手势检测及触摸事件处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter状态管理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter线程和异步UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter表单输入与富文本</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter认识视图(Views)</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter调用硬件、第三方服务以及平台交互、通知</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter路由与导航</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter项目结构、资源、依赖和本地化</a></li> </ul> <h2 id="参考-"><a href="https://coding.imooc.com/class/321.html">参考 </a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Sat, 23 Mar 2019 00:00:00 +0800 http://localhost:4000/2019/03/23/flutter-statelesswidget-statefulwidget/ http://localhost:4000/2019/03/23/flutter-statelesswidget-statefulwidget/ Flutter Android iOS Dart 「快速上手Flutter开发系列教程」之线程和异步UI <blockquote> <p><a href="https://coding.imooc.com/class/321.html">为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,点我Get!!!</a></p> </blockquote> <p><img src="io/flutter_app/img/blog/async-flutter.jpg" alt="Flutter异步编程" /></p> <p>在这篇文章中,将向大家分享在Flutter中:</p> <ul> <li><a href="https://coding.imooc.com/class/321.html">怎么编写异步的代码?</a></li> <li><a href="https://coding.imooc.com/class/321.html">怎么把工作放到后台线程执行?</a></li> <li><a href="https://coding.imooc.com/class/321.html">如何进行网络请求?</a></li> <li><a href="https://coding.imooc.com/class/321.html">如何为长时间运行的任务添加一个进度指示器?</a></li> </ul> <p>这些Flutter开发的实用技能。</p> <h2 id="怎么编写异步的代码"><a href="https://coding.imooc.com/class/321.html">怎么编写异步的代码?</a></h2> <p>Dart有一个单线程执行模型,支持<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Isolate</code></a>(一种在另一个线程上运行Dart代码的方法),一个事件循环和异步编程。除非你自己创建一个 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Isolate</code></a> ,否则你的 Dart 代码永远运行在主UI 线程,并由 event loop 驱动。Flutter 的 event loop 和 iOS 中的 main loop 相似:<code class="highlighter-rouge">Looper</code> 是附加在主线程上的。</p> <p>Dart 的单线程模型,并不意味着你写的代码一定要作为阻塞操作的方式运行,从而卡住 UI。相反,可以使用 Dart 语言提供的异步工具,例如 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">async / await</code></a> ,来实现异步操作。</p> <p>举个例子,你可以使用<a href="https://coding.imooc.com/class/321.html"> <code class="highlighter-rouge">async / await</code></a> 来让 Dart 帮你做一些繁重的工作,编写网络请求代码而不会挂起 UI:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">);</span> <span class="o">});</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>上面做法等价于Android中的<code class="highlighter-rouge">runOnUiThread</code>。 以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>一旦 <code class="highlighter-rouge">await</code> 的网络请求完成,通过调用 <code class="highlighter-rouge">setState()</code> 来更新 UI,这会触发 widget 子树的重建,并更新相关数据。</p> <p>下面的例子展示了异步加载数据,并用 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">ListView</code></a> 展示出来:</p> <p><img src="io/flutter_app/img/blog/loadData-async.png" alt="loadData-async" /></p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:convert'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:flutter/material.dart'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:http/http.dart'</span> <span class="k">as</span> <span class="n">http</span><span class="o">;</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="o">)</span> <span class="o">{</span> <span class="n">runApp</span><span class="o">(</span><span class="n">SampleApp</span><span class="o">());</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">SampleApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="s">'Sample App'</span><span class="o">,</span> <span class="nl">theme:</span> <span class="n">ThemeData</span><span class="o">(</span> <span class="nl">primarySwatch:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">blue</span><span class="o">,</span> <span class="o">),</span> <span class="nl">home:</span> <span class="n">SampleAppPage</span><span class="o">(),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">SampleAppPage</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">SampleAppPage</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="nd">@override</span> <span class="n">_SampleAppPageState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_SampleAppPageState</span><span class="o">();</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">_SampleAppPageState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">SampleAppPage</span><span class="o">&gt;</span> <span class="o">{</span> <span class="n">List</span> <span class="n">widgets</span> <span class="o">=</span> <span class="o">[];</span> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">loadData</span><span class="o">();</span> <span class="o">}</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Sample App"</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">ListView</span><span class="o">.</span><span class="na">builder</span><span class="o">(</span> <span class="nl">itemCount:</span> <span class="n">widgets</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="nl">itemBuilder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="kt">int</span> <span class="n">position</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getRow</span><span class="o">(</span><span class="n">position</span><span class="o">);</span> <span class="o">}));</span> <span class="o">}</span> <span class="n">Widget</span> <span class="n">getRow</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Padding</span><span class="o">(</span> <span class="nl">padding:</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="o">(</span><span class="mf">10.0</span><span class="o">),</span> <span class="nl">child:</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Row </span><span class="si">${widgets[i]["title"]}</span><span class="s">"</span><span class="o">)</span> <span class="o">);</span> <span class="o">}</span> <span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">);</span> <span class="o">});</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <h2 id="怎么把工作放到后台线程执行"><a href="https://coding.imooc.com/class/321.html">怎么把工作放到后台线程执行?</a></h2> <p>由于 Flutter 是单线程并且跑着一个 event loop(就像 Node.js),因此你不必担心线程管理或生成后台线程。如果你正在做 I/O 操作,如访问磁盘或网络请求,可以安全地使用 <code class="highlighter-rouge">async / await</code>来完成。如果你需要做让 CPU 执行繁忙的计算密集型任务,你需要使用 <code class="highlighter-rouge">Isolate</code> 来避免阻塞 event loop。</p> <blockquote> <p>在Android中,当你想访问一个网络资源时,你通常会创建一个AsyncTask,当你需要一个耗时的后台任务时,你通常需要IntentService,在Flutter中则不需要这么繁琐。</p> </blockquote> <p>对于 I/O 操作,通过关键字 <code class="highlighter-rouge">async</code>把方法声明为异步方法,然后通过<code class="highlighter-rouge">await</code>关键字等待该异步方法执行完成:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">);</span> <span class="o">});</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>在Android中,当你继承AsyncTask时,通常会覆盖3个方法,OnPreExecute、doInBackground和onPostExecute。 在Flutter中没有这种模式的等价物,因为你只需await函数执行完成,而Dart的事件循环将负责其余的事情。</p> </blockquote> <p>以上就是对诸如网络请求、数据库访问等,I/O 操作的典型做法。</p> <p>然而,有时候你需要处理大量的数据,这会导致你的 UI 挂起。在 Flutter 中,使用 <code class="highlighter-rouge">Isolate</code> 来发挥多核心 CPU 的优势来处理那些长期运行或是计算密集型的任务。</p> <p><a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Isolate</code></a> 是分离的运行线程,并且不和主线程的内存堆共享内存。这意味着你不能访问主线程中的变量,或者使用 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">setState()</code></a> 来更新 UI。正如它们的名字一样,Isolate 不能共享内存。</p> <p>下面的例子展示了一个简单的<a href="https://coding.imooc.com/class/321.html">Isolate</a>是如何把数据返回给主线程来更新 UI 的:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:isolate'</span><span class="o">;</span> <span class="o">...</span> <span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="c1">// 打开ReceivePort以接收传入的消息</span> <span class="n">ReceivePort</span> <span class="n">receivePort</span> <span class="o">=</span> <span class="n">ReceivePort</span><span class="o">();</span> <span class="c1">//创建并生成与当前Isolate共享相同代码的Isolate</span> <span class="n">await</span> <span class="n">Isolate</span><span class="o">.</span><span class="na">spawn</span><span class="o">(</span><span class="n">dataLoader</span><span class="o">,</span> <span class="n">receivePort</span><span class="o">.</span><span class="na">sendPort</span><span class="o">);</span> <span class="c1">// 流的第一个元素</span> <span class="n">SendPort</span> <span class="n">sendPort</span> <span class="o">=</span> <span class="n">await</span> <span class="n">receivePort</span><span class="o">.</span><span class="na">first</span><span class="o">;</span> <span class="c1">// 流的第一个元素被收到后监听会关闭,所以需要新打开一个ReceivePort以接收传入的消息</span> <span class="n">ReceivePort</span> <span class="n">response</span> <span class="o">=</span> <span class="n">ReceivePort</span><span class="o">();</span> <span class="c1">//通过此发送端口向其对应的“ReceivePort”①发送异步[消息],这个“消息”指的是发送的参数②。</span> <span class="n">sendPort</span><span class="o">.</span><span class="na">send</span><span class="o">(</span> <span class="o">[</span><span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">,</span> <span class="n">response</span><span class="o">.</span><span class="na">sendPort</span><span class="o">]);</span> <span class="c1">// 获取端口发送来的数据③</span> <span class="n">List</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">await</span> <span class="n">response</span><span class="o">.</span><span class="na">first</span><span class="o">;</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">msg</span><span class="o">;</span> <span class="o">});</span> <span class="o">}</span> <span class="c1">// isolate的入口函数,该函数会在新的Isolate中调用,Isolate.spawn的message参数会作为调用它时的唯一参数</span> <span class="kd">static</span> <span class="n">dataLoader</span><span class="o">(</span><span class="n">SendPort</span> <span class="n">sendPort</span><span class="o">)</span> <span class="n">async</span> <span class="o">{</span> <span class="c1">// 打开ReceivePort①以接收传入的消息</span> <span class="n">ReceivePort</span> <span class="n">port</span> <span class="o">=</span> <span class="n">ReceivePort</span><span class="o">();</span> <span class="c1">// 通知其他的isolates,本isolate 所监听的端口</span> <span class="n">sendPort</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">port</span><span class="o">.</span><span class="na">sendPort</span><span class="o">);</span> <span class="c1">// 获取其他端口发送的异步消息 msg② -&gt; ["https://jsonplaceholder.typicode.com/posts", response.sendPort]</span> <span class="n">await</span> <span class="k">for</span> <span class="o">(</span><span class="kd">var</span> <span class="n">msg</span> <span class="k">in</span> <span class="n">port</span><span class="o">)</span> <span class="o">{</span> <span class="c1">//等价于List msg= await port.first;</span> <span class="kt">String</span> <span class="n">data</span> <span class="o">=</span> <span class="n">msg</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span> <span class="n">SendPort</span> <span class="n">replyTo</span> <span class="o">=</span> <span class="n">msg</span><span class="o">[</span><span class="mi">1</span><span class="o">];</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="n">data</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="c1">// 其对应的“ReceivePort”发送解析出来的JSON数据③</span> <span class="n">replyTo</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>这里,<code class="highlighter-rouge">dataLoader()</code> 是一个运行于自己独立执行线程上的 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Isolate</code></a>。在 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">Isolate</code></a> 里,你可以执行 CPU 密集型任务(例如解析一个庞大的 json,解析json也是很耗时的哦),或是计算密集型的数学操作,如加密或信号处理等。</p> <p>你可以运行下面的完整例子:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:convert'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'dart:isolate'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:flutter/material.dart'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:http/http.dart'</span> <span class="k">as</span> <span class="n">http</span><span class="o">;</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="o">)</span> <span class="o">{</span> <span class="n">runApp</span><span class="o">(</span><span class="n">SampleApp</span><span class="o">());</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">SampleApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="s">'Sample App'</span><span class="o">,</span> <span class="nl">theme:</span> <span class="n">ThemeData</span><span class="o">(</span> <span class="nl">primarySwatch:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">blue</span><span class="o">,</span> <span class="o">),</span> <span class="nl">home:</span> <span class="n">SampleAppPage</span><span class="o">(),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">SampleAppPage</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">SampleAppPage</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="nd">@override</span> <span class="n">_SampleAppPageState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_SampleAppPageState</span><span class="o">();</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">_SampleAppPageState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">SampleAppPage</span><span class="o">&gt;</span> <span class="o">{</span> <span class="n">List</span> <span class="n">widgets</span> <span class="o">=</span> <span class="o">[];</span> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">loadData</span><span class="o">();</span> <span class="o">}</span> <span class="n">showLoadingDialog</span><span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">widgets</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="kc">true</span><span class="o">;</span> <span class="o">}</span> <span class="k">return</span> <span class="kc">false</span><span class="o">;</span> <span class="o">}</span> <span class="n">getBody</span><span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">showLoadingDialog</span><span class="o">())</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getProgressDialog</span><span class="o">();</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getListView</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="n">getProgressDialog</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Center</span><span class="o">(</span><span class="nl">child:</span> <span class="n">CircularProgressIndicator</span><span class="o">());</span> <span class="o">}</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Sample App"</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">getBody</span><span class="o">());</span> <span class="o">}</span> <span class="n">ListView</span> <span class="n">getListView</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">ListView</span><span class="o">.</span><span class="na">builder</span><span class="o">(</span> <span class="nl">itemCount:</span> <span class="n">widgets</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="nl">itemBuilder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="kt">int</span> <span class="n">position</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getRow</span><span class="o">(</span><span class="n">position</span><span class="o">);</span> <span class="o">});</span> <span class="n">Widget</span> <span class="n">getRow</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Padding</span><span class="o">(</span> <span class="nl">padding:</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="o">(</span><span class="mf">10.0</span><span class="o">),</span> <span class="nl">child:</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Row </span><span class="si">${widgets[i]["title"]}</span><span class="s">"</span><span class="o">));</span> <span class="o">}</span> <span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="c1">// 打开ReceivePort以接收传入的消息</span> <span class="n">ReceivePort</span> <span class="n">receivePort</span> <span class="o">=</span> <span class="n">ReceivePort</span><span class="o">();</span> <span class="c1">//创建并生成与当前Isolate共享相同代码的Isolate</span> <span class="n">await</span> <span class="n">Isolate</span><span class="o">.</span><span class="na">spawn</span><span class="o">(</span><span class="n">dataLoader</span><span class="o">,</span> <span class="n">receivePort</span><span class="o">.</span><span class="na">sendPort</span><span class="o">);</span> <span class="c1">// 流的第一个元素</span> <span class="n">SendPort</span> <span class="n">sendPort</span> <span class="o">=</span> <span class="n">await</span> <span class="n">receivePort</span><span class="o">.</span><span class="na">first</span><span class="o">;</span> <span class="c1">// 流的第一个元素被收到后监听会关闭,所以需要新打开一个ReceivePort以接收传入的消息</span> <span class="n">ReceivePort</span> <span class="n">response</span> <span class="o">=</span> <span class="n">ReceivePort</span><span class="o">();</span> <span class="c1">//通过此发送端口向其对应的“ReceivePort”①发送异步[消息],这个“消息”指的是发送的参数②。</span> <span class="n">sendPort</span><span class="o">.</span><span class="na">send</span><span class="o">(</span> <span class="o">[</span><span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">,</span> <span class="n">response</span><span class="o">.</span><span class="na">sendPort</span><span class="o">]);</span> <span class="c1">// 获取端口发送来的数据③</span> <span class="n">List</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">await</span> <span class="n">response</span><span class="o">.</span><span class="na">first</span><span class="o">;</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">msg</span><span class="o">;</span> <span class="o">});</span> <span class="o">}</span> <span class="c1">// isolate的入口函数,该函数会在新的Isolate中调用,Isolate.spawn的message参数会作为调用它时的唯一参数</span> <span class="kd">static</span> <span class="n">dataLoader</span><span class="o">(</span><span class="n">SendPort</span> <span class="n">sendPort</span><span class="o">)</span> <span class="n">async</span> <span class="o">{</span> <span class="c1">// 打开ReceivePort①以接收传入的消息</span> <span class="n">ReceivePort</span> <span class="n">port</span> <span class="o">=</span> <span class="n">ReceivePort</span><span class="o">();</span> <span class="c1">// 通知其他的isolates,本isolate 所监听的端口</span> <span class="n">sendPort</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">port</span><span class="o">.</span><span class="na">sendPort</span><span class="o">);</span> <span class="c1">// 获取其他端口发送的异步消息 msg② -&gt; ["https://jsonplaceholder.typicode.com/posts", response.sendPort]</span> <span class="n">await</span> <span class="k">for</span> <span class="o">(</span><span class="kd">var</span> <span class="n">msg</span> <span class="k">in</span> <span class="n">port</span><span class="o">)</span> <span class="o">{</span> <span class="c1">//等价于List msg= await port.first;</span> <span class="kt">String</span> <span class="n">data</span> <span class="o">=</span> <span class="n">msg</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span> <span class="n">SendPort</span> <span class="n">replyTo</span> <span class="o">=</span> <span class="n">msg</span><span class="o">[</span><span class="mi">1</span><span class="o">];</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="n">data</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="c1">// 其对应的“ReceivePort”发送解析出来的JSON数据③</span> <span class="n">replyTo</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>关于Flutter的更多异步编程知识,可以学习<a href="https://coding.imooc.com/class/321.html">《Flutter从入门到进阶-实战携程网App》</a>。</p> </blockquote> <h2 id="如何进行网络请求"><a href="https://coding.imooc.com/class/321.html">如何进行网络请求?</a></h2> <p>在 Flutter 中,使用流行的 <a href="https://pub.dartlang.org/packages/http">http package</a> 做网络请求非常简单。它把你可能需要自己做的网络请求操作抽象了出来,让发起请求变得简单。</p> <p>要使用 http 包,在 <code class="highlighter-rouge">pubspec.yaml</code> 中添加如下依赖:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dependencies: ... http: ^0.12.0+1 </code></pre></div></div> <p>发起网络请求,在 <code class="highlighter-rouge">http.get()</code> 这个 <code class="highlighter-rouge">async</code> 方法中使用 <code class="highlighter-rouge">await</code> :</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:convert'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:flutter/material.dart'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:http/http.dart'</span> <span class="k">as</span> <span class="n">http</span><span class="o">;</span> <span class="o">[...]</span> <span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">);</span> <span class="o">});</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <blockquote> <p>以上代码片段的完整部分可以在<a href="https://coding.imooc.com/class/321.html">课程源码</a>中查找。</p> </blockquote> <p>一旦获得结果后,你可以通过调用setState来告诉Flutter更新其状态,setState将使用网络调用的结果更新UI。</p> <blockquote> <p>关于网络请求的更多内容和实战技巧可学习<a href="https://coding.imooc.com/class/321.html">《基于Http实现网络操作》</a>部分的课程。</p> </blockquote> <h2 id="如何为长时间运行的任务添加一个进度指示器"><a href="https://coding.imooc.com/class/321.html">如何为长时间运行的任务添加一个进度指示器?</a></h2> <ul> <li>在 iOS 中,在后台运行耗时任务时我们通常会使用 UIProgressView。</li> <li>在 Android 中,在后台运行耗时任务时我们通常会使用 ProgressBar。</li> </ul> <p>那么,在Flutter也有与之对应的widget叫<a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">ProgressIndicator</code></a>。通过一个布尔 flag 来控制是否展示进度。在任务开始时,告诉 Flutter 更新状态,并在结束后隐藏。</p> <p>在下面的例子中,build 函数被拆分成三个函数。如果 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">showLoadingDialog()</code></a> 是 <code class="highlighter-rouge">true</code> (当 <code class="highlighter-rouge">widgets.length == 0</code> 时),则渲染 <a href="https://coding.imooc.com/class/321.html"><code class="highlighter-rouge">ProgressIndicator</code></a>。否则,当数据从网络请求中返回时,渲染 <code class="highlighter-rouge">ListView</code>:</p> <div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:convert'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:flutter/material.dart'</span><span class="o">;</span> <span class="kn">import</span> <span class="s">'package:http/http.dart'</span> <span class="k">as</span> <span class="n">http</span><span class="o">;</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="o">)</span> <span class="o">{</span> <span class="n">runApp</span><span class="o">(</span><span class="n">SampleApp</span><span class="o">());</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">SampleApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="o">{</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">MaterialApp</span><span class="o">(</span> <span class="nl">title:</span> <span class="s">'Sample App'</span><span class="o">,</span> <span class="nl">theme:</span> <span class="n">ThemeData</span><span class="o">(</span> <span class="nl">primarySwatch:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">blue</span><span class="o">,</span> <span class="o">),</span> <span class="nl">home:</span> <span class="n">SampleAppPage</span><span class="o">(),</span> <span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">SampleAppPage</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="o">{</span> <span class="n">SampleAppPage</span><span class="o">({</span><span class="n">Key</span> <span class="n">key</span><span class="o">})</span> <span class="o">:</span> <span class="k">super</span><span class="o">(</span><span class="nl">key:</span> <span class="n">key</span><span class="o">);</span> <span class="nd">@override</span> <span class="n">_SampleAppPageState</span> <span class="n">createState</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">_SampleAppPageState</span><span class="o">();</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">_SampleAppPageState</span> <span class="kd">extends</span> <span class="n">State</span><span class="o">&lt;</span><span class="n">SampleAppPage</span><span class="o">&gt;</span> <span class="o">{</span> <span class="n">List</span> <span class="n">widgets</span> <span class="o">=</span> <span class="o">[];</span> <span class="nd">@override</span> <span class="kt">void</span> <span class="n">initState</span><span class="o">()</span> <span class="o">{</span> <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="o">();</span> <span class="n">loadData</span><span class="o">();</span> <span class="o">}</span> <span class="n">showLoadingDialog</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">widgets</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">;</span> <span class="o">}</span> <span class="n">getBody</span><span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">showLoadingDialog</span><span class="o">())</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getProgressDialog</span><span class="o">();</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getListView</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="n">getProgressDialog</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Center</span><span class="o">(</span><span class="nl">child:</span> <span class="n">CircularProgressIndicator</span><span class="o">());</span> <span class="o">}</span> <span class="nd">@override</span> <span class="n">Widget</span> <span class="n">build</span><span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Scaffold</span><span class="o">(</span> <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="o">(</span> <span class="nl">title:</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Sample App"</span><span class="o">),</span> <span class="o">),</span> <span class="nl">body:</span> <span class="n">getBody</span><span class="o">());</span> <span class="o">}</span> <span class="n">ListView</span> <span class="n">getListView</span><span class="o">()</span> <span class="o">=&gt;</span> <span class="n">ListView</span><span class="o">.</span><span class="na">builder</span><span class="o">(</span> <span class="nl">itemCount:</span> <span class="n">widgets</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="nl">itemBuilder:</span> <span class="o">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="o">,</span> <span class="kt">int</span> <span class="n">position</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">getRow</span><span class="o">(</span><span class="n">position</span><span class="o">);</span> <span class="o">});</span> <span class="n">Widget</span> <span class="n">getRow</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">Padding</span><span class="o">(</span><span class="nl">padding:</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="o">(</span><span class="mf">10.0</span><span class="o">),</span> <span class="nl">child:</span> <span class="n">Text</span><span class="o">(</span><span class="s">"Row </span><span class="si">${widgets[i]["title"]}</span><span class="s">"</span><span class="o">));</span> <span class="o">}</span> <span class="n">loadData</span><span class="o">()</span> <span class="n">async</span> <span class="o">{</span> <span class="kt">String</span> <span class="n">dataURL</span> <span class="o">=</span> <span class="s">"https://jsonplaceholder.typicode.com/posts"</span><span class="o">;</span> <span class="n">http</span><span class="o">.</span><span class="na">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">await</span> <span class="n">http</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">dataURL</span><span class="o">);</span> <span class="n">setState</span><span class="o">(()</span> <span class="o">{</span> <span class="n">widgets</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">);</span> <span class="o">});</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <h2 id="未完待续"><a href="https://coding.imooc.com/class/321.html">未完待续</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter入门基础知识</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter主题和文字处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter什么是声明式UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter布局与列表</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter手势检测及触摸事件处理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter状态管理</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter线程和异步UI</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter表单输入与富文本</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter认识视图(Views)</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter调用硬件、第三方服务以及平台交互、通知</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter路由与导航</a></li> <li><a href="https://coding.imooc.com/class/321.html">Flutter项目结构、资源、依赖和本地化</a></li> </ul> <h2 id="推荐学习资料"><a href="https://coding.imooc.com/class/321.html">推荐学习资料</a></h2> <ul> <li><a href="https://coding.imooc.com/class/321.html">Flutter从入门到进阶实战携程网App</a></li> </ul> Sat, 16 Mar 2019 00:00:00 +0800 http://localhost:4000/2019/03/16/thread-and-asynchronous-ui/ http://localhost:4000/2019/03/16/thread-and-asynchronous-ui/ Flutter Android iOS Dart